semantics: complete DEF-to-region migration, fix regressions

- Convert legacy [DEF🆔Type] anchors to #region/#endregion across 329 files
- Reinstate _normalize_timestamp_value in sql_generator.py
- Fix MarkerLogger→logger migration in events.py (molecular CoT markers)
- Fix dataset_review orchestrator dependencies (_build_execution_snapshot)
- Fix config_manager stale-record deletion (moved to save path only)
- Add 77 missing [/DEF:] closers in 5 unbalanced test files
- Update assistant_chat.integration.test.js for #region format
- Apply molecular-cot-logging markers (REASON/REFLECT/EXPLORE) via logger.* methods
This commit is contained in:
2026-05-12 23:54:55 +03:00
parent fe8978f716
commit 306c5ae742
331 changed files with 9630 additions and 10312 deletions

View File

@@ -1,7 +1,8 @@
# #region test_auth [C:3] [TYPE Module]
# @BRIEF Unit tests for authentication module
# @LAYER Domain
# @RELATION VERIFIES -> [AuthPackage]
# [DEF:test_auth:Module]
# @COMPLEXITY: 3
# @PURPOSE: Unit tests for authentication module
# @LAYER: Domain
# @RELATION: VERIFIES -> AuthPackage
import sys
from pathlib import Path
@@ -57,9 +58,9 @@ def auth_repo(db_session):
return AuthRepository(db_session)
# #region test_create_user [TYPE Function]
# @BRIEF Verifies that a persisted user can be retrieved with intact credential hash.
# @RELATION BINDS_TO -> [test_auth]
# [DEF:test_create_user:Function]
# @PURPOSE: Verifies that a persisted user can be retrieved with intact credential hash.
# @RELATION: BINDS_TO -> test_auth
def test_create_user(auth_repo):
"""Test user creation"""
user = User(
@@ -79,12 +80,12 @@ def test_create_user(auth_repo):
assert verify_password("testpassword123", retrieved_user.password_hash)
# #endregion test_create_user
# [/DEF:test_create_user:Function]
# #region test_authenticate_user [TYPE Function]
# @BRIEF Validates authentication outcomes for valid, wrong-password, and unknown-user cases.
# @RELATION BINDS_TO -> [test_auth]
# [DEF:test_authenticate_user:Function]
# @PURPOSE: Validates authentication outcomes for valid, wrong-password, and unknown-user cases.
# @RELATION: BINDS_TO -> test_auth
def test_authenticate_user(auth_service, auth_repo):
"""Test user authentication with valid and invalid credentials"""
user = User(
@@ -111,12 +112,12 @@ def test_authenticate_user(auth_service, auth_repo):
assert invalid_user is None
# #endregion test_authenticate_user
# [/DEF:test_authenticate_user:Function]
# #region test_create_session [TYPE Function]
# @BRIEF Ensures session creation returns bearer token payload fields.
# @RELATION BINDS_TO -> [test_auth]
# [DEF:test_create_session:Function]
# @PURPOSE: Ensures session creation returns bearer token payload fields.
# @RELATION: BINDS_TO -> test_auth
def test_create_session(auth_service, auth_repo):
"""Test session token creation"""
user = User(
@@ -136,12 +137,12 @@ def test_create_session(auth_service, auth_repo):
assert len(session["access_token"]) > 0
# #endregion test_create_session
# [/DEF:test_create_session:Function]
# #region test_role_permission_association [TYPE Function]
# @BRIEF Confirms role-permission many-to-many assignments persist and reload correctly.
# @RELATION BINDS_TO -> [test_auth]
# [DEF:test_role_permission_association:Function]
# @PURPOSE: Confirms role-permission many-to-many assignments persist and reload correctly.
# @RELATION: BINDS_TO -> test_auth
def test_role_permission_association(auth_repo):
"""Test role and permission association"""
role = Role(name="Admin", description="System administrator")
@@ -162,12 +163,12 @@ def test_role_permission_association(auth_repo):
assert "admin:users:WRITE" in permissions
# #endregion test_role_permission_association
# [/DEF:test_role_permission_association:Function]
# #region test_user_role_association [TYPE Function]
# @BRIEF Confirms user-role assignment persists and is queryable from repository reads.
# @RELATION BINDS_TO -> [test_auth]
# [DEF:test_user_role_association:Function]
# @PURPOSE: Confirms user-role assignment persists and is queryable from repository reads.
# @RELATION: BINDS_TO -> test_auth
def test_user_role_association(auth_repo):
"""Test user and role association"""
role = Role(name="Admin", description="System administrator")
@@ -190,12 +191,12 @@ def test_user_role_association(auth_repo):
assert retrieved_user.roles[0].name == "Admin"
# #endregion test_user_role_association
# [/DEF:test_user_role_association:Function]
# #region test_ad_group_mapping [TYPE Function]
# @BRIEF Verifies AD group mapping rows persist and reference the expected role.
# @RELATION BINDS_TO -> [test_auth]
# [DEF:test_ad_group_mapping:Function]
# @PURPOSE: Verifies AD group mapping rows persist and reference the expected role.
# @RELATION: BINDS_TO -> test_auth
def test_ad_group_mapping(auth_repo):
"""Test AD group mapping"""
role = Role(name="ADFS_Admin", description="ADFS administrators")
@@ -217,12 +218,12 @@ def test_ad_group_mapping(auth_repo):
assert retrieved_mapping.role_id == role.id
# #endregion test_ad_group_mapping
# [/DEF:test_ad_group_mapping:Function]
# #region test_authenticate_user_updates_last_login [TYPE Function]
# @BRIEF Verifies successful authentication updates last_login audit field.
# @RELATION BINDS_TO -> [test_auth]
# [DEF:test_authenticate_user_updates_last_login:Function]
# @PURPOSE: Verifies successful authentication updates last_login audit field.
# @RELATION: BINDS_TO -> test_auth
def test_authenticate_user_updates_last_login(auth_service, auth_repo):
"""@SIDE_EFFECT: authenticate_user updates last_login timestamp on success."""
user = User(
@@ -241,12 +242,12 @@ def test_authenticate_user_updates_last_login(auth_service, auth_repo):
assert authenticated.last_login is not None
# #endregion test_authenticate_user_updates_last_login
# [/DEF:test_authenticate_user_updates_last_login:Function]
# #region test_authenticate_inactive_user [TYPE Function]
# @BRIEF Verifies inactive accounts are rejected during password authentication.
# @RELATION BINDS_TO -> [test_auth]
# [DEF:test_authenticate_inactive_user:Function]
# @PURPOSE: Verifies inactive accounts are rejected during password authentication.
# @RELATION: BINDS_TO -> test_auth
def test_authenticate_inactive_user(auth_service, auth_repo):
"""@PRE: User with is_active=False should not authenticate."""
user = User(
@@ -263,24 +264,24 @@ def test_authenticate_inactive_user(auth_service, auth_repo):
assert result is None
# #endregion test_authenticate_inactive_user
# [/DEF:test_authenticate_inactive_user:Function]
# #region test_verify_password_empty_hash [TYPE Function]
# @BRIEF Verifies password verification safely rejects empty or null password hashes.
# @RELATION BINDS_TO -> [test_auth]
# [DEF:test_verify_password_empty_hash:Function]
# @PURPOSE: Verifies password verification safely rejects empty or null password hashes.
# @RELATION: BINDS_TO -> test_auth
def test_verify_password_empty_hash():
"""@PRE: verify_password with empty/None hash returns False."""
assert verify_password("anypassword", "") is False
assert verify_password("anypassword", None) is False
# #endregion test_verify_password_empty_hash
# [/DEF:test_verify_password_empty_hash:Function]
# #region test_provision_adfs_user_new [TYPE Function]
# @BRIEF Verifies JIT provisioning creates a new ADFS user and maps group-derived roles.
# @RELATION BINDS_TO -> [test_auth]
# [DEF:test_provision_adfs_user_new:Function]
# @PURPOSE: Verifies JIT provisioning creates a new ADFS user and maps group-derived roles.
# @RELATION: BINDS_TO -> test_auth
def test_provision_adfs_user_new(auth_service, auth_repo):
"""@POST: provision_adfs_user creates a new ADFS user with correct roles."""
# Set up a role and AD group mapping
@@ -307,7 +308,7 @@ def test_provision_adfs_user_new(auth_service, auth_repo):
assert user.roles[0].name == "ADFS_Viewer"
# #endregion test_provision_adfs_user_new
# [/DEF:test_provision_adfs_user_new:Function]
# [DEF:test_provision_adfs_user_existing:Function]
@@ -337,5 +338,5 @@ def test_provision_adfs_user_existing(auth_service, auth_repo):
assert len(user.roles) == 0 # No matching group mappings
# #endregion test_auth
# #endregion test_provision_adfs_user_existing
# [/DEF:test_auth:Module]
# [/DEF:test_provision_adfs_user_existing:Function]

View File

@@ -1,21 +1,19 @@
# #region AuthConfigModule [C:2] [TYPE Module] [SEMANTICS auth, config, settings, jwt, adfs]
#
# @BRIEF Centralized configuration for authentication and authorization.
# @LAYER Core
# @INVARIANT All sensitive configuration must have defaults or be loaded from environment.
# @RELATION DEPENDS_ON -> [pydantic]
#
# @LAYER: Core
# @RELATION DEPENDS_ON -> pydantic
#
# @INVARIANT: All sensitive configuration must have defaults or be loaded from environment.
# [SECTION: IMPORTS]
from pydantic import Field
from pydantic_settings import BaseSettings
# [/SECTION]
# #region AuthConfig [TYPE Class]
# @BRIEF Holds authentication-related settings.
# @PRE Environment variables may be provided via .env file.
# @POST Returns a configuration object with validated settings.
# @RELATION INHERITS -> [pydantic_settings.BaseSettings]
# @PRE: Environment variables may be provided via .env file.
# @POST: Returns a configuration object with validated settings.
# @RELATION INHERITS -> pydantic_settings.BaseSettings
class AuthConfig(BaseSettings):
# JWT Settings
SECRET_KEY: str = Field(default="super-secret-key-change-in-production", env="AUTH_SECRET_KEY")
@@ -41,7 +39,7 @@ class AuthConfig(BaseSettings):
# #region auth_config [TYPE Variable]
# @BRIEF Singleton instance of AuthConfig.
# @RELATION DEPENDS_ON -> [AuthConfig]
# @RELATION DEPENDS_ON -> AuthConfig
auth_config = AuthConfig()
# #endregion auth_config

View File

@@ -1,30 +1,25 @@
# #region AuthJwtModule [C:3] [TYPE Module] [SEMANTICS jwt, token, session, auth]
#
# @BRIEF JWT token generation and validation logic.
# @LAYER Core
# @INVARIANT Tokens must include expiration time and user identifier.
# @LAYER: Core
# @RELATION DEPENDS_ON -> [auth_config]
# @RELATION USES -> [auth_config]
#
#
# @INVARIANT: Tokens must include expiration time and user identifier.
# [SECTION: IMPORTS]
from datetime import datetime, timedelta
from typing import Optional
from jose import jwt
from .config import auth_config
from ..logger import belief_scope
# [/SECTION]
# #region create_access_token [TYPE Function]
# @BRIEF Generates a new JWT access token.
# @PRE data dict contains 'sub' (user_id) and optional 'scopes' (roles).
# @POST Returns a signed JWT string.
# @PRE: data dict contains 'sub' (user_id) and optional 'scopes' (roles).
# @POST: Returns a signed JWT string.
# @RELATION DEPENDS_ON -> [auth_config]
#
# @PARAM: data (dict) - Payload data for the token.
# @PARAM: expires_delta (Optional[timedelta]) - Custom expiration time.
# @RETURN: str - The encoded JWT.
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
with belief_scope("create_access_token"):
to_encode = data.copy()
@@ -47,13 +42,10 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -
# #region decode_token [TYPE Function]
# @BRIEF Decodes and validates a JWT token.
# @PRE token is a signed JWT string.
# @POST Returns the decoded payload if valid.
# @PRE: token is a signed JWT string.
# @POST: Returns the decoded payload if valid.
# @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(

View File

@@ -1,24 +1,19 @@
# #region AuthLoggerModule [C:3] [TYPE Module] [SEMANTICS auth, logger, audit, security]
#
# @BRIEF Audit logging for security-related events.
# @LAYER Core
# @INVARIANT Must not log sensitive data like passwords or full tokens.
# @RELATION USES -> [belief_scope]
#
# @LAYER: Core
# @RELATION USES -> belief_scope
#
# @INVARIANT: Must not log sensitive data like passwords or full tokens.
# [SECTION: IMPORTS]
from ..logger import logger, belief_scope
from datetime import datetime
# [/SECTION]
# #region log_security_event [TYPE Function]
# @BRIEF Logs a security-related event for audit trails.
# @PRE event_type and username are strings.
# @POST Security event is written to the application log.
# @RELATION USES -> [logger]
# @PARAM: event_type (str) - Type of event (e.g., LOGIN_SUCCESS, PERMISSION_DENIED).
# @PARAM: username (str) - The user involved in the event.
# @PARAM: details (dict) - Additional non-sensitive metadata.
# @PRE: event_type and username are strings.
# @POST: Security event is written to the application log.
# @RELATION USES -> logger
def log_security_event(event_type: str, username: str, details: dict = None):
with belief_scope("log_security_event", f"{event_type}:{username}"):
timestamp = datetime.utcnow().isoformat()
@@ -28,4 +23,4 @@ def log_security_event(event_type: str, username: str, details: dict = None):
logger.info(msg)
# #endregion log_security_event
# #endregion AuthLoggerModule
# #endregion AuthLoggerModule

View File

@@ -1,29 +1,27 @@
# #region AuthOauthModule [C:2] [TYPE Module] [SEMANTICS auth, oauth, oidc, adfs]
#
# @BRIEF ADFS OIDC configuration and client using Authlib.
# @LAYER Core
# @INVARIANT Must use secure OIDC flows.
# @RELATION DEPENDS_ON -> [authlib]
# @RELATION USES -> [auth_config]
#
# @LAYER: Core
# @RELATION DEPENDS_ON -> authlib
# @RELATION USES -> auth_config
#
# @INVARIANT: Must use secure OIDC flows.
# [SECTION: IMPORTS]
from authlib.integrations.starlette_client import OAuth
from .config import auth_config
# [/SECTION]
# #region oauth [TYPE Variable]
# @BRIEF Global Authlib OAuth registry.
# @RELATION DEPENDS_ON -> [OAuth]
# @RELATION DEPENDS_ON -> OAuth
oauth = OAuth()
# #endregion oauth
# #region register_adfs [TYPE Function]
# @BRIEF Registers the ADFS OIDC client.
# @PRE ADFS configuration is provided in auth_config.
# @POST ADFS client is registered in oauth registry.
# @RELATION USES -> [oauth]
# @RELATION USES -> [auth_config]
# @PRE: ADFS configuration is provided in auth_config.
# @POST: ADFS client is registered in oauth registry.
# @RELATION USES -> oauth
# @RELATION USES -> auth_config
def register_adfs():
if auth_config.ADFS_CLIENT_ID:
oauth.register(
@@ -39,10 +37,9 @@ def register_adfs():
# #region is_adfs_configured [TYPE Function]
# @BRIEF Checks if ADFS is properly configured.
# @PRE None.
# @POST Returns True if ADFS client is registered, False otherwise.
# @RETURN bool - Configuration status.
# @RELATION USES -> [oauth]
# @PRE: None.
# @POST: Returns True if ADFS client is registered, False otherwise.
# @RELATION USES -> oauth
def is_adfs_configured() -> bool:
"""Check if ADFS OAuth client is registered."""
return 'adfs' in oauth._registry
@@ -51,4 +48,4 @@ def is_adfs_configured() -> bool:
# Initial registration
register_adfs()
# #endregion AuthOauthModule
# #endregion AuthOauthModule

View File

@@ -1,39 +1,37 @@
# #region AuthRepositoryModule [C:5] [TYPE Module] [SEMANTICS auth, repository, database, user, role, permission]
# @BRIEF Data access layer for authentication and user preference entities.
# @LAYER Domain
# @PRE Database connection is active.
# @POST Provides valid access to identity data.
# @SIDE_EFFECT Executes database read queries through the injected SQLAlchemy session boundary.
# @DATA_CONTRACT Input[sqlalchemy.orm.Session] -> Output[User|Role|Permission|UserDashboardPreference access]
# @INVARIANT All database read/write operations must execute via the injected SQLAlchemy session boundary.
# @LAYER: Domain
# @RELATION DEPENDS_ON -> [AuthModels]
# @RELATION DEPENDS_ON -> [ProfileModels]
# @RELATION DEPENDS_ON -> [belief_scope]
# @INVARIANT: All database read/write operations must execute via the injected SQLAlchemy session boundary.
# @DATA_CONTRACT: Input[sqlalchemy.orm.Session] -> Output[User|Role|Permission|UserDashboardPreference access]
# @PRE: Database connection is active.
# @POST: Provides valid access to identity data.
# @SIDE_EFFECT: Executes database read queries through the injected SQLAlchemy session boundary.
# [SECTION: IMPORTS]
from typing import List, Optional
from sqlalchemy.orm import Session, selectinload
from ...models.auth import Permission, Role, User, ADGroupMapping
from ...models.profile import UserDashboardPreference
from ..logger import belief_scope, logger
# [/SECTION]
# #region AuthRepository [TYPE Class]
# @BRIEF 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.
# @PRE: Database session is bound.
# @POST: Entity instances returned safely.
# @SIDE_EFFECT: Performs database reads.
# @RELATION DEPENDS_ON -> [AuthModels]
class AuthRepository:
def __init__(self, db: Session):
self.db = db
# #region get_user_by_id [TYPE Function]
# @BRIEF Retrieve user by UUID.
# @PRE user_id is a valid UUID string.
# @POST Returns User object if found, else None.
# @RELATION DEPENDS_ON -> [User]
# [DEF:get_user_by_id:Function]
# @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]
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}")
@@ -41,13 +39,13 @@ class AuthRepository:
logger.reflect(f"User found: {result is not None}")
return result
# #endregion get_user_by_id
# [/DEF:get_user_by_id:Function]
# #region get_user_by_username [TYPE Function]
# @BRIEF Retrieve user by username.
# @PRE username is a non-empty string.
# @POST Returns User object if found, else None.
# @RELATION DEPENDS_ON -> [User]
# [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]
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}")
@@ -55,12 +53,12 @@ class AuthRepository:
logger.reflect(f"User found: {result is not None}")
return result
# #endregion get_user_by_username
# [/DEF:get_user_by_username:Function]
# #region get_role_by_id [TYPE Function]
# @BRIEF Retrieve role by UUID with permissions preloaded.
# @RELATION DEPENDS_ON -> [Role]
# @RELATION DEPENDS_ON -> [Permission]
# [DEF:get_role_by_id:Function]
# @PURPOSE: Retrieve role by UUID with permissions preloaded.
# @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 (
@@ -70,31 +68,31 @@ class AuthRepository:
.first()
)
# #endregion get_role_by_id
# [/DEF:get_role_by_id:Function]
# #region get_role_by_name [TYPE Function]
# @BRIEF Retrieve role by unique name.
# @RELATION DEPENDS_ON -> [Role]
# [DEF:get_role_by_name:Function]
# @PURPOSE: Retrieve role by unique name.
# @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()
# #endregion get_role_by_name
# [/DEF:get_role_by_name:Function]
# #region get_permission_by_id [TYPE Function]
# @BRIEF Retrieve permission by UUID.
# @RELATION DEPENDS_ON -> [Permission]
# [DEF:get_permission_by_id:Function]
# @PURPOSE: Retrieve permission by UUID.
# @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()
)
# #endregion get_permission_by_id
# [/DEF:get_permission_by_id:Function]
# #region get_permission_by_resource_action [TYPE Function]
# @BRIEF Retrieve permission by resource and action tuple.
# @RELATION DEPENDS_ON -> [Permission]
# [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]:
@@ -105,20 +103,20 @@ class AuthRepository:
.first()
)
# #endregion get_permission_by_resource_action
# [/DEF:get_permission_by_resource_action:Function]
# #region list_permissions [TYPE Function]
# @BRIEF List all system permissions.
# @RELATION DEPENDS_ON -> [Permission]
# [DEF:list_permissions:Function]
# @PURPOSE: List all system permissions.
# @RELATION: DEPENDS_ON -> [Permission]
def list_permissions(self) -> List[Permission]:
with belief_scope("AuthRepository.list_permissions"):
return self.db.query(Permission).all()
# #endregion list_permissions
# [/DEF:list_permissions:Function]
# #region get_user_dashboard_preference [TYPE Function]
# @BRIEF Retrieve dashboard filters/preferences for a user.
# @RELATION DEPENDS_ON -> [UserDashboardPreference]
# [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]:
@@ -129,14 +127,14 @@ class AuthRepository:
.first()
)
# #endregion get_user_dashboard_preference
# [/DEF:get_user_dashboard_preference:Function]
# #region get_roles_by_ad_groups [TYPE Function]
# @BRIEF 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]
# [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]
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}")
@@ -149,7 +147,7 @@ class AuthRepository:
.all()
)
# #endregion get_roles_by_ad_groups
# [/DEF:get_roles_by_ad_groups:Function]
# #endregion AuthRepository

View File

@@ -1,24 +1,19 @@
# #region AuthSecurityModule [C:2] [TYPE Module] [SEMANTICS security, password, hashing, bcrypt]
#
# @BRIEF Utility for password hashing and verification using Passlib.
# @LAYER Core
# @INVARIANT Uses bcrypt for hashing with standard work factor.
# @RELATION DEPENDS_ON -> [bcrypt]
#
# @LAYER: Core
# @RELATION DEPENDS_ON -> bcrypt
#
# @INVARIANT: Uses bcrypt for hashing with standard work factor.
# [SECTION: IMPORTS]
import bcrypt
# [/SECTION]
# #region verify_password [TYPE Function]
# @BRIEF Verifies a plain password against a hashed password.
# @PRE plain_password is a string, hashed_password is a bcrypt hash.
# @POST Returns True if password matches, False otherwise.
# @RELATION DEPENDS_ON -> [bcrypt]
# @PRE: plain_password is a string, hashed_password is a bcrypt hash.
# @POST: Returns True if password matches, False otherwise.
# @RELATION DEPENDS_ON -> bcrypt
#
# @PARAM: plain_password (str) - The unhashed password.
# @PARAM: hashed_password (str) - The stored hash.
# @RETURN: bool - Verification result.
def verify_password(plain_password: str, hashed_password: str) -> bool:
if not hashed_password:
return False
@@ -33,12 +28,10 @@ def verify_password(plain_password: str, hashed_password: str) -> bool:
# #region get_password_hash [TYPE Function]
# @BRIEF Generates a bcrypt hash for a plain password.
# @PRE password is a string.
# @POST Returns a secure bcrypt hash string.
# @RELATION DEPENDS_ON -> [bcrypt]
# @PRE: password is a string.
# @POST: Returns a secure bcrypt hash string.
# @RELATION DEPENDS_ON -> bcrypt
#
# @PARAM: password (str) - The password to hash.
# @RETURN: str - The generated hash.
def get_password_hash(password: str) -> str:
return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
# #endregion get_password_hash