public virtual void Update(UserAccount account)
        {
            if (account == null)
            {
                Tracing.Error("[UserAccountService.Update] called -- failed null account");
                throw new ArgumentNullException("account");
            }

            Tracing.Information("[UserAccountService.Update] called for account: {0}", account.ID);

            account.LastUpdated = account.UtcNow;
            this.userRepository.Update(account);
        }
        public virtual void DeleteAccount(Guid accountID)
        {
            Tracing.Information("[UserAccountService.DeleteAccount] called: {0}", accountID);

            var account = this.GetByID(accountID);

            if (account == null)
            {
                throw new ArgumentException("Invalid AccountID");
            }

            DeleteAccount(account);
        }
예제 #3
0
        protected virtual void UpdateLinkedAccount(LinkedAccount account, IEnumerable <Claim> claims = null)
        {
            Tracing.Information("[UserAccount.UpdateLinkedAccount] called for accountID: {0}", this.ID);

            if (account == null)
            {
                Tracing.Error("[UserAccount.UpdateLinkedAccount] failed -- null account");
                throw new ArgumentNullException("account");
            }

            account.LastLogin = UtcNow;
            account.UpdateClaims(claims);
        }
예제 #4
0
        protected internal virtual bool RequestTwoFactorAuthCode()
        {
            Tracing.Information("[UserAccount.RequestTwoFactorAuthCode] called for accountID: {0}", this.ID);

            if (!this.IsAccountVerified)
            {
                Tracing.Error("[UserAccount.RequestTwoFactorAuthCode] failed -- account not verified");
                return(false);
            }

            if (this.IsAccountClosed)
            {
                Tracing.Error("[UserAccount.RequestTwoFactorAuthCode] failed -- account closed");
                return(false);
            }

            if (!this.IsLoginAllowed)
            {
                Tracing.Error("[UserAccount.RequestTwoFactorAuthCode] failed -- login not allowed");
                return(false);
            }

            if (this.AccountTwoFactorAuthMode != TwoFactorAuthMode.Mobile)
            {
                Tracing.Error("[UserAccount.RequestTwoFactorAuthCode] failed -- AccountTwoFactorAuthMode not mobile");
                return(false);
            }

            if (String.IsNullOrWhiteSpace(MobilePhoneNumber))
            {
                Tracing.Error("[UserAccount.RequestTwoFactorAuthCode] failed -- empty MobilePhoneNumber");
                return(false);
            }

            if (this.IsMobileCodeStale ||
                this.VerificationPurpose == VerificationKeyPurpose.ChangeMobile)
            {
                Tracing.Verbose("[UserAccount.RequestTwoFactorAuthCode] new mobile code issued");
                this.IssueMobileCode();
            }

            Tracing.Verbose("[UserAccount.RequestTwoFactorAuthCode] success");

            this.CurrentTwoFactorAuthStatus = TwoFactorAuthMode.Mobile;

            this.AddEvent(new TwoFactorAuthenticationCodeNotificationEvent {
                Account = this
            });

            return(true);
        }
        public virtual void SendTwoFactorAuthenticationCode(Guid accountID)
        {
            Tracing.Information(String.Format("[UserAccountService.SendTwoFactorAuthenticationCode] called: {0}", accountID));

            var account = this.GetByID(accountID);

            if (account == null)
            {
                throw new ArgumentException("Invalid AccountID");
            }

            account.RequestTwoFactorAuthCode();
            Update(account);
        }
예제 #6
0
        public virtual void RemoveLinkedAccount(string provider)
        {
            Tracing.Information("[UserAccount.RemoveLinkedAccount] called for accountID: {0}", this.ID);

            var linked = this.LinkedAccounts.Where(x => x.ProviderName == provider);

            foreach (var item in linked)
            {
                this.LinkedAccounts.Remove(item);
                this.AddEvent(new LinkedAccountRemovedEvent {
                    Account = this, LinkedAccount = item
                });
            }
        }
예제 #7
0
        public virtual void RemoveLinkedAccount(string provider, string id)
        {
            Tracing.Information("[UserAccount.RemoveLinkedAccount] called for accountID: {0}", this.ID);

            var linked = GetLinkedAccount(provider, id);

            if (linked != null)
            {
                this.LinkedAccounts.Remove(linked);
                this.AddEvent(new LinkedAccountRemovedEvent {
                    Account = this, LinkedAccount = linked
                });
            }
        }
        public virtual void ConfigureTwoFactorAuthentication(Guid accountID, TwoFactorAuthMode mode)
        {
            Tracing.Information("[UserAccountService.ConfigureTwoFactorAuthentication] called: {0}", accountID);

            var account = this.GetByID(accountID);

            if (account == null)
            {
                throw new ArgumentException("Invalid AccountID");
            }

            account.ConfigureTwoFactorAuthentication(mode);
            Update(account);
        }
        public virtual void RemoveMobilePhone(Guid accountID)
        {
            Tracing.Information("[UserAccountService.RemoveMobilePhone] called: {0}", accountID);

            var account = this.GetByID(accountID);

            if (account == null)
            {
                throw new ArgumentException("Invalid AccountID");
            }

            account.ClearMobilePhoneNumber();
            Update(account);
        }
예제 #10
0
        internal protected virtual void Init(string tenant, string username, string password, string email)
        {
            Tracing.Information("[UserAccount.Init] called");

            if (String.IsNullOrWhiteSpace(tenant))
            {
                Tracing.Error("[UserAccount.Init] failed -- no tenant");
                throw new ArgumentNullException("tenant");
            }
            if (String.IsNullOrWhiteSpace(username))
            {
                Tracing.Error("[UserAccount.Init] failed -- no username");
                throw new ValidationException("Username is required.");
            }
            if (String.IsNullOrWhiteSpace(password))
            {
                Tracing.Error("[UserAccount.Init] failed -- no password");
                throw new ValidationException("Password is required.");
            }
            if (String.IsNullOrWhiteSpace(email))
            {
                Tracing.Error("[UserAccount.Init] failed -- no email");
                throw new ValidationException("Email is required.");
            }

            if (this.ID != Guid.Empty)
            {
                Tracing.Error("[UserAccount.Init] failed -- ID already assigned");
                throw new Exception("Can't call Init if UserAccount is already assigned an ID");
            }

            this.ID                         = Guid.NewGuid();
            this.Tenant                     = tenant;
            this.Username                   = username;
            this.Email                      = email;
            this.Created                    = this.UtcNow;
            this.LastUpdated                = this.Created;
            this.HashedPassword             = HashPassword(password);
            this.PasswordChanged            = this.Created;
            this.IsAccountVerified          = false;
            this.IsLoginAllowed             = false;
            this.AccountTwoFactorAuthMode   = TwoFactorAuthMode.None;
            this.CurrentTwoFactorAuthStatus = TwoFactorAuthMode.None;
            this.SetVerificationKey(VerificationKeyPurpose.VerifyAccount);

            this.AddEvent(new AccountCreatedEvent {
                Account = this
            });
        }
        public virtual bool ChangeEmailFromKey(Guid accountID, string password, string key, string newEmail)
        {
            Tracing.Information("[UserAccountService.ChangeEmailFromKey] called: {0}, {1}", accountID, newEmail);

            if (String.IsNullOrWhiteSpace(password))
            {
                Tracing.Error("[UserAccountService.ChangeEmailFromKey] failed -- null password");
                throw new ValidationException("Invalid password.");
            }
            if (String.IsNullOrWhiteSpace(key))
            {
                Tracing.Error("[UserAccountService.ChangeEmailFromKey] failed -- null key");
                throw new ValidationException("Invalid key.");
            }
            if (String.IsNullOrWhiteSpace(newEmail))
            {
                Tracing.Error("[UserAccountService.ChangeEmailFromKey] failed -- null newEmail");
                throw new ValidationException("Invalid email.");
            }

            var account = this.GetByID(accountID);

            if (account == null)
            {
                throw new ArgumentException("Invalid AccountID");
            }

            if (!Authenticate(account, password, AuthenticationPurpose.VerifyPassword))
            {
                Tracing.Error("[UserAccountService.ChangeEmailFromKey] failed -- authN failed");
                throw new ValidationException("Invalid password.");
            }

            ValidateEmail(account, newEmail);

            var result = account.ChangeEmailFromKey(key, newEmail);

            if (result && SecuritySettings.EmailIsUsername)
            {
                Tracing.Verbose("[UserAccountService.ChangeEmailFromKey] security setting EmailIsUsername is true and AllowEmailChangeWhenEmailIsUsername is true, so changing username: {0}, to: {1}", account.Username, newEmail);
                account.Username = newEmail;
            }

            Update(account);

            Tracing.Verbose("[UserAccountService.ChangeEmailFromKey] result: {0}", result);

            return(result);
        }
        public virtual void SignOut()
        {
            Tracing.Information(String.Format("[ClaimsBasedAuthenticationService.SignOut] called: {0}", ClaimsPrincipal.Current.Claims.GetValue(ClaimTypes.NameIdentifier)));

            // clear cookie
            var sam = FederatedAuthentication.SessionAuthenticationModule;

            if (sam == null)
            {
                Tracing.Verbose("[ClaimsBasedAuthenticationService.Signout] SessionAuthenticationModule is not configured");
                throw new Exception("SessionAuthenticationModule is not configured and it needs to be.");
            }

            sam.SignOut();
        }
        public virtual bool ChangeEmailFromKey(Guid accountID, string password, string key, string newEmail)
        {
            Tracing.Information(String.Format("[UserAccountService.ChangeEmailFromKey] called: {0}, {1}, {2}", accountID, key, newEmail));

            if (String.IsNullOrWhiteSpace(password))
            {
                throw new ValidationException("Invalid password.");
            }
            if (String.IsNullOrWhiteSpace(key))
            {
                throw new ValidationException("Invalid key.");
            }
            if (String.IsNullOrWhiteSpace(newEmail))
            {
                throw new ValidationException("Invalid email.");
            }

            var account = this.GetByID(accountID);

            if (account == null)
            {
                throw new ArgumentException("Invalid AccountID");
            }

            Tracing.Verbose(String.Format("[UserAccountService.ChangeEmailFromKey] account located: {0}, {1}", account.Tenant, account.Username));

            if (!Authenticate(account, password, AuthenticationPurpose.VerifyPassword))
            {
                throw new ValidationException("Invalid password.");
            }

            ValidateEmail(account, newEmail);

            var result = account.ChangeEmailFromKey(key, newEmail);

            if (result && SecuritySettings.EmailIsUsername)
            {
                Tracing.Warning(String.Format("[UserAccountService.ChangeEmailFromKey] security setting EmailIsUsername is true and AllowEmailChangeWhenEmailIsUsername is true, so changing username: {0}, to: {1}", account.Username, newEmail));
                account.Username = newEmail;
            }

            Update(account);

            Tracing.Verbose(String.Format("[UserAccountService.ChangeEmailFromKey] change email outcome: {0}, {1}, {2}", account.Tenant, account.Username, result ? "Successful" : "Failed"));

            return(result);
        }
        public virtual void SetPassword(string tenant, string username, string newPassword)
        {
            Tracing.Information(String.Format("[UserAccountService.SetPassword] called: {0}, {1}", tenant, username));

            if (!SecuritySettings.Instance.MultiTenant)
            {
                tenant = SecuritySettings.Instance.DefaultTenant;
            }

            if (String.IsNullOrWhiteSpace(tenant))
            {
                throw new ValidationException("Invalid tenant.");
            }
            if (String.IsNullOrWhiteSpace(username))
            {
                throw new ValidationException("Invalid username.");
            }
            if (String.IsNullOrWhiteSpace(newPassword))
            {
                throw new ValidationException("Invalid newPassword.");
            }

            ValidatePassword(tenant, username, newPassword);

            var account = this.GetByUsername(tenant, username);

            if (account == null)
            {
                throw new ValidationException("Invalid tenant and/or username.");
            }

            Tracing.Information(String.Format("[UserAccountService.SetPassword] setting new password for: {0}, {1}", tenant, username));

            using (var tx = new TransactionScope())
            {
                account.SetPassword(newPassword);
                this.userRepository.SaveChanges();

                if (this.notificationService != null)
                {
                    this.notificationService.SendPasswordChangeNotice(account);
                }

                tx.Complete();
            }
        }
예제 #15
0
        public virtual void RemoveCertificate(X509Certificate2 certificate)
        {
            Tracing.Information("[UserAccount.RemoveCertificate] called for accountID: {0}", this.ID);

            if (certificate == null)
            {
                Tracing.Error("[UserAccount.RemoveCertificate] failed -- null certificate");
                throw new ArgumentNullException("certificate");
            }
            if (certificate.Handle == IntPtr.Zero)
            {
                Tracing.Error("[UserAccount.RemoveCertificate] failed -- invalid certificate handle");
                throw new ArgumentException("Invalid certificate");
            }

            RemoveCertificate(certificate.Thumbprint);
        }
예제 #16
0
        protected internal virtual bool VerifyAccount(string key)
        {
            Tracing.Information("[UserAccount.VerifyAccount] called for accountID: {0}", this.ID);

            if (String.IsNullOrWhiteSpace(key))
            {
                Tracing.Error("[UserAccount.VerifyAccount] failed -- no key");
                return(false);
            }

            if (IsAccountVerified)
            {
                Tracing.Error("[UserAccount.VerifyAccount] failed -- account already verified");
                return(false);
            }

            if (this.VerificationPurpose != VerificationKeyPurpose.VerifyAccount)
            {
                Tracing.Error("[UserAccount.VerifyAccount] failed -- verification purpose invalid");
                return(false);
            }

            if (IsVerificationKeyStale)
            {
                Tracing.Error("[UserAccount.VerifyAccount] failed -- verification key stale");
                return(false);
            }

            if (this.VerificationKey != key)
            {
                Tracing.Error("[UserAccount.VerifyAccount] failed -- verification key doesn't match");
                return(false);
            }

            Tracing.Verbose("[UserAccount.VerifyAccount] succeeded");

            this.IsAccountVerified = true;
            this.ClearVerificationKey();

            this.AddEvent(new AccountVerifiedEvent {
                Account = this
            });

            return(true);
        }
        public virtual bool AuthenticateWithCode(Guid accountID, string code, out UserAccount account)
        {
            Tracing.Information("[UserAccountService.AuthenticateWithCode] called {0}", accountID);

            account = this.GetByID(accountID);
            if (account == null)
            {
                throw new ArgumentException("Invalid AccountID");
            }

            var result = account.VerifyTwoFactorAuthCode(code);

            Update(account);

            Tracing.Verbose("[UserAccountService.AuthenticateWithCode] result {0}", result);

            return(result);
        }
예제 #18
0
        protected internal virtual void ChangeUsername(string newUsername)
        {
            Tracing.Information("[UserAccount.ChangeUsername] called for accountID: {0}", this.ID);

            if (String.IsNullOrWhiteSpace(newUsername))
            {
                Tracing.Error("[UserAccount.ChangeUsername] failed -- invalid newUsername");
                throw new ArgumentNullException(newUsername);
            }

            Tracing.Verbose("[UserAccount.ChangeUsername] success");

            this.Username = newUsername;

            this.AddEvent(new UsernameChangedEvent {
                Account = this
            });
        }
        public virtual bool VerifyAccount(string key)
        {
            Tracing.Information("[UserAccountService.VerifyAccount] called: {0}", key);

            var account = this.GetByVerificationKey(key);

            if (account == null)
            {
                return(false);
            }

            var result = account.VerifyAccount(key);

            Update(account);

            Tracing.Verbose("[UserAccountService.VerifyAccount] result: {0}", result);

            return(result);
        }
예제 #20
0
        protected internal virtual bool ChangePasswordFromResetKey(string key, string newPassword)
        {
            Tracing.Information("[UserAccount.ChangePasswordFromResetKey] called for accountID: {0}", this.ID);

            if (String.IsNullOrWhiteSpace(key))
            {
                Tracing.Error("[UserAccount.ChangePasswordFromResetKey] failed -- no key");
                return(false);
            }

            if (!this.IsAccountVerified)
            {
                Tracing.Error("[UserAccount.ChangePasswordFromResetKey] failed -- account not verified");
                return(false);
            }

            // if the key is too old don't honor it
            if (IsVerificationKeyStale)
            {
                Tracing.Error("[UserAccount.ChangePasswordFromResetKey] failed -- verification key too old");
                return(false);
            }

            if (this.VerificationPurpose != VerificationKeyPurpose.ChangePassword)
            {
                Tracing.Error("[UserAccount.ChangePasswordFromResetKey] failed -- invalid verification key purpose");
                return(false);
            }

            // check if key matches
            if (this.VerificationKey != key)
            {
                Tracing.Error("[UserAccount.ChangePasswordFromResetKey] failed -- verification keys don't match");
                return(false);
            }

            Tracing.Verbose("[UserAccount.ChangePasswordFromResetKey] success");

            this.ClearVerificationKey();
            this.SetPassword(newPassword);

            return(true);
        }
        public virtual bool VerifyAccount(string key)
        {
            Tracing.Information(String.Format("[UserAccountService.VerifyAccount] called: {0}", key));

            var account = this.GetByVerificationKey(key);

            if (account == null)
            {
                return(false);
            }

            Tracing.Verbose(String.Format("[UserAccountService.VerifyAccount] account located: {0}, {1}", account.Tenant, account.Username));

            var result = account.VerifyAccount(key);

            Update(account);

            return(result);
        }
예제 #22
0
        public void Send(Message msg)
        {
            Tracing.Information("[SmtpMessageDelivery.Send] sending mail to " + msg.To);
            if (!String.IsNullOrWhiteSpace(msg.Cc))
            {
                Tracing.Information("[SmtpMessageDelivery.Send] cc'ing mail to " + msg.Cc);
            }

            if (String.IsNullOrWhiteSpace(msg.From))
            {
                SmtpSection smtp = ConfigurationManager.GetSection("system.net/mailSettings/smtp") as SmtpSection;
                msg.From = smtp.From;
            }

            using (SmtpClient smtp = new SmtpClient())
            {
                smtp.Timeout = SmtpTimeout;
                try
                {
                    MailMessage mailMessage = new MailMessage(msg.From, msg.To, msg.Subject, msg.Body)
                    {
                        IsBodyHtml = SendAsHtml
                    };
                    if (!String.IsNullOrWhiteSpace(msg.Cc))
                    {
                        foreach (string email in msg.Cc.Split(',', ';'))
                        {
                            mailMessage.CC.Add(email);
                        }
                    }
                    smtp.Send(mailMessage);
                }
                catch (SmtpException e)
                {
                    Tracing.Error("[SmtpMessageDelivery.Send] SmtpException: " + e.Message);
                }
                catch (Exception e)
                {
                    Tracing.Error("[SmtpMessageDelivery.Send] Exception: " + e.Message);
                }
            }
        }
예제 #23
0
        protected internal virtual void ConfigureTwoFactorAuthentication(TwoFactorAuthMode mode)
        {
            Tracing.Information("[UserAccount.ConfigureTwoFactorAuthentication] called for accountID: {0}, mode: {1}", this.ID, mode);

            if (this.AccountTwoFactorAuthMode == mode)
            {
                Tracing.Warning("[UserAccount.ConfigureTwoFactorAuthentication] nothing to do -- mode is same as current value");
                return;
            }

            if (mode == TwoFactorAuthMode.Mobile &&
                String.IsNullOrWhiteSpace(this.MobilePhoneNumber))
            {
                Tracing.Error("[UserAccount.ConfigureTwoFactorAuthentication] failed -- mobile requested but no mobile phone for account");
                throw new ValidationException("Register a mobile phone number to enable mobile two factor authentication.");
            }

            if (mode == TwoFactorAuthMode.Certificate &&
                !this.Certificates.Any())
            {
                Tracing.Error("[UserAccount.ConfigureTwoFactorAuthentication] failed -- certificate requested but no certificates for account");
                throw new ValidationException("Add a client certificate to enable certificate two factor authentication.");
            }

            this.ClearMobileAuthCode();
            this.AccountTwoFactorAuthMode = mode;

            if (mode == TwoFactorAuthMode.None)
            {
                Tracing.Verbose("[UserAccount.ConfigureTwoFactorAuthentication] success -- two factor auth disabled");
                this.AddEvent(new TwoFactorAuthenticationDisabledEvent {
                    Account = this
                });
            }
            else
            {
                Tracing.Verbose("[UserAccount.ConfigureTwoFactorAuthentication] success -- two factor auth enabled, mode: {0}", mode);
                this.AddEvent(new TwoFactorAuthenticationEnabledEvent {
                    Account = this, Mode = mode
                });
            }
        }
        public virtual bool AuthenticateWithCertificate(X509Certificate2 certificate, out UserAccount account)
        {
            Tracing.Information("[UserAccountService.AuthenticateWithCertificate] called");

            certificate.Validate();

            account = this.GetByCertificate(certificate.Thumbprint);
            if (account == null)
            {
                return(false);
            }

            var result = account.Authenticate(certificate);

            Update(account);

            Tracing.Verbose("[UserAccountService.AuthenticateWithCertificate] result {0}", result);

            return(result);
        }
예제 #25
0
        protected internal virtual void SetPassword(string password)
        {
            Tracing.Information("[UserAccount.SetPassword] called for accountID: {0}", this.ID);

            if (String.IsNullOrWhiteSpace(password))
            {
                Tracing.Error("[UserAccount.SetPassword] failed -- no password provided");
                throw new ValidationException("Invalid password.");
            }

            Tracing.Verbose("[UserAccount.SetPassword] setting new password hash");

            HashedPassword        = HashPassword(password);
            PasswordChanged       = UtcNow;
            RequiresPasswordReset = false;

            this.AddEvent(new PasswordChangedEvent {
                Account = this, NewPassword = password
            });
        }
        public virtual void ChangeMobilePhoneRequest(Guid accountID, string newMobilePhoneNumber)
        {
            Tracing.Information("[UserAccountService.ChangeMobilePhoneRequest] called: {0}, {1}", accountID, newMobilePhoneNumber);

            if (String.IsNullOrWhiteSpace(newMobilePhoneNumber))
            {
                Tracing.Error("[UserAccountService.ChangeMobilePhoneRequest] failed -- null newMobilePhoneNumber");
                throw new ValidationException("Invalid Phone Number.");
            }

            var account = this.GetByID(accountID);

            if (account == null)
            {
                throw new ArgumentException("Invalid AccountID");
            }

            account.RequestChangeMobilePhoneNumber(newMobilePhoneNumber);
            Update(account);
        }
        public virtual bool AuthenticateWithCertificate(Guid accountID, X509Certificate2 certificate, out UserAccount account)
        {
            Tracing.Information("[UserAccountService.AuthenticateWithCertificate] called for userID: {0}", accountID);

            certificate.Validate();

            account = this.GetByID(accountID);
            if (account == null)
            {
                throw new ArgumentException("Invalid AccountID");
            }

            var result = account.Authenticate(certificate);

            Update(account);

            Tracing.Verbose("[UserAccountService.AuthenticateWithCertificate] result: {0}", result);

            return(result);
        }
예제 #28
0
        protected internal virtual bool ConfirmMobilePhoneNumberFromCode(string code)
        {
            Tracing.Information("[UserAccount.ConfirmMobilePhoneNumberFromCode] called for accountID: {0}", this.ID);

            if (String.IsNullOrWhiteSpace(code))
            {
                Tracing.Error("[UserAccount.ConfirmMobilePhoneNumberFromCode] failed -- no code");
                return(false);
            }

            if (this.VerificationPurpose != VerificationKeyPurpose.ChangeMobile)
            {
                Tracing.Error("[UserAccount.ConfirmMobilePhoneNumberFromCode] failed -- invalid verification key purpose");
                return(false);
            }

            if (this.IsMobileCodeStale)
            {
                Tracing.Error("[UserAccount.ConfirmMobilePhoneNumberFromCode] failed -- stale mobile code");
                return(false);
            }

            if (code != this.MobileCode)
            {
                Tracing.Error("[UserAccount.ConfirmMobilePhoneNumberFromCode] failed -- codes don't match");
                return(false);
            }

            Tracing.Verbose("[UserAccount.ConfirmMobilePhoneNumberFromCode] success");

            this.MobilePhoneNumber = this.VerificationKey;

            this.ClearVerificationKey();
            this.ClearMobileAuthCode();

            this.AddEvent(new MobilePhoneChangedEvent {
                Account = this
            });

            return(true);
        }
        public virtual bool CancelNewAccount(string key)
        {
            Tracing.Information("[UserAccountService.CancelNewAccount] called: {0}", key);

            var account = this.GetByVerificationKey(key);

            if (account == null)
            {
                return(false);
            }

            if (account.CancelNewAccount(key))
            {
                Tracing.Verbose("[UserAccountService.CancelNewAccount] account cancelled");
                DeleteAccount(account);
                return(true);
            }

            Tracing.Verbose("[UserAccountService.CancelNewAccount] account not cancelled");
            return(false);
        }
예제 #30
0
        protected internal virtual bool RequestTwoFactorAuthCertificate()
        {
            Tracing.Information("[UserAccount.RequestTwoFactorAuthCertificate] called for accountID: {0}", this.ID);

            if (!this.IsAccountVerified)
            {
                Tracing.Error("[UserAccount.RequestTwoFactorAuthCertificate] failed -- account not verified");
                return(false);
            }

            if (this.IsAccountClosed)
            {
                Tracing.Error("[UserAccount.RequestTwoFactorAuthCertificate] failed -- account closed");
                return(false);
            }

            if (!this.IsLoginAllowed)
            {
                Tracing.Error("[UserAccount.RequestTwoFactorAuthCertificate] failed -- login not allowed");
                return(false);
            }

            if (this.AccountTwoFactorAuthMode != TwoFactorAuthMode.Certificate)
            {
                Tracing.Error("[UserAccount.RequestTwoFactorAuthCertificate] failed -- current auth mode is not certificate");
                return(false);
            }

            if (!this.Certificates.Any())
            {
                Tracing.Error("[UserAccount.RequestTwoFactorAuthCertificate] failed -- no certificates");
                return(false);
            }

            Tracing.Verbose("[UserAccount.RequestTwoFactorAuthCertificate] success");

            this.CurrentTwoFactorAuthStatus = TwoFactorAuthMode.Certificate;

            return(true);
        }