1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-17 00:03:17 -05:00
bitwarden/src/Api/Auth/Models/Request/TwoFactorRequestModels.cs
Henrik 8bac7f0145
[PM-14476] Avoid multiple lookups in dictionaries (#4973)
* Avoid multiple lookups in dictionaries

* Consistency in fallback to empty CollectionIds

* Readability at the cost of lines changed

* Readability

* Changes after running dotnet format
2025-06-02 11:18:28 -05:00

307 lines
9.5 KiB
C#

using System.ComponentModel.DataAnnotations;
using Bit.Api.Auth.Models.Request.Accounts;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Identity.TokenProviders;
using Bit.Core.Auth.Models;
using Bit.Core.Entities;
using Fido2NetLib;
namespace Bit.Api.Auth.Models.Request;
public class UpdateTwoFactorAuthenticatorRequestModel : SecretVerificationRequestModel
{
[Required]
[StringLength(50)]
public string Token { get; set; }
[Required]
[StringLength(50)]
public string Key { get; set; }
public string UserVerificationToken { get; set; }
public User ToUser(User existingUser)
{
var providers = existingUser.GetTwoFactorProviders();
if (providers == null)
{
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
}
else
{
providers.Remove(TwoFactorProviderType.Authenticator);
}
providers.Add(TwoFactorProviderType.Authenticator, new TwoFactorProvider
{
MetaData = new Dictionary<string, object> { ["Key"] = Key },
Enabled = true
});
existingUser.SetTwoFactorProviders(providers);
return existingUser;
}
}
public class UpdateTwoFactorDuoRequestModel : SecretVerificationRequestModel, IValidatableObject
{
/*
String lengths based on Duo's documentation
https://github.com/duosecurity/duo_universal_csharp/blob/main/DuoUniversal/Client.cs
*/
[Required]
[StringLength(20, MinimumLength = 20, ErrorMessage = "Client Id must be exactly 20 characters.")]
public string ClientId { get; set; }
[Required]
[StringLength(40, MinimumLength = 40, ErrorMessage = "Client Secret must be exactly 40 characters.")]
public string ClientSecret { get; set; }
[Required]
public string Host { get; set; }
public User ToUser(User existingUser)
{
var providers = existingUser.GetTwoFactorProviders();
if (providers == null)
{
providers = [];
}
else
{
providers.Remove(TwoFactorProviderType.Duo);
}
providers.Add(TwoFactorProviderType.Duo, new TwoFactorProvider
{
MetaData = new Dictionary<string, object>
{
["ClientSecret"] = ClientSecret,
["ClientId"] = ClientId,
["Host"] = Host
},
Enabled = true
});
existingUser.SetTwoFactorProviders(providers);
return existingUser;
}
public Organization ToOrganization(Organization existingOrg)
{
var providers = existingOrg.GetTwoFactorProviders();
if (providers == null)
{
providers = [];
}
else
{
providers.Remove(TwoFactorProviderType.OrganizationDuo);
}
providers.Add(TwoFactorProviderType.OrganizationDuo, new TwoFactorProvider
{
MetaData = new Dictionary<string, object>
{
["ClientSecret"] = ClientSecret,
["ClientId"] = ClientId,
["Host"] = Host
},
Enabled = true
});
existingOrg.SetTwoFactorProviders(providers);
return existingOrg;
}
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (string.IsNullOrWhiteSpace(ClientId))
{
results.Add(new ValidationResult("ClientId is required.", [nameof(ClientId)]));
}
if (string.IsNullOrWhiteSpace(ClientSecret))
{
results.Add(new ValidationResult("ClientSecret is required.", [nameof(ClientSecret)]));
}
if (string.IsNullOrWhiteSpace(Host) || !DuoUniversalTokenService.ValidDuoHost(Host))
{
results.Add(new ValidationResult("Host is invalid.", [nameof(Host)]));
}
return results;
}
}
public class UpdateTwoFactorYubicoOtpRequestModel : SecretVerificationRequestModel, IValidatableObject
{
public string Key1 { get; set; }
public string Key2 { get; set; }
public string Key3 { get; set; }
public string Key4 { get; set; }
public string Key5 { get; set; }
[Required]
public bool? Nfc { get; set; }
public User ToUser(User existingUser)
{
var providers = existingUser.GetTwoFactorProviders();
if (providers == null)
{
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
}
else
{
providers.Remove(TwoFactorProviderType.YubiKey);
}
providers.Add(TwoFactorProviderType.YubiKey, new TwoFactorProvider
{
MetaData = new Dictionary<string, object>
{
["Key1"] = FormatKey(Key1),
["Key2"] = FormatKey(Key2),
["Key3"] = FormatKey(Key3),
["Key4"] = FormatKey(Key4),
["Key5"] = FormatKey(Key5),
["Nfc"] = Nfc.Value
},
Enabled = true
});
existingUser.SetTwoFactorProviders(providers);
return existingUser;
}
private string FormatKey(string keyValue)
{
if (string.IsNullOrWhiteSpace(keyValue))
{
return null;
}
return keyValue.Substring(0, 12);
}
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(Key1) && string.IsNullOrWhiteSpace(Key2) && string.IsNullOrWhiteSpace(Key3) &&
string.IsNullOrWhiteSpace(Key4) && string.IsNullOrWhiteSpace(Key5))
{
yield return new ValidationResult("A key is required.", new string[] { nameof(Key1) });
}
if (!string.IsNullOrWhiteSpace(Key1) && Key1.Length < 12)
{
yield return new ValidationResult("Key 1 in invalid.", new string[] { nameof(Key1) });
}
if (!string.IsNullOrWhiteSpace(Key2) && Key2.Length < 12)
{
yield return new ValidationResult("Key 2 in invalid.", new string[] { nameof(Key2) });
}
if (!string.IsNullOrWhiteSpace(Key3) && Key3.Length < 12)
{
yield return new ValidationResult("Key 3 in invalid.", new string[] { nameof(Key3) });
}
if (!string.IsNullOrWhiteSpace(Key4) && Key4.Length < 12)
{
yield return new ValidationResult("Key 4 in invalid.", new string[] { nameof(Key4) });
}
if (!string.IsNullOrWhiteSpace(Key5) && Key5.Length < 12)
{
yield return new ValidationResult("Key 5 in invalid.", new string[] { nameof(Key5) });
}
}
}
public class TwoFactorEmailRequestModel : SecretVerificationRequestModel
{
[Required]
[EmailAddress]
[StringLength(256)]
public string Email { get; set; }
public string AuthRequestId { get; set; }
// An auth session token used for obtaining email and as an authN factor for the sending of emailed 2FA OTPs.
public string SsoEmail2FaSessionToken { get; set; }
public User ToUser(User existingUser)
{
var providers = existingUser.GetTwoFactorProviders();
if (providers == null)
{
providers = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
}
else
{
providers.Remove(TwoFactorProviderType.Email);
}
providers.Add(TwoFactorProviderType.Email, new TwoFactorProvider
{
MetaData = new Dictionary<string, object> { ["Email"] = Email.ToLowerInvariant() },
Enabled = true
});
existingUser.SetTwoFactorProviders(providers);
return existingUser;
}
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrEmpty(Secret) && string.IsNullOrEmpty(AuthRequestAccessCode) && string.IsNullOrEmpty((SsoEmail2FaSessionToken)))
{
yield return new ValidationResult("MasterPasswordHash, OTP, AccessCode, or SsoEmail2faSessionToken must be supplied.");
}
}
}
public class TwoFactorWebAuthnRequestModel : TwoFactorWebAuthnDeleteRequestModel
{
[Required]
public AuthenticatorAttestationRawResponse DeviceResponse { get; set; }
public string Name { get; set; }
}
public class TwoFactorWebAuthnDeleteRequestModel : SecretVerificationRequestModel, IValidatableObject
{
[Required]
public int? Id { get; set; }
public override IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
foreach (var validationResult in base.Validate(validationContext))
{
yield return validationResult;
}
if (!Id.HasValue || Id < 0 || Id > 5)
{
yield return new ValidationResult("Invalid Key Id", new string[] { nameof(Id) });
}
}
}
public class UpdateTwoFactorEmailRequestModel : TwoFactorEmailRequestModel
{
[Required]
[StringLength(50)]
public string Token { get; set; }
}
public class TwoFactorProviderRequestModel : SecretVerificationRequestModel
{
[Required]
public TwoFactorProviderType? Type { get; set; }
}
public class TwoFactorRecoveryRequestModel : TwoFactorEmailRequestModel
{
[Required]
[StringLength(32)]
public string RecoveryCode { get; set; }
}
public class TwoFactorAuthenticatorDisableRequestModel : TwoFactorProviderRequestModel
{
[Required]
public string UserVerificationToken { get; set; }
[Required]
public string Key { get; set; }
}