예제 #1
0
        public async Task <ActionResult <User> > Create(User user)
        {
            if (await _userService.GetWithEmail(user.Email) != null)
            {
                return(Unauthorized("Email already exists."));
            }
            var salt = HashProvider.GetSalt();

            user.IV        = salt;
            user.Password  = HashProvider.GetHash(user.Password, salt);
            user.CreatedAt = DateTime.Now;

            var claims = new[]
            {
                new Claim(ClaimTypes.Email, user.Email)
            };

            user.RefreshToken = TokenProvider.GetToken
                                (
                user,
                _configuration["SecurityKey"],
                DateTime.Now.AddYears(100),
                claims
                                );

            await _userService.Create(user);

            return(CreatedAtRoute("GetUser", new { id = user.Id.ToString() }, user));
        }
예제 #2
0
        /// <summary>
        /// Registers a new user.  The PasswordHash property should be the actual password.
        /// </summary>
        /// <param name="user">A user with a raw password which is turned into a password hash as part of registration.</param>
        /// <param name="duration">The amount of time that the initial session will be valid.</param>
        /// <param name="ipAddress">The internet address where the user is connecting from.</param>
        /// <param name="result">A ExecutionResults instance to add applicable
        /// warning and error messages to.</param>
        /// <returns>A boolean indicating success (true) or failure (false).</returns>
        public override UserIdentity RegisterUser(User user, UserSessionDurationType duration, String ipAddress, ExecutionResults result)
        {
            string password = user.PasswordHash;

            if (!ValidateName(user.Name, result) || !ValidatePassword(password, result))
            {
                return(new cs.UserIdentity());
            }

            var existing = GetUserByName(user.Name);

            if (existing != null)
            {   //seed user table with deleted users with names you don't want users to have
                result.AppendError("The name you specified cannot be used.");
                return(new cs.UserIdentity());
            }
            if (user.UserID.Equals(Guid.Empty))
            {
                user.UserID = Guid.NewGuid();
            }

            HashProvider hasher = HashManager.SelectProvider();
            var          salt   = new UserSalt
            {
                PasswordSalt = hasher.GetSalt(),
                UserID       = user.UserID,
                HashGroup    = new Random(DateTime.Now.Second).Next(HashGroupMinimum, HashGroupMaximum),
                HashName     = hasher.Name
            };

            user.PasswordHash = hasher.Hash(salt.PasswordSalt, password,
                                            salt.HashGroup + BaseHashIterations);
            using (var scope = new System.Transactions.TransactionScope())
            {
                //starts as a lightweight transaction
                SaveUser(user);
                //enlists in a full distributed transaction if users and salts have different connection strings
                SaveUserSalt(salt);
            }
            return(AuthenticateUser(name: user.Name, password: password, duration: duration,
                                    ipAddress: ipAddress, checkHistory: false, allowUpdateHash: false, result: result));
        }
예제 #3
0
        /// <summary>
        /// Generates a new password reset code for a user and stores that as the current code valid
        /// for the next hour.
        /// </summary>
        /// <param name="name">The user name / email address.</param>
        /// <returns>If the user exists, then a reset code string; otherwise null.</returns>
        public override String GenerateUserResetCode(String name)
        {
            var user = GetUserByName(name);

            if (user == null)
            {
                return(null);
            }

            var salt = GetUserSalt(user.UserID);

            if (!String.IsNullOrWhiteSpace(salt.ResetCode) && salt.ResetCodeExpiration > DateTime.UtcNow.AddMinutes(5))
            {
                return(salt.ResetCode); //if submits form to request a code multiple times during window, then use the same code unless about to expire.
            }
            HashProvider hasher = !string.IsNullOrEmpty(salt.HashName) ? HashManager.Providers[salt.HashName] : HashManager.DefaultProvider;

            salt.ResetCode           = hasher.GetSalt(16);
            salt.ResetCodeExpiration = DateTime.UtcNow.AddHours(1);
            SaveUserSalt(salt);

            return(salt.ResetCode);
        }
예제 #4
0
        /// <summary>
        /// updates a user's name and/or password.
        /// </summary>
        /// <param name="item">The user details to be saved.  If Password is empty is it not changed.  If specified it should be the new raw password (not a hash).</param>
        /// <param name="currentPassword">The current raw password for the user used to authenticate that the change can be made, or the current resetcode last sent to this user.</param>
        /// <param name="ipAddress">The internet address where the user is connecting from.</param>
        /// <param name="result">A ExecutionResults instance to add applicable
        /// warning and error messages to.</param>
        /// <returns>A boolean indicating success (true) or failure (false).</returns>
        public override bool UpdateUser(User item, String currentPassword, String ipAddress, ExecutionResults result)
        {
            if (item.UserID.Equals(Guid.Empty))
            {
                throw new ArgumentException("The user identity must be specified.");
            }
            var user = GetUserByID(item.UserID);
            var salt = GetUserSalt(item.UserID);

            if (user == null || salt == null)
            {
                result.AppendError("The specified user identity does not exist.");
                return(false);
            }
            if (salt.ResetCode == currentPassword)
            {
                if (salt.ResetCodeExpiration < DateTime.UtcNow)
                {
                    result.AppendError(
                        "Your password reset code has expired.  Request a new one to be sent to you, and then use it immediately.");
                    return(false);
                }
                salt.ResetCode           = null;
                salt.ResetCodeExpiration = DateTime.UtcNow;
            }
            else
            {
                var rememberMe = !cs.SecurityContextManager.IsAnonymous &&
                                 cs.SecurityContextManager.CurrentUser.Identity.Ticket.UserSession.ExpirationDate >
                                 DateTime.UtcNow.AddMinutes(PublicSessionDuration);
                if (!AuthenticateUser(name: item.Name, password: currentPassword, ipAddress: ipAddress,
                                      duration: rememberMe ? UserSessionDurationType.Extended : UserSessionDurationType.PublicComputer,
                                      allowUpdateHash: false, checkHistory: false, result: result).IsAuthenticated)
                {
                    result.AppendError("Cannot change password due to authentication error with current password.");
                    return(false);
                }
            }
            if (user.Name != item.Name)
            {   //user is changing their sign in name.  Make sure the new name is available.
                var nameExisting = GetUserByName(item.Name);
                if (nameExisting != null)
                {
                    result.AppendError("The name you specified cannot be used.");
                    return(false);
                }
                user.Name = item.Name;
            }
            if (!String.IsNullOrEmpty(item.PasswordHash))
            {
                var password = item.PasswordHash;
                //update hashes on regular basis, keeps the iterations in latest range for current users, and with a 'current' hash provider.
                HashProvider hasher = HashManager.SelectProvider();
                salt.PasswordSalt        = hasher.GetSalt();
                salt.HashGroup           = new Random(DateTime.Now.Second).Next(HashGroupMinimum, HashGroupMaximum);
                salt.HashName            = hasher.Name;
                user.PasswordHash        = hasher.Hash(salt.PasswordSalt, password, salt.HashGroup + BaseHashIterations);
                user.PasswordUpdatedDate = DateTime.UtcNow;
            }
            using (var scope = new System.Transactions.TransactionScope())
            {
                //starts as a lightweight transaction
                SaveUser(user);
                //enlists in a full distributed transaction if users and salts have different connection strings
                SaveUserSalt(salt);
            }
            return(true);
        }
예제 #5
0
        private cs.UserIdentity AuthenticateUser(string name, string password,
                                                 UserSessionDurationType duration, string ipAddress, bool checkHistory,
                                                 bool allowUpdateHash, ExecutionResults result)
        {
            if (checkHistory)
            {
                var recentFailures = GetRecentFailedUserNameAuthenticationCount(name);
                if (recentFailures > AllowedFailuresPerPeriod)
                {
                    return(FailAuthenticateUser(name, ipAddress, result));
                }
            }
            User user = GetUserByName(name);

            if (user == null)
            {
                return(FailAuthenticateUser(name, ipAddress, result));
            }
            UserSalt salt = GetUserSalt(user.UserID);

            if (salt == null)
            {
                return(FailAuthenticateUser(name, ipAddress, result));
            }

            //this should get a named hashProvider used to originally hash the password...
            //  fallback to 'default' provider in legacy case when we didn't store the name.
            HashProvider hasher       = !string.IsNullOrEmpty(salt.HashName) ? HashManager.Providers[salt.HashName] : HashManager.DefaultProvider;
            var          passwordHash = hasher.Hash(salt.PasswordSalt, password, salt.HashGroup + BaseHashIterations);

            if (user.PasswordHash != passwordHash)
            {
                return(FailAuthenticateUser(name, ipAddress, result));
            }
            var session = new UserSession
            {
                CreatedDate    = DateTime.UtcNow,
                ExpirationDate = DateTime.UtcNow.AddMinutes(duration == UserSessionDurationType.PublicComputer ? PublicSessionDuration : ExtendedSessionDuration),
                UserID         = user.UserID,
                RenewalToken   = Guid.NewGuid()
            };
            var history = new AuthenticationHistory
            {
                IPAddress       = ipAddress,
                IsAuthenticated = true,
                UserName        = name,
                UserSession     = session
            };

            using (var scope = new System.Transactions.TransactionScope())
            {
                if (allowUpdateHash && (hasher.IsObsolete || user.PasswordHashUpdatedDate < DateTime.UtcNow.AddMonths(-1)))
                {
                    //update hashes on regular basis, keeps the iterations in latest range for current users, and with a 'current' hash provider.
                    hasher                       = HashManager.SelectProvider();
                    salt.PasswordSalt            = hasher.GetSalt();
                    salt.HashGroup               = new Random(DateTime.Now.Second).Next(HashGroupMinimum, HashGroupMaximum);
                    salt.HashName                = hasher.Name;
                    user.PasswordHash            = hasher.Hash(salt.PasswordSalt, password, salt.HashGroup + BaseHashIterations);
                    user.PasswordHashUpdatedDate = DateTime.UtcNow;
                    //starts as a lightweight transaction
                    SaveUser(user);
                    //enlists in a full distributed transaction if users and salts have different connection strings
                    SaveUserSalt(salt);
                }
                //either continues distributed transaction if applicable,
                //  or creates a new lightweight transaction for these two commands
                SaveUserSession(session);
                InsertUserHistory(history);
            }
            return(new cs.UserIdentity(history, this.Name));
        }