# Business Rules — Unit 5: User Management ## Rule Index | ID | Rule | Enforced In | |---|---|---| | BR-U5-01 | Only Owner and Administrator can access UsersPage | Frontend (RoleGuard), Backend (AdminOnly policy) | | BR-U5-02 | Owner role cannot be assigned via invitation | Frontend (role selector), Backend | | BR-U5-03 | At least one Owner must exist at all times | Backend (change role endpoint) | | BR-U5-04 | Owner can change any user's role (subject to BR-U5-03) | Backend (change role endpoint) | | BR-U5-05 | Administrator can only promote User to Administrator | Backend (change role endpoint) | | BR-U5-06 | Copy invite link action is only shown for users where invitationPending = true | Frontend (conditional rendering) | | BR-U5-07 | Invite form requires valid email format and role selection | Frontend (zod validation) | | BR-U5-08 | An email already registered cannot be invited again | Backend (invite endpoint validation) | --- ## Decision Flow 1 — Invite User Authorization ```mermaid graph TD invite_req["Invite User request"] check_access{"Requester is Owner\nor Administrator?"} check_email{"Email has valid format?"} check_registered{"Email already registered?"} check_role{"Role is Administrator\nor User?"} success_invite["Create invitation token\nReturn inviteLink"] deny_403["403 Forbidden"] err_email["Validation error\nInvalid email format"] err_registered["Validation error\nEmail already in use"] err_role["Validation error\nOwner role cannot be invited"] invite_req --> check_access check_access -->|No| deny_403 check_access -->|Yes| check_email check_email -->|No| err_email check_email -->|Yes| check_registered check_registered -->|Yes| err_registered check_registered -->|No| check_role check_role -->|Owner| err_role check_role -->|Administrator or User| success_invite classDef start_node fill:#2196F3,stroke:#0d47a1,stroke-width:1px,color:#000 classDef decision_node fill:#FF9800,stroke:#e65100,stroke-width:1px,color:#000 classDef success_node fill:#4CAF50,stroke:#2e7d32,stroke-width:1px,color:#000 classDef error_node fill:#F44336,stroke:#b71c1c,stroke-width:1px,color:#000 class invite_req start_node class check_access,check_email,check_registered,check_role decision_node class success_invite success_node class deny_403,err_email,err_registered,err_role error_node ``` Text alternative: Invite request checked for requester access, email validity, registration status, and role — only valid Administrator/User invites proceed; all others are blocked. --- ## Decision Flow 2 — Change Role Authorization ```mermaid graph TD change_req["Change Role request\nPUT /Users/{userId}/role"] req_role{"Requester role?"} deny_user["403 Forbidden\nUser role cannot change roles"] admin_target{"Target user's current role?"} deny_admin["403 Forbidden\nAdmin cannot change Owner or Admin roles"] owner_check{"Would this leave\nzero Owners?"} deny_last["Blocked\nAt least 1 Owner required"] success_change["Update user role\nReturn 200 OK"] change_req --> req_role req_role -->|User| deny_user req_role -->|Administrator| admin_target req_role -->|Owner| owner_check admin_target -->|Owner or Administrator| deny_admin admin_target -->|User| success_change owner_check -->|Yes| deny_last owner_check -->|No| success_change classDef start_node fill:#2196F3,stroke:#0d47a1,stroke-width:1px,color:#000 classDef decision_node fill:#FF9800,stroke:#e65100,stroke-width:1px,color:#000 classDef success_node fill:#4CAF50,stroke:#2e7d32,stroke-width:1px,color:#000 classDef error_node fill:#F44336,stroke:#b71c1c,stroke-width:1px,color:#000 class change_req start_node class req_role,admin_target,owner_check decision_node class success_change success_node class deny_user,deny_admin,deny_last error_node ``` Text alternative: Role change checked for requester level — User is blocked; Administrator can only change User-role targets; Owner can change any role, blocked only if it would leave zero Owners. --- ## Frontend Validation Rules (InviteUserForm — zod schema) ```typescript const inviteUserSchema = z.object({ email: z.string().email({ message: 'Valid email address required' }), role: z.enum(['Administrator', 'User'], { required_error: 'Role selection is required', }), }); ``` ## Frontend Access Control Rules (RoleGuard / conditional rendering) | Element | Visibility condition | |---|---| | UsersPage route | `role === 'Owner' \|\| role === 'Administrator'` | | Invite User button | Always visible on UsersPage (same role requirement as page) | | Copy invite link action | `user.invitationPending === true` | | Change role dropdown | Visible to all UsersPage visitors; available target roles differ by requester role (see BR-U5-04, BR-U5-05) | | Owner option in role dropdown | Only shown when requester is Owner |