mirror of
				https://github.com/mealie-recipes/mealie.git
				synced 2025-10-31 02:03:35 -04:00 
			
		
		
		
	fix: Fix bugs with account locking (#2580)
* fix(security): reset login attempts after successful login Enforce a maximum number of consecutive failed logins. Successfully logging in should reset the count. #2569 * fix(security): fix when user is unlocked The user should be unlocked when locked_at is set, but the lock has expired. #2569
This commit is contained in:
		| @@ -72,7 +72,9 @@ def authenticate_user(session, email: str, password: str) -> PrivateUser | bool: | |||||||
|             user_service.lock_user(user) |             user_service.lock_user(user) | ||||||
|  |  | ||||||
|         return False |         return False | ||||||
|     return user |  | ||||||
|  |     user.login_attemps = 0 | ||||||
|  |     return db.users.update(user.id, user) | ||||||
|  |  | ||||||
|  |  | ||||||
| def verify_password(plain_password: str, hashed_password: str) -> bool: | def verify_password(plain_password: str, hashed_password: str) -> bool: | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ class UserService(BaseService): | |||||||
|         unlocked = 0 |         unlocked = 0 | ||||||
|  |  | ||||||
|         for user in locked_users: |         for user in locked_users: | ||||||
|             if force or user.is_locked and user.locked_at is not None: |             if force or not user.is_locked and user.locked_at is not None: | ||||||
|                 self.unlock_user(user) |                 self.unlock_user(user) | ||||||
|                 unlocked += 1 |                 unlocked += 1 | ||||||
|  |  | ||||||
|   | |||||||
| @@ -61,3 +61,34 @@ def test_lock_unlocker_user(database: AllRepositories, unique_user: TestUser) -> | |||||||
|     # Sanity check that the is_locked property is working |     # Sanity check that the is_locked property is working | ||||||
|     user.locked_at = datetime.now() - timedelta(days=2) |     user.locked_at = datetime.now() - timedelta(days=2) | ||||||
|     assert not user.is_locked |     assert not user.is_locked | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_reset_locked_users(database: AllRepositories, unique_user: TestUser) -> None: | ||||||
|  |     user_service = UserService(database) | ||||||
|  |  | ||||||
|  |     # Test that the user is unlocked | ||||||
|  |     user = database.users.get_one(unique_user.user_id) | ||||||
|  |     assert not user.is_locked | ||||||
|  |     assert not user.locked_at | ||||||
|  |  | ||||||
|  |     # Test that the user is locked | ||||||
|  |     user.login_attemps = 5 | ||||||
|  |     user = user_service.lock_user(user) | ||||||
|  |     assert user.is_locked | ||||||
|  |     assert user.login_attemps == 5 | ||||||
|  |  | ||||||
|  |     # Test that the locked user is not unlocked by reset | ||||||
|  |     unlocked = user_service.reset_locked_users() | ||||||
|  |     user = database.users.get_one(unique_user.user_id) | ||||||
|  |     assert unlocked == 0 | ||||||
|  |     assert user.is_locked | ||||||
|  |     assert user.login_attemps == 5 | ||||||
|  |  | ||||||
|  |     # Test that the locked user is unlocked by reset | ||||||
|  |     user.locked_at = datetime.now() - timedelta(days=2) | ||||||
|  |     database.users.update(user.id, user) | ||||||
|  |     unlocked = user_service.reset_locked_users() | ||||||
|  |     user = database.users.get_one(unique_user.user_id) | ||||||
|  |     assert unlocked == 1 | ||||||
|  |     assert not user.is_locked | ||||||
|  |     assert user.login_attemps == 0 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user