Ir al contenido

ADR-014 — User Entity, Roles and Avatar System

ADR-014 — User Entity, Roles and Avatar System

Sección titulada «ADR-014 — User Entity, Roles and Avatar System»

Accepted

Coveris needs one user model serving both authentication identity (SimpleJWT local, Cognito prod) and business permissions. It must be OIDC-compatible so Cognito migration requires no schema or API changes.

sub (UUID) is the permanent OIDC identifier — it never changes. email is USERNAME_FIELD but can change. given_name and family_name replicate OIDC claims for direct Cognito token ingestion.

Five hierarchical roles are needed. Per-object RBAC (django-guardian) was rejected — MVP does not require it.

The UI (topbar, control panel, audit timeline) needs an avatar. S3 upload is out of scope for MVP. Instead: initials + deterministic color from sub, zero backend changes, WCAG AA 4.5:1. All needed data already flows from GET /api/v1/auth/me/.

User model. backend/apps/users/models.py extends AbstractBaseUser: sub (UUIDField, auto, non-editable, unique), email (EmailField, USERNAME_FIELD), given_name / family_name (CharField, max 255), role (CharField with choices), email_verified (BooleanField, default False), is_active, is_staff, date_joined. No other profile data in this table; extensions go in satellite tables.

Roles. Five roles on the role field, progressively restricted:

  • ADMIN — full access; manages users, org units, and all system configuration.
  • MANAGER — manages org units, employees, and assignments within their scope.
  • SUPERVISOR — read plus limited write restricted to their operational unit.
  • VIEWER — read-only across the entire application.
  • EMPLOYEE — reserved for future self-service; no functional use in MVP.

Default role: VIEWER. Escalation only via Django admin or an ADMIN-only endpoint.

User Avatar System. p-avatar renders [image] post-MVP; for MVP all users show [label] with shape="circle" (initials + color).

Initials (getInitials, frontend/src/app/shared/avatar-utils.ts): first letter of given_name + family_name; if only given_name, its first two chars; if neither, pi pi-user. Reactive to name changes.

Color (getAvatarColor, same file): first 8 hex digits of sub (no hyphens) mod 8 over palette — Blue #1e88e5, Green #43a047, Red #e53935, Purple #8e24aa, Orange #fb8c00, Cyan #00acc1, Indigo #3949ab, Pink #d81b60. All pass WCAG AA 4.5:1 with white text. Color is stable because sub is immutable ([[adr-008-authentication|ADR-008]]).

Consumers: app.topbar.ts, control-panel.component.ts, audit-log timeline (getInitialsFromFullName for non-system employee names).

User lifecycle. Creation via Django admin (MVP); self-registration deferred. Deactivation is soft-delete (is_active = False); no physical deletes in MVP. GDPR erasure (Art. 17) and portability (Art. 20) are authentication endpoints. Auth flow: [[adr-008-authentication|ADR-008]].

Image avatar (Option B, deferred). When S3 is available, add avatar_url (URLField, nullable) to User, PATCH /api/v1/auth/me/avatar/, and a migration. p-avatar switches from [label] to [image]; initials remain the permanent fallback.

Model is OIDC-aligned and Cognito-ready: switching to Cognito requires only configuring the JWT validator, no schema changes. sub keeps all foreign references stable regardless of email changes.

All avatar logic is frontend-only (purely presentational). No new endpoints, migrations, or security surface for MVP.

Initials + color is the permanent fallback — there is never a “no avatar” state in the UI.

Option B requires a migration, a new endpoint in API.md, and a superseding ADR. The nullable avatar_url field ensures backward compatibility.

EMPLOYEE is a model-only reserve in MVP; activating it requires an explicit ADR.