Ejemplo n.º 1
0
        public void ThrottlerWorkTest()
        {
            DependencyContext.Using<ILocalCache>((c, cache) =>
            {
                object hitInfo = null;
                A.CallTo(() => cache.Add(A<string>.Ignored, A<object>.Ignored, A<TimeSpan>.Ignored))
                    .Invokes((s) => hitInfo = s.Arguments[1]);
                A.CallTo(() => cache.Get<object>(A<string>.Ignored)).ReturnsLazily(() => hitInfo);
                A.CallTo(() => cache.Remove(A<string>.Ignored)).Invokes(() => hitInfo = null);
                A.CallTo(() => c.Resolver.Resolve<ILocalCache>()).Returns(cache);

                var throttler = new Throttler("test", TimeSpan.Zero, 2);
                Assert.True(throttler.Check());
                Assert.True(throttler.Check());
                Assert.False(throttler.Check());
                hitInfo = null;
                Assert.True(throttler.Check());
                Assert.True(throttler.Check());
                Assert.False(throttler.Check());
                throttler.Reset();
                Assert.True(throttler.Check());
            });
        }
        private bool ValidateFirstTimeUser(ref string username, string password)
        {
            var throttler = new Throttler("ValidateUser:"******"Error on directory first time authentication", ex, this.GetType());
                return(false);
            }

            try
            {
                string salt        = null;
                var    hash        = UserRepository.GenerateHash(password, ref salt);
                var    displayName = entry.FirstName + " " + entry.LastName;
                var    email       = entry.Email.TrimToNull() ?? (username + "@yourdefaultdomain.com");
                username = entry.Username.TrimToNull() ?? username;

                using (var connection = SqlConnections.NewFor <UserRow>())
                    using (var uow = new UnitOfWork(connection))
                    {
                        var userId = (int)connection.InsertAndGetID(new UserRow
                        {
                            Username            = username,
                            Source              = "ldap",
                            DisplayName         = displayName,
                            Email               = email,
                            PasswordHash        = hash,
                            PasswordSalt        = salt,
                            IsActive            = 1,
                            InsertDate          = DateTime.Now,
                            InsertUserId        = 1,
                            LastDirectoryUpdate = DateTime.Now
                        });

                        uow.Commit();

                        UserRetrieveService.RemoveCachedUser(userId, username);
                    }

                return(true);
            }
            catch (Exception ex)
            {
                Log.Error("Error while importing directory user", ex, this.GetType());
                return(false);
            }
        }
        private bool ValidateExistingUser(ref string username, string password, UserDefinition user)
        {
            username = user.Username;

            if (user.IsActive != 1)
            {
                if (Log.IsInfoEnabled)
                {
                    Log.Error(String.Format("Inactive user login attempt: {0}", username), this.GetType());
                }

                return(false);
            }

            // prevent more than 50 invalid login attempts in 30 minutes
            var throttler = new Throttler("ValidateUser:"******"site" || user.Source == "sign" || directoryService == null)
            {
                if (validatePassword())
                {
                    throttler.Reset();
                    return(true);
                }

                return(false);
            }

            if (user.Source != "ldap")
            {
                throw new ArgumentOutOfRangeException("userSource");
            }

            if (!string.IsNullOrEmpty(user.PasswordHash) &&
                user.LastDirectoryUpdate != null &&
                user.LastDirectoryUpdate.Value.AddHours(1) >= DateTime.Now)
            {
                if (validatePassword())
                {
                    throttler.Reset();
                    return(true);
                }

                return(false);
            }

            DirectoryEntry entry;

            try
            {
                entry = directoryService.Validate(username, password);
                if (entry == null)
                {
                    return(false);
                }

                throttler.Reset();
            }
            catch (Exception ex)
            {
                Log.Error("Error on directory access", ex, this.GetType());

                // couldn't access directory. allow user to login with cached password
                if (!user.PasswordHash.IsTrimmedEmpty())
                {
                    if (validatePassword())
                    {
                        throttler.Reset();
                        return(true);
                    }

                    return(false);
                }

                throw;
            }

            try
            {
                string salt        = user.PasswordSalt.TrimToNull();
                var    hash        = UserRepository.GenerateHash(password, ref salt);
                var    displayName = entry.FirstName + " " + entry.LastName;
                var    email       = entry.Email.TrimToNull() ?? user.Email ?? (username + "@yourdefaultdomain.com");

                using (var connection = SqlConnections.NewFor <UserRow>())
                    using (var uow = new UnitOfWork(connection))
                    {
                        var fld = UserRow.Fields;
                        new SqlUpdate(fld.TableName)
                        .Set(fld.DisplayName, displayName)
                        .Set(fld.PasswordHash, hash)
                        .Set(fld.PasswordSalt, salt)
                        .Set(fld.Email, email)
                        .Set(fld.LastDirectoryUpdate, DateTime.Now)
                        .WhereEqual(fld.UserId, user.UserId)
                        .Execute(connection, ExpectedRows.One);

                        uow.Commit();

                        UserRetrieveService.RemoveCachedUser(user.UserId, username);
                    }

                return(true);
            }
            catch (Exception ex)
            {
                Log.Error("Error while updating directory user", ex, this.GetType());
                return(true);
            }
        }
Ejemplo n.º 4
0
        private void CheckTwoFactorAuthentication(string username, LoginRequest request)
        {
            bool isTwoFactorReq = !string.IsNullOrEmpty(request.TwoFactorGuid) || request.TwoFactorCode != null;

            if (isTwoFactorReq)
            {
                Check.NotNullOrEmpty(request.TwoFactorGuid, "twoFactorGuid");
                Check.NotNull(request.TwoFactorCode, "twoFactorCode");

                var key  = "TwoFactorAuth:" + request.TwoFactorGuid;
                var data = DistributedCache.Get <TwoFactorData>(key);
                if (data == null || data.Username == null || data.Username != username)
                {
                    throw new ValidationError("Can't validate credentials. Please try login again!");
                }

                data.RetryCount++;
                if (data.RetryCount > 3)
                {
                    DistributedCache.Set <TwoFactorData>(key, null);
                    throw new ValidationError("Can't validate credentials. Please try login again!");
                }
                else
                {
                    DistributedCache.Set(key, data);
                }

                if (data.TwoFactorCode != request.TwoFactorCode)
                {
                    throw new ValidationError("Validation code is invalid. Please check and try again.");
                }

                // login success. clear to not let same two factor guid/two factor code two be reused later
                DistributedCache.Set <TwoFactorData>(key, null);

                return;
            }

            var user = Dependency.Resolve <IUserRetrieveService>().ByUsername(username) as UserDefinition;

            if (user != null &&
                ((user.TwoFactorAuth == Administration.TwoFactorAuthType.SMS &&
                  user.MobilePhoneVerified &&
                  UserRepository.IsValidPhone(user.MobilePhoneNumber)) ||
                 (user.TwoFactorAuth == Administration.TwoFactorAuthType.Email)))
            {
                var env = Config.Get <EnvironmentSettings>();

                // you may disable two factor auth when <compilation debug="true" /> in web.config, by uncommenting lines below
                // if (HttpContext.IsDebuggingEnabled)
                //    return;

                var md5  = new MD5CryptoServiceProvider().ComputeHash(Encoding.UTF8.GetBytes(request.Password ?? ""));
                var data = new TwoFactorData
                {
                    RetryCount    = 0,
                    Username      = username,
                    TwoFactorCode = new Random().Next(9000) + 1000
                };

                // this is to prevent users from sending too many SMS in a certain time interval
                var throttler = new Throttler("TwoFactorAuthThrottle:" + username, TimeSpan.FromMinutes(5), 10);
                if (!throttler.Check())
                {
                    throw new ValidationError(
                              "Can't proceed with two factor authentication. You are over your validation limit!");
                }

                var twoFactorGuid = Guid.NewGuid().ToString("N");

                string authenticationMessage;
                if (user.TwoFactorAuth == Administration.TwoFactorAuthType.SMS)
                {
                    var mobile = user.MobilePhoneNumber.Trim();
                    Dependency.Resolve <Administration.ISMSService>().Send(
                        phoneNumber: mobile,
                        text: "Please use code " + data.TwoFactorCode + " for Allocation login.",
                        reason: "Sent by Allocation system for two factor authenication by SMS (" + user.Username +
                        ")");

                    // mask mobile number
                    mobile = mobile.Substring(0, 2) + new string('*', mobile.Length - 4) +
                             mobile.Substring(mobile.Length - 2, 2);
                    authenticationMessage = "Please enter code sent to your mobile phone with number " + mobile +
                                            " in <span class='counter'>{0}</span> seconds." +
                                            ((Dependency.Resolve <Administration.ISMSService>() is Administration
                                              .FakeSMSService)
                                                ? " (You can find a text file under App_Data/SMS directory, as you haven't configured SMS service yet)"
                                                : "");
                }
                else
                {
                    EmailHelper.Send(
                        address: user.Email,
                        subject: "Your two-factor authentication code for Allocation login",
                        body: "Please use code " + data.TwoFactorCode + " for Allocation login.");
                    authenticationMessage = "Please enter code sent to your e-mail adress in {0} seconds." +
                                            " (If you didn't configure an SMTP server, you can find e-mails under App_Data/Mail directory";
                }

                DistributedCache.Set("TwoFactorAuth:" + twoFactorGuid, data, TimeSpan.FromMinutes(2));
                throw new ValidationError("TwoFactorAuthenticationRequired",
                                          authenticationMessage + "|" + twoFactorGuid, "Two factor authentication is required!");
            }
        }