fix: finalize semantic repair and test updates
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#
|
||||
# @SEMANTICS: auth, config, settings, jwt, adfs
|
||||
# @PURPOSE: Centralized configuration for authentication and authorization.
|
||||
# @COMPLEXITY: 2
|
||||
# @LAYER: Core
|
||||
# @RELATION: DEPENDS_ON -> pydantic
|
||||
#
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
# @SEMANTICS: jwt, token, session, auth
|
||||
# @PURPOSE: JWT token generation and validation logic.
|
||||
# @LAYER: Core
|
||||
# @RELATION: DEPENDS_ON -> jose
|
||||
# @RELATION: USES -> auth_config
|
||||
# @RELATION: DEPENDS_ON -> [auth_config]
|
||||
# @RELATION: USES -> [auth_config]
|
||||
#
|
||||
# @INVARIANT: Tokens must include expiration time and user identifier.
|
||||
|
||||
@@ -17,11 +17,12 @@ from .config import auth_config
|
||||
from ..logger import belief_scope
|
||||
# [/SECTION]
|
||||
|
||||
|
||||
# [DEF:create_access_token:Function]
|
||||
# @PURPOSE: Generates a new JWT access token.
|
||||
# @PRE: data dict contains 'sub' (user_id) and optional 'scopes' (roles).
|
||||
# @POST: Returns a signed JWT string.
|
||||
# @RELATION: DEPENDS_ON -> auth_config
|
||||
# @RELATION: DEPENDS_ON -> [auth_config]
|
||||
#
|
||||
# @PARAM: data (dict) - Payload data for the token.
|
||||
# @PARAM: expires_delta (Optional[timedelta]) - Custom expiration time.
|
||||
@@ -32,26 +33,37 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(minutes=auth_config.ACCESS_TOKEN_EXPIRE_MINUTES)
|
||||
|
||||
expire = datetime.utcnow() + timedelta(
|
||||
minutes=auth_config.ACCESS_TOKEN_EXPIRE_MINUTES
|
||||
)
|
||||
|
||||
to_encode.update({"exp": expire})
|
||||
encoded_jwt = jwt.encode(to_encode, auth_config.SECRET_KEY, algorithm=auth_config.ALGORITHM)
|
||||
encoded_jwt = jwt.encode(
|
||||
to_encode, auth_config.SECRET_KEY, algorithm=auth_config.ALGORITHM
|
||||
)
|
||||
return encoded_jwt
|
||||
|
||||
|
||||
# [/DEF:create_access_token:Function]
|
||||
|
||||
|
||||
# [DEF:decode_token:Function]
|
||||
# @PURPOSE: Decodes and validates a JWT token.
|
||||
# @PRE: token is a signed JWT string.
|
||||
# @POST: Returns the decoded payload if valid.
|
||||
# @RELATION: DEPENDS_ON -> auth_config
|
||||
# @RELATION: DEPENDS_ON -> [auth_config]
|
||||
#
|
||||
# @PARAM: token (str) - The JWT to decode.
|
||||
# @RETURN: dict - The decoded payload.
|
||||
# @THROW: jose.JWTError - If token is invalid or expired.
|
||||
def decode_token(token: str) -> dict:
|
||||
with belief_scope("decode_token"):
|
||||
payload = jwt.decode(token, auth_config.SECRET_KEY, algorithms=[auth_config.ALGORITHM])
|
||||
payload = jwt.decode(
|
||||
token, auth_config.SECRET_KEY, algorithms=[auth_config.ALGORITHM]
|
||||
)
|
||||
return payload
|
||||
|
||||
|
||||
# [/DEF:decode_token:Function]
|
||||
|
||||
# [/DEF:AuthJwtModule:Module]
|
||||
# [/DEF:AuthJwtModule:Module]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#
|
||||
# @SEMANTICS: auth, oauth, oidc, adfs
|
||||
# @PURPOSE: ADFS OIDC configuration and client using Authlib.
|
||||
# @COMPLEXITY: 2
|
||||
# @LAYER: Core
|
||||
# @RELATION: DEPENDS_ON -> authlib
|
||||
# @RELATION: USES -> auth_config
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
# @SEMANTICS: auth, repository, database, user, role, permission
|
||||
# @PURPOSE: Data access layer for authentication and user preference entities.
|
||||
# @LAYER: Domain
|
||||
# @RELATION: DEPENDS_ON ->[sqlalchemy.orm.Session]
|
||||
# @RELATION: DEPENDS_ON ->[User:Class]
|
||||
# @RELATION: DEPENDS_ON ->[Role:Class]
|
||||
# @RELATION: DEPENDS_ON ->[Permission:Class]
|
||||
# @RELATION: DEPENDS_ON ->[UserDashboardPreference:Class]
|
||||
# @RELATION: DEPENDS_ON ->[belief_scope:Function]
|
||||
# @RELATION: DEPENDS_ON -> [Session]
|
||||
# @RELATION: DEPENDS_ON -> [User]
|
||||
# @RELATION: DEPENDS_ON -> [Role]
|
||||
# @RELATION: DEPENDS_ON -> [Permission]
|
||||
# @RELATION: DEPENDS_ON -> [UserDashboardPreference]
|
||||
# @RELATION: DEPENDS_ON -> [belief_scope]
|
||||
# @INVARIANT: All database read/write operations must execute via the injected SQLAlchemy session boundary.
|
||||
# @DATA_CONTRACT: Session -> [User | Role | Permission | UserDashboardPreference]
|
||||
# @PRE: Database connection is active.
|
||||
@@ -24,12 +24,13 @@ from ...models.profile import UserDashboardPreference
|
||||
from ..logger import belief_scope, logger
|
||||
# [/SECTION]
|
||||
|
||||
|
||||
# [DEF:AuthRepository:Class]
|
||||
# @PURPOSE: Provides low-level CRUD operations for identity and authorization records.
|
||||
# @PRE: Database session is bound.
|
||||
# @POST: Entity instances returned safely.
|
||||
# @SIDE_EFFECT: Performs database reads.
|
||||
# @RELATION: DEPENDS_ON -> sqlalchemy.orm.Session
|
||||
# @RELATION: DEPENDS_ON -> [Session]
|
||||
class AuthRepository:
|
||||
# @PURPOSE: Initialize repository with database session.
|
||||
def __init__(self, db: Session):
|
||||
@@ -39,98 +40,125 @@ class AuthRepository:
|
||||
# @PURPOSE: Retrieve user by UUID.
|
||||
# @PRE: user_id is a valid UUID string.
|
||||
# @POST: Returns User object if found, else None.
|
||||
# @RELATION: DEPENDS_ON -> User
|
||||
# @RELATION: DEPENDS_ON -> [User]
|
||||
def get_user_by_id(self, user_id: str) -> Optional[User]:
|
||||
with belief_scope("AuthRepository.get_user_by_id"):
|
||||
logger.reason(f"Fetching user by id: {user_id}")
|
||||
result = self.db.query(User).filter(User.id == user_id).first()
|
||||
logger.reflect(f"User found: {result is not None}")
|
||||
return result
|
||||
|
||||
# [/DEF:get_user_by_id:Function]
|
||||
|
||||
# [DEF:get_user_by_username:Function]
|
||||
# @PURPOSE: Retrieve user by username.
|
||||
# @PRE: username is a non-empty string.
|
||||
# @POST: Returns User object if found, else None.
|
||||
# @RELATION: DEPENDS_ON -> User
|
||||
# @RELATION: DEPENDS_ON -> [User]
|
||||
def get_user_by_username(self, username: str) -> Optional[User]:
|
||||
with belief_scope("AuthRepository.get_user_by_username"):
|
||||
logger.reason(f"Fetching user by username: {username}")
|
||||
result = self.db.query(User).filter(User.username == username).first()
|
||||
logger.reflect(f"User found: {result is not None}")
|
||||
return result
|
||||
|
||||
# [/DEF:get_user_by_username:Function]
|
||||
|
||||
# [DEF:get_role_by_id:Function]
|
||||
# @PURPOSE: Retrieve role by UUID with permissions preloaded.
|
||||
# @RELATION: DEPENDS_ON -> Role
|
||||
# @RELATION: DEPENDS_ON -> Permission
|
||||
# @RELATION: DEPENDS_ON -> [Role]
|
||||
# @RELATION: DEPENDS_ON -> [Permission]
|
||||
def get_role_by_id(self, role_id: str) -> Optional[Role]:
|
||||
with belief_scope("AuthRepository.get_role_by_id"):
|
||||
return self.db.query(Role).options(selectinload(Role.permissions)).filter(Role.id == role_id).first()
|
||||
return (
|
||||
self.db.query(Role)
|
||||
.options(selectinload(Role.permissions))
|
||||
.filter(Role.id == role_id)
|
||||
.first()
|
||||
)
|
||||
|
||||
# [/DEF:get_role_by_id:Function]
|
||||
|
||||
# [DEF:get_role_by_name:Function]
|
||||
# @PURPOSE: Retrieve role by unique name.
|
||||
# @RELATION: DEPENDS_ON -> Role
|
||||
# @RELATION: DEPENDS_ON -> [Role]
|
||||
def get_role_by_name(self, name: str) -> Optional[Role]:
|
||||
with belief_scope("AuthRepository.get_role_by_name"):
|
||||
return self.db.query(Role).filter(Role.name == name).first()
|
||||
|
||||
# [/DEF:get_role_by_name:Function]
|
||||
|
||||
# [DEF:get_permission_by_id:Function]
|
||||
# @PURPOSE: Retrieve permission by UUID.
|
||||
# @RELATION: DEPENDS_ON -> Permission
|
||||
# @RELATION: DEPENDS_ON -> [Permission]
|
||||
def get_permission_by_id(self, permission_id: str) -> Optional[Permission]:
|
||||
with belief_scope("AuthRepository.get_permission_by_id"):
|
||||
return self.db.query(Permission).filter(Permission.id == permission_id).first()
|
||||
return (
|
||||
self.db.query(Permission).filter(Permission.id == permission_id).first()
|
||||
)
|
||||
|
||||
# [/DEF:get_permission_by_id:Function]
|
||||
|
||||
# [DEF:get_permission_by_resource_action:Function]
|
||||
# @PURPOSE: Retrieve permission by resource and action tuple.
|
||||
# @RELATION: DEPENDS_ON -> Permission
|
||||
def get_permission_by_resource_action(self, resource: str, action: str) -> Optional[Permission]:
|
||||
# @RELATION: DEPENDS_ON -> [Permission]
|
||||
def get_permission_by_resource_action(
|
||||
self, resource: str, action: str
|
||||
) -> Optional[Permission]:
|
||||
with belief_scope("AuthRepository.get_permission_by_resource_action"):
|
||||
return self.db.query(Permission).filter(
|
||||
Permission.resource == resource,
|
||||
Permission.action == action
|
||||
).first()
|
||||
return (
|
||||
self.db.query(Permission)
|
||||
.filter(Permission.resource == resource, Permission.action == action)
|
||||
.first()
|
||||
)
|
||||
|
||||
# [/DEF:get_permission_by_resource_action:Function]
|
||||
|
||||
# [DEF:list_permissions:Function]
|
||||
# @PURPOSE: List all system permissions.
|
||||
# @RELATION: DEPENDS_ON -> Permission
|
||||
# @RELATION: DEPENDS_ON -> [Permission]
|
||||
def list_permissions(self) -> List[Permission]:
|
||||
with belief_scope("AuthRepository.list_permissions"):
|
||||
return self.db.query(Permission).all()
|
||||
|
||||
# [/DEF:list_permissions:Function]
|
||||
|
||||
# [DEF:get_user_dashboard_preference:Function]
|
||||
# @PURPOSE: Retrieve dashboard filters/preferences for a user.
|
||||
# @RELATION: DEPENDS_ON -> UserDashboardPreference
|
||||
def get_user_dashboard_preference(self, user_id: str) -> Optional[UserDashboardPreference]:
|
||||
# @RELATION: DEPENDS_ON -> [UserDashboardPreference]
|
||||
def get_user_dashboard_preference(
|
||||
self, user_id: str
|
||||
) -> Optional[UserDashboardPreference]:
|
||||
with belief_scope("AuthRepository.get_user_dashboard_preference"):
|
||||
return self.db.query(UserDashboardPreference).filter(
|
||||
UserDashboardPreference.user_id == user_id
|
||||
).first()
|
||||
return (
|
||||
self.db.query(UserDashboardPreference)
|
||||
.filter(UserDashboardPreference.user_id == user_id)
|
||||
.first()
|
||||
)
|
||||
|
||||
# [/DEF:get_user_dashboard_preference:Function]
|
||||
|
||||
# [DEF:get_roles_by_ad_groups:Function]
|
||||
# @PURPOSE: Retrieve roles that match a list of AD group names.
|
||||
# @PRE: groups is a list of strings representing AD group identifiers.
|
||||
# @POST: Returns a list of Role objects mapped to the provided AD groups.
|
||||
# @RELATION: DEPENDS_ON -> Role
|
||||
# @RELATION: DEPENDS_ON -> ADGroupMapping
|
||||
# @RELATION: DEPENDS_ON -> [Role]
|
||||
# @RELATION: DEPENDS_ON -> [ADGroupMapping]
|
||||
def get_roles_by_ad_groups(self, groups: List[str]) -> List[Role]:
|
||||
with belief_scope("AuthRepository.get_roles_by_ad_groups"):
|
||||
logger.reason(f"Fetching roles for AD groups: {groups}")
|
||||
if not groups:
|
||||
return []
|
||||
return self.db.query(Role).join(ADGroupMapping).filter(
|
||||
ADGroupMapping.ad_group.in_(groups)
|
||||
).all()
|
||||
return (
|
||||
self.db.query(Role)
|
||||
.join(ADGroupMapping)
|
||||
.filter(ADGroupMapping.ad_group.in_(groups))
|
||||
.all()
|
||||
)
|
||||
|
||||
# [/DEF:get_roles_by_ad_groups:Function]
|
||||
|
||||
|
||||
# [/DEF:AuthRepository:Class]
|
||||
|
||||
# [/DEF:AuthRepositoryModule:Module]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#
|
||||
# @SEMANTICS: security, password, hashing, bcrypt
|
||||
# @PURPOSE: Utility for password hashing and verification using Passlib.
|
||||
# @COMPLEXITY: 2
|
||||
# @LAYER: Core
|
||||
# @RELATION: DEPENDS_ON -> bcrypt
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user