mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-07-14 22:45:47 +00:00
feat: notify users about outdated password hashes used
This commit is contained in:
@ -59,10 +59,10 @@ class Mapping(Auth):
|
||||
"""
|
||||
if password is None:
|
||||
return False # invalid data supplied
|
||||
user = self.get_user(username)
|
||||
user = await self.get_user(username)
|
||||
return user is not None and user.check_credentials(password, self.salt)
|
||||
|
||||
def get_user(self, username: str) -> User | None:
|
||||
async def get_user(self, username: str) -> User | None:
|
||||
"""
|
||||
retrieve user from in-memory mapping
|
||||
|
||||
@ -84,7 +84,7 @@ class Mapping(Auth):
|
||||
Returns:
|
||||
bool: ``True`` in case if user is known and can be authorized and ``False`` otherwise
|
||||
"""
|
||||
return username is not None and self.get_user(username) is not None
|
||||
return username is not None and await self.get_user(username) is not None
|
||||
|
||||
async def verify_access(self, username: str, required: UserAccess, context: str | None) -> bool:
|
||||
"""
|
||||
@ -98,5 +98,5 @@ class Mapping(Auth):
|
||||
Returns:
|
||||
bool: ``True`` in case if user is allowed to do this request and ``False`` otherwise
|
||||
"""
|
||||
user = self.get_user(username)
|
||||
user = await self.get_user(username)
|
||||
return user is not None and user.verify_access(required)
|
||||
|
@ -120,7 +120,7 @@ class PAM(Mapping):
|
||||
bool: ``True`` in case if user is allowed to do this request and ``False`` otherwise
|
||||
"""
|
||||
# this method is basically inverted, first we check overrides in database and then fallback to the PAM logic
|
||||
if (user := self.get_user(username)) is not None:
|
||||
if (user := await self.get_user(username)) is not None:
|
||||
return user.verify_access(required)
|
||||
# if username is in admin group, then we treat it as full access
|
||||
if username in self.group_members(self.full_access_group):
|
||||
|
@ -32,6 +32,7 @@ class User:
|
||||
authorized web user model
|
||||
|
||||
Attributes:
|
||||
SUPPORTED_ALGOS(set[str]): (class attribute) list of the supported hashing algorithms
|
||||
username(str): username
|
||||
password(str): hashed user password with salt
|
||||
access(UserAccess): user role
|
||||
@ -68,6 +69,8 @@ class User:
|
||||
packager_id: str | None = None
|
||||
key: str | None = None
|
||||
|
||||
SUPPORTED_ALGOS = {"$2$", "$2a$", "$2x$", "$2y$", "$2b$"}
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
"""
|
||||
remove empty fields
|
||||
@ -75,6 +78,19 @@ class User:
|
||||
object.__setattr__(self, "packager_id", self.packager_id or None)
|
||||
object.__setattr__(self, "key", self.key or None)
|
||||
|
||||
@property
|
||||
def algo(self) -> str | None:
|
||||
"""
|
||||
extract algorithm used for the hashing password
|
||||
|
||||
Returns:
|
||||
str | None: first part of password hash (e.g. ``$2$``) if available or ``None`` otherwise
|
||||
"""
|
||||
if not self.password:
|
||||
return None
|
||||
algo = next(segment for segment in self.password.split("$") if segment)
|
||||
return f"${algo}$"
|
||||
|
||||
@staticmethod
|
||||
def generate_password(length: int) -> str:
|
||||
"""
|
||||
@ -98,7 +114,12 @@ class User:
|
||||
|
||||
Returns:
|
||||
bool: ``True`` in case if password matches, ``False`` otherwise
|
||||
|
||||
Raises:
|
||||
ValueError: if user password is set to unsupported algorithm
|
||||
"""
|
||||
if (algo := self.algo) is not None and algo not in self.SUPPORTED_ALGOS:
|
||||
raise ValueError(f"Crypt {algo} is not supported, consider setting new password")
|
||||
try:
|
||||
return bcrypt.checkpw((password + salt).encode("utf8"), self.password.encode("utf8"))
|
||||
except ValueError:
|
||||
|
Reference in New Issue
Block a user