Feat(Chore): Verify Flow, Invites, Suspend, Depart Cert Policy

feat: add password reset and email verification flow
feat: add org invite listing, cancellation, and invite link fallback
feat: add user suspend/unsuspend with audit logging
feat: add department certificate policy (expiry, extensions)
feat: enforce dept cert policy on SSH certificate signing
feat: wire up OIDC consent and token flow (replace mocks)
feat: rework CLI auth bridge to use frontend login flow
feat: add admin OAuth provider management (CRUD)
chore: refactor model import paths after module reorganisation
chore: clean up config, decorators, and dev tooling
This commit is contained in:
2026-03-01 16:50:27 +05:45
parent 07193a2d2e
commit a0d4e59c24
39 changed files with 2035 additions and 611 deletions
+40 -3
View File
@@ -64,11 +64,47 @@ def login_required(f):
session.last_activity_at = datetime.now(timezone.utc)
from gatehouse_app import db
db.session.commit()
# Set context variables
g.current_user = session.user
g.current_session = session
user = session.user
token_groups: list = []
try:
if session.device_info:
# device_info may carry OIDC claims stored at login time
claims = session.device_info
# Normalise: Gatehouse stores roles as [{"organization_id":…,"role":…}]
roles_claim = claims.get("roles", [])
if isinstance(roles_claim, list):
for entry in roles_claim:
if isinstance(entry, dict):
role_val = entry.get("role")
if role_val:
token_groups.append(str(role_val))
elif isinstance(entry, str):
token_groups.append(entry)
# Standard OIDC groups claim
groups_claim = claims.get("groups", [])
if isinstance(groups_claim, list):
token_groups.extend(str(g_) for g_ in groups_claim if g_)
except Exception:
pass # Never block auth over token_groups enrichment failure
user.token_groups = token_groups
# Activation check: if the user has an `activated` attribute and it is
# explicitly False, block access. New accounts without the attribute are
# treated as active to avoid breaking existing sessions.
activated = getattr(user, "activated", None)
if activated is False:
return api_response(
success=False,
message="Account not yet activated. Please check your email for an activation link.",
status=403,
error_type="ACCOUNT_NOT_ACTIVATED",
)
return f(*args, **kwargs)
return decorated_function
@@ -97,11 +133,12 @@ def require_role(*allowed_roles):
raise ForbiddenError("Organization context required")
# Check user's role in the organization
from gatehouse_app.models.organization_member import OrganizationMember
from gatehouse_app.models.organization.organization_member import OrganizationMember
membership = OrganizationMember.query.filter_by(
user_id=g.current_user.id,
organization_id=org_id,
deleted_at=None,
).first()
if not membership: