feat(users): passwordless user creation via emailed set-password link, display full name instead of username
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
Admins now create users with a name + email (no password) and role; a Send Email action (per-user, also reusable for resets) issues a one-time token and POSTs it to an admin-configurable webhook URL — intended for a Power Automate flow that delivers the actual email. The old admin-sets-the-password flow is kept server-side (LegacyUserCreateWithPassword) but no longer wired into the UI. Every user-facing surface (top bar, sidebar, user management, audit log, assignee pickers, dispute notifications) now shows full_name instead of username, falling back to username when unset. Also fixes long attack_procedure/expected_detection/suggested_text text overflowing its container in the template review panels and Red/Blue team fields (missing break-words on whitespace-pre-wrap blocks).
This commit is contained in:
@@ -10,6 +10,9 @@ from __future__ import annotations
|
||||
# Import uuid
|
||||
import uuid
|
||||
|
||||
# Import secrets
|
||||
import secrets
|
||||
|
||||
# Import Session from sqlalchemy.orm
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
@@ -88,6 +91,40 @@ def create_user(
|
||||
return user
|
||||
|
||||
|
||||
def create_user_without_password(db: Session, *, full_name: str, email: str, role: str) -> User:
|
||||
"""Create a new user without a password — they set their own via an
|
||||
admin-triggered, emailed one-time link (see password_setup_service).
|
||||
|
||||
``username`` is derived from ``email`` since it's still needed
|
||||
internally as the login identifier, but it's never surfaced in the UI.
|
||||
|
||||
Raises DuplicateEntityError if a user with this email/username already
|
||||
exists. Raises BusinessRuleViolation if role is invalid. Does not
|
||||
commit; the router handles that.
|
||||
"""
|
||||
existing = db.query(User).filter(User.username == email).first()
|
||||
if existing:
|
||||
raise DuplicateEntityError("User", "email", email)
|
||||
|
||||
if role not in VALID_ROLES:
|
||||
raise BusinessRuleViolation(
|
||||
f"Invalid role '{role}'. Must be one of: {', '.join(sorted(VALID_ROLES))}"
|
||||
)
|
||||
|
||||
user = User(
|
||||
username=email,
|
||||
email=email,
|
||||
full_name=full_name,
|
||||
# Unusable random password — the user can only ever get in via the
|
||||
# emailed set-password link, never by guessing/being told a value.
|
||||
hashed_password=hash_password(secrets.token_urlsafe(32)),
|
||||
role=role,
|
||||
must_change_password=True,
|
||||
)
|
||||
db.add(user)
|
||||
return user
|
||||
|
||||
|
||||
# Define function get_user_or_raise
|
||||
def get_user_or_raise(db: Session, user_id: uuid.UUID) -> User:
|
||||
"""Return a user by ID or raise EntityNotFoundError."""
|
||||
|
||||
Reference in New Issue
Block a user