feat(tests,users): blocking procedure-suggestion review on test detail, multi-role switcher, Power Automate webhook payload
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
- A lead opening a test with a pending procedure suggestion awaiting
their review now gets a blocking popup (approve/reject only) instead
of discovering it later in a separate queue.
- Users can be granted more than one role via extra_roles; only one is
ever active at a time (no permission mixing) and a top-bar switcher
lets the user swap which one, taking effect immediately since role
is read fresh from the DB on every request.
- The password-setup/reset webhook now posts the agreed Power Automate
contract ({to, subject, body} with the platform's standard greeting
and signature template) and supports an admin-configured API key
sent as an x-api-key header.
This commit is contained in:
@@ -159,6 +159,13 @@ def update_user(db: Session, user_id: uuid.UUID, **fields: object) -> User:
|
||||
f"Invalid role '{update_data['role']}'. Must be one of: {', '.join(sorted(VALID_ROLES))}"
|
||||
)
|
||||
|
||||
if update_data.get("extra_roles") is not None:
|
||||
invalid = [r for r in update_data["extra_roles"] if r not in VALID_ROLES]
|
||||
if invalid:
|
||||
raise BusinessRuleViolation(
|
||||
f"Invalid role(s) {', '.join(invalid)}. Must be one of: {', '.join(sorted(VALID_ROLES))}"
|
||||
)
|
||||
|
||||
# Check: "password" in update_data
|
||||
if "password" in update_data:
|
||||
# Assign update_data["hashed_password"] = hash_password(str(update_data.pop("password")))
|
||||
@@ -171,3 +178,25 @@ def update_user(db: Session, user_id: uuid.UUID, **fields: object) -> User:
|
||||
|
||||
# Return user
|
||||
return user
|
||||
|
||||
|
||||
def switch_active_role(db: Session, user: User, new_role: str) -> User:
|
||||
"""Swap *user*'s currently-active role with one from their extra_roles.
|
||||
|
||||
Roles never mix — the user acts under exactly one role at a time, this
|
||||
just changes which one. Raises BusinessRuleViolation if new_role isn't
|
||||
one this user has been granted. Does not commit; caller commits.
|
||||
"""
|
||||
available = {user.role, *(user.extra_roles or [])}
|
||||
if new_role not in available:
|
||||
raise BusinessRuleViolation(f"Role '{new_role}' is not available to this user")
|
||||
|
||||
if new_role == user.role:
|
||||
return user
|
||||
|
||||
old_role = user.role
|
||||
remaining = [r for r in (user.extra_roles or []) if r != new_role]
|
||||
remaining.append(old_role)
|
||||
user.role = new_role
|
||||
user.extra_roles = remaining
|
||||
return user
|
||||
|
||||
Reference in New Issue
Block a user