# 7. Access Control & Permissions — Test Cases

User Type: **Super Administrator**
Source: *Mi Digital Academy - Education CRM Features Document*
Spec: access_control_permissions.md — every feature, sub-feature, and rule covered

---

## Test Execution Policy
- Zero tolerance: any deviation from documented behavior = FAILED = bug
- Every bug is immediately logged/reported (Bug ID, feature, sub-feature, expected vs actual, severity) and fixed 100% before the group passes
- Feature group passes only at 100% test pass rate

## Coverage Matrix
| Feature | Sub-feature / Rule | Test IDs |
|---------|--------------------|----------|
| 7.1 Role-Based Access Control | Role-to-permission mapping enforced on every request | TC-SA-01-07-001 |
| 7.1 Role-Based Access Control | Feature-level access | TC-SA-01-07-002 |
| 7.1 Role-Based Access Control | Data-level scoping | TC-SA-01-07-003 |
| 7.1 Role-Based Access Control | Deny-by-default | TC-SA-01-07-004 |
| 7.1 Role-Based Access Control | Consistent denial experience | TC-SA-01-07-005 |
| 7.1 Role-Based Access Control | Access-denied logging | TC-SA-01-07-006 |
| 7.1 Role-Based Access Control | Prompt propagation (next request, no re-login) | TC-SA-01-07-007 |
| 7.1 Role-Based Access Control | Super Administrator exemption | TC-SA-01-07-008 |
| 7.1 Role-Based Access Control | Rule: changes take effect on the next request, not only at next login | TC-SA-01-07-009 |
| 7.1 Role-Based Access Control | Rule: two users with the same role see different record sets | TC-SA-01-07-010 |
| 7.1 Role-Based Access Control | Rule: all access-denied events logged with user, role, target | TC-SA-01-07-011 |
| 7.1 Role-Based Access Control | Rule: Super Admin not subject to feature or data scoping | TC-SA-01-07-012 |
| 7.1 Role-Based Access Control | Rule: denial enforced at the service layer, not just the UI | TC-SA-01-07-013 |
| 7.1 Role-Based Access Control | Rule: multiple roles → union of features, scoping still applies | TC-SA-01-07-014 |
| 7.2 Configure Permissions per Role | Permission catalog | TC-SA-01-07-015 |
| 7.2 Configure Permissions per Role | Role editor | TC-SA-01-07-016 |
| 7.2 Configure Permissions per Role | Preset role templates | TC-SA-01-07-017 |
| 7.2 Configure Permissions per Role | Custom roles with arbitrary permission sets | TC-SA-01-07-018 |
| 7.2 Configure Permissions per Role | Change preview (blast radius) | TC-SA-01-07-019 |
| 7.2 Configure Permissions per Role | Separation-of-duties flags | TC-SA-01-07-020 |
| 7.2 Configure Permissions per Role | Immediate propagation to all holders | TC-SA-01-07-021 |
| 7.2 Configure Permissions per Role | Audit logging with before/after and affected user count | TC-SA-01-07-022 |
| 7.2 Configure Permissions per Role | Rule: removing an in-use permission ends it on the next action | TC-SA-01-07-023 |
| 7.2 Configure Permissions per Role | Rule: SoD combinations flagged as warnings, not hard blocks | TC-SA-01-07-024 |
| 7.2 Configure Permissions per Role | Rule: every change audit-logged with before/after, admin, count | TC-SA-01-07-025 |
| 7.2 Configure Permissions per Role | Rule: editing a preset edits it for all holders (preview explicit) | TC-SA-01-07-026 |
| 7.2 Configure Permissions per Role | Rule: a role with no users can still be edited and saved | TC-SA-01-07-027 |
| 7.2 Configure Permissions per Role | Rule: the catalog is the single source of truth | TC-SA-01-07-028 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Activate | TC-SA-01-07-029 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Suspend | TC-SA-01-07-030 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Deactivate | TC-SA-01-07-031 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Bulk status changes | TC-SA-01-07-032 |
| 7.3 Activate, Suspend, and Deactivate Accounts | User notification on status change | TC-SA-01-07-033 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Subscription handling on suspension/deactivation | TC-SA-01-07-034 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Reason presets plus free-text note | TC-SA-01-07-035 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Audit logging of all status changes | TC-SA-01-07-036 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Rule: suspension is immediate; active sessions terminated on next request | TC-SA-01-07-037 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Rule: suspension pauses recurring billing; activation resumes it | TC-SA-01-07-038 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Rule: deactivation is irreversible from the UI | TC-SA-01-07-039 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Rule: deactivation triggers the privacy data-handling flow | TC-SA-01-07-040 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Rule: bulk changes require confirmed preview + reason; single batch | TC-SA-01-07-041 |
| 7.3 Activate, Suspend, and Deactivate Accounts | Rule: all changes notify the user with reason and appeal path | TC-SA-01-07-042 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Configurable failure threshold | TC-SA-01-07-043 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Configurable lockout duration or progressive lockout | TC-SA-01-07-044 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Scope: per account, per IP, or both | TC-SA-01-07-045 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Lockout notice with remaining time and support contact | TC-SA-01-07-046 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Admin view with filters and one-click unlock after verification | TC-SA-01-07-047 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Alert in security monitoring when lockouts spike | TC-SA-01-07-048 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Separate counting for password and OTP failures | TC-SA-01-07-049 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Audit logging of all lockout and unlock events | TC-SA-01-07-050 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Rule: counters reset after a successful login or window elapse | TC-SA-01-07-051 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Rule: progressive lockout increases duration up to a cap | TC-SA-01-07-052 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Rule: OTP failures counted separately from password failures | TC-SA-01-07-053 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Rule: per-IP and per-account lockouts active independently | TC-SA-01-07-054 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Rule: one-click unlock requires identity verification + recorded reason | TC-SA-01-07-055 |
| 7.4 Account Lockout after Repeated Failed Login Attempts | Rule: all lockout/unlock events audit-logged with full fields | TC-SA-01-07-056 |

## 7.1 Role-Based Access Control

### TC-SA-01-07-001 — Role-to-permission mapping enforced on every request
**Type:** Positive
**Covers:** 7.1 → Role-to-permission mapping enforced on every request (web and service surfaces)
**Preconditions:** Users with different roles; the role-to-permission mapping known.
**Steps:**
1. As each role, make requests to permitted and non-permitted features.
2. Verify permitted requests succeed and non-permitted requests are denied.
3. Repeat via the underlying service surface (not just the web UI).
**Expected Result:** Every request is checked against the user's role-to-permission mapping; permitted requests succeed and non-permitted ones are denied; the enforcement is consistent across the web interface and all underlying service surfaces; no request bypasses the check.
**Priority:** Critical

### TC-SA-01-07-002 — Feature-level access: sections and actions
**Type:** Positive
**Covers:** 7.1 → Feature-level access: which sections/modules a role can open and which actions it can perform
**Preconditions:** A role with a known feature-level permission set (e.g., Content Reviewer: review queue + content read).
**Steps:**
1. Log in as the role and verify the navigation shows only the permitted sections.
2. Open a permitted section and verify the permitted actions are available.
3. Verify a permitted section's non-permitted actions are not available.
4. Verify non-permitted sections are not in the navigation.
**Expected Result:** The navigation shows only the sections the role may open; within a permitted section only the permitted actions are available; non-permitted sections and actions are absent from the UI; the feature-level mapping matches the configuration.
**Priority:** High

### TC-SA-01-07-003 — Data-level scoping: which records a role can see
**Type:** Positive
**Covers:** 7.1 → Data-level scoping: which records a role can see (teacher → assigned courses; support agent → own queue)
**Preconditions:** A teacher with assigned courses; a support agent with a queue; other records they should not see.
**Steps:**
1. Log in as the teacher and verify they see only their assigned courses and students.
2. Log in as the support agent and verify they see only tickets in their queue.
3. Verify neither sees records outside their scope.
**Expected Result:** The teacher sees only their assigned courses/students (not other teachers' records); the support agent sees only their queue's tickets; the data-level scoping matches the configuration; no out-of-scope record is visible.
**Priority:** Critical

### TC-SA-01-07-004 — Deny-by-default: anything not explicitly permitted is denied
**Type:** Negative
**Covers:** 7.1 → Deny-by-default: any feature, action, or record not explicitly permitted is denied
**Preconditions:** A role with a minimal explicit permission set; features/actions/records not in the set.
**Steps:**
1. As the role, attempt to access a feature not in its permission set.
2. Attempt an action not in its permission set (within a permitted feature).
3. Attempt to access a record not in its data scope.
**Expected Result:** All three are denied (deny-by-default — there is no implicit allow); the denials are clear and consistent; each denial is logged; the default is deny, not allow.
**Priority:** Critical

### TC-SA-01-07-005 — Consistent denial experience with optional "request access"
**Type:** Positive
**Covers:** 7.1 → Consistent denial experience: a clear "you don't have permission" message with an optional "request access" path
**Preconditions:** A role that will be denied access to a section; the denial to trigger.
**Steps:**
1. As the role, navigate to a non-permitted section.
2. Read the denial message.
3. Verify the optional "request access" path is present and functional.
**Expected Result:** The denial shows a clear message (e.g., "You don't have permission to access this section."); the message is consistent across denials; the optional "request access" path is present and submits a request; the denial is logged.
**Priority:** Medium

### TC-SA-01-07-006 — Access-denied logging: user, role, target, timestamp
**Type:** Positive
**Covers:** 7.1 → Access-denied logging: every denial recorded with user, role, target, and timestamp
**Preconditions:** Several denials produced; the access-denied log.
**Steps:**
1. Produce denials across users, roles, and targets.
2. Locate all of them in the access-denied log.
3. Verify user, role, target, and timestamp on each.
**Expected Result:** Every denial is logged with the user, their role, the target (feature/action/record), and the timestamp; no denial is missing; the log supports spotting roles that repeatedly hit denials (misconfiguration signal); the log is exportable.
**Priority:** High

### TC-SA-01-07-007 — Prompt propagation: permission changes take effect on the next request
**Type:** Positive
**Covers:** 7.1 → Prompt propagation: permission changes take effect on the user's next request, without re-login
**Preconditions:** A user with an active session; a permission change to make for their role.
**Steps:**
1. Note the user's current access (an active session).
2. Add a permission to their role (no re-login).
3. Make the next request as the user and verify the new access is available.
4. Remove a permission and verify it is lost on the next request.
**Expected Result:** The added permission is available on the user's next request (no re-login needed); the removed permission is lost on the next request; the propagation is prompt (not deferred to the next login); the changes are logged.
**Priority:** High

### TC-SA-01-07-008 — Super Administrator exemption: full access, no scoping
**Type:** Positive
**Covers:** 7.1 → Super Administrator exemption: full access, not subject to scoping
**Preconditions:** Super Admin access; all sections and data.
**Steps:**
1. As the Super Admin, open every section/module.
2. Verify full access (no denials).
3. Verify data-level access is not scoped (all records visible).
**Expected Result:** The Super Admin has full access to every section and action; no feature or data scoping is applied; no denials occur for the Super Admin; the exemption is consistent across all surfaces.
**Priority:** High

### TC-SA-01-07-009 — Changes take effect on the next request, not only at next login
**Type:** Edge
**Covers:** 7.1 → Rule: permission changes take effect on the next request; a user mid-session gains/loses access promptly
**Preconditions:** A user mid-session (active for a while); a permission change to make.
**Steps:**
1. With the user mid-session, remove a permission they are using.
2. Make the next request as the user and verify the access is lost.
3. Add a permission and verify it is gained on the next request.
4. Verify no re-login was required for either.
**Expected Result:** The mid-session user loses the removed access on the next request (promptly, not at next login); they gain the added access on the next request; no re-login is required; the changes are logged; the user is not stranded (a clear denial if they attempt the lost access).
**Priority:** High

### TC-SA-01-07-010 — Two users with the same role see different record sets
**Type:** Positive
**Covers:** 7.1 → Rule: data scoping is per-user within the role (two teachers see different courses)
**Preconditions:** Two teachers with the same role, each with different assigned courses.
**Steps:**
1. Log in as Teacher A and record the visible courses.
2. Log in as Teacher B and record the visible courses.
3. Verify each sees only their own assigned courses (different record sets).
**Expected Result:** Teacher A sees only their assigned courses; Teacher B sees only their assigned courses; the two see different record sets despite the same role; the scoping is per-user within the role; no cross-teacher record is visible.
**Priority:** High

### TC-SA-01-07-011 — All access-denied events logged with user, role, target
**Type:** Positive
**Covers:** 7.1 → Rule: all access-denied events logged with user, role, and target for audit and misconfiguration spotting
**Preconditions:** Multiple denials produced; the access-denied log.
**Steps:**
1. Produce denials for several users/roles/targets.
2. Locate all in the log.
3. Verify user, role, and target on each.
4. Identify a role repeatedly hitting denials (misconfiguration signal).
**Expected Result:** Every access-denied event is logged with user, role, and target; the log supports the quarterly review (confirming enforcement and spotting misconfigured roles); a role repeatedly hitting denials is identifiable; the log is exportable.
**Priority:** High

### TC-SA-01-07-012 — Super Admin not subject to feature or data scoping
**Type:** Positive
**Covers:** 7.1 → Rule: the Super Administrator role has full access and is not subject to feature or data scoping
**Preconditions:** Super Admin access; a data set that would be scoped for other roles.
**Steps:**
1. As the Super Admin, verify access to every feature (no feature scoping).
2. Verify access to the full data set (no data scoping — e.g., all teachers' courses, all queues).
3. Confirm no denial is applied to the Super Admin.
**Expected Result:** The Super Admin is not subject to feature scoping (all features accessible) or data scoping (all records visible); no denial is applied; the exemption is absolute and consistent; the access is logged as Super Admin.
**Priority:** Medium

### TC-SA-01-07-013 — Denial enforced at the service layer, not just the UI
**Type:** Negative
**Covers:** 7.1 → Rule: denial enforced at the service layer; a hidden menu item cannot be reached by direct navigation or service calls
**Preconditions:** A role without access to a section (e.g., Pricing Management); the section's URL and underlying service endpoint.
**Steps:**
1. As the role, manually navigate to the non-permitted section's URL.
2. Observe the server-side denial.
3. Call the underlying service endpoint directly (bypassing the UI).
4. Observe the denial.
**Expected Result:** The direct URL navigation is denied server-side (not just hidden in the UI); the direct service call is also denied; the hidden menu item cannot be reached by any path without permission; both denials are logged with user, role, and target.
**Priority:** Critical

### TC-SA-01-07-014 — Multiple roles: union of features, scoping still applies
**Type:** Edge
**Covers:** 7.1 → Rule: a user with multiple roles gets the union of permitted features, but data scoping still applies
**Preconditions:** A user with two roles, each with different feature permissions and data scopes.
**Steps:**
1. As the multi-role user, verify access to the union of both roles' features.
2. Verify a feature in neither role is denied.
3. Verify the data scoping still applies per the scoping rules (not a union of all data).
**Expected Result:** The user can access the union of the permitted features from both roles; a feature in neither role is denied; the data scoping still applies per the scoping rules (the user does not gain a union of all data from both roles beyond the scoping rules); the access is logged with the effective role set.
**Priority:** High

## 7.2 Configure Permissions per Role

### TC-SA-01-07-015 — Permission catalog: the full list of grantable permissions
**Type:** Positive
**Covers:** 7.2 → Permission catalog: the full list of grantable permissions (features, actions, data scopes)
**Preconditions:** Super Admin access to Role & Permission Management; the permission catalog.
**Steps:**
1. Open the permission catalog.
2. Verify it lists all grantable permissions (features, actions within features, data scopes).
3. Verify the catalog is organized and complete (no grantable permission missing).
**Expected Result:** The catalog shows the full list of grantable permissions (features, actions, data scopes); it is organized (by module); it is the single source of truth (every grantable permission is present); the catalog is browsable/searchable.
**Priority:** High

### TC-SA-01-07-016 — Role editor: toggle permissions on/off per role, organized by module
**Type:** Positive
**Covers:** 7.2 → Role editor: toggle permissions on/off per role, organized by module
**Preconditions:** A role to edit; the role editor.
**Steps:**
1. Open the role editor for the role.
2. Verify the current permission set is shown, organized by module.
3. Toggle a permission off and verify the change is staged.
4. Toggle another on and verify it is staged.
**Expected Result:** The role editor shows the role's permission set organized by module; permissions can be toggled on/off; the changes are staged (not applied until saved); the editor reflects the current state accurately; the toggles are intuitive.
**Priority:** High

### TC-SA-01-07-017 — Preset role templates with sensible defaults
**Type:** Positive
**Covers:** 7.2 → Preset role templates (Billing Administrator, Teacher, Content Reviewer, Support Agent) with sensible defaults
**Preconditions:** The preset role templates; the role editor.
**Steps:**
1. Open each preset template (Billing Administrator, Teacher, Content Reviewer, Support Agent).
2. Verify each has sensible default permissions for its function.
3. Verify the presets match the documented role responsibilities.
**Expected Result:** Each preset template exists with sensible defaults (Billing Administrator → billing permissions; Teacher → course permissions; Content Reviewer → review permissions; Support Agent → ticket permissions); the defaults match the documented responsibilities; the presets are starting points (editable).
**Priority:** Medium

### TC-SA-01-07-018 — Custom roles with arbitrary permission sets
**Type:** Positive
**Covers:** 7.2 → Custom roles with arbitrary permission sets
**Preconditions:** The role editor; a base role to copy from.
**Steps:**
1. Create a new custom role (e.g., "Refunds Processor") by copying a preset.
2. Remove/add permissions to form an arbitrary set.
3. Save the custom role.
4. Assign it to a user and verify the access matches.
**Expected Result:** The custom role is created with the arbitrary permission set (copied from the preset then modified); it is saved and assignable; a user assigned to it gets exactly the custom set; the custom role appears in the role list; the creation is audit-logged.
**Priority:** High

### TC-SA-01-07-019 — Change preview: blast radius before saving
**Type:** Positive
**Covers:** 7.2 → Change preview: before saving, see what a permission change will affect (how many users, which capabilities gained/lost)
**Preconditions:** A role with holders; a permission change to stage.
**Steps:**
1. Stage a permission change in the role editor.
2. Open the change preview before saving.
3. Verify it shows the affected user count and the capabilities gained/lost.
4. Save and verify the preview matched the actual effect.
**Expected Result:** The preview shows the blast radius (how many users are affected and which capabilities are gained/lost) before saving; the preview is accurate (matches the actual effect after saving); the preview makes the impact explicit (especially for preset roles); the preview is available for every change.
**Priority:** High

### TC-SA-01-07-020 — Separation-of-duties flags on risky combinations
**Type:** Positive
**Covers:** 7.2 → Separation-of-duties flags: warn on risky combinations (e.g., create discount codes + approve refunds)
**Preconditions:** The role editor; a role to configure with a risky combination.
**Steps:**
1. In the role editor, enable a risky combination (e.g., "create discount codes" + "approve refunds").
2. Observe the separation-of-duties warning.
3. Remove one of the capabilities and verify the warning clears.
**Expected Result:** The risky combination triggers a separation-of-duties warning in the editor; the warning identifies the specific combination; removing one capability clears the warning; the warning is a flag (not a hard block — the admin can proceed deliberately); the flag is logged.
**Priority:** High

### TC-SA-01-07-021 — Immediate propagation of permission changes to all holders
**Type:** Positive
**Covers:** 7.2 → Immediate propagation of permission changes to all holders of the role
**Preconditions:** A role with multiple holders; a permission change to make.
**Steps:**
1. Note the holders' current access.
2. Make a permission change to the role and save.
3. As each holder, make the next request and verify the change is reflected.
**Expected Result:** The permission change propagates immediately to all holders of the role (each sees the change on their next request); no holder is missed; the propagation is prompt (not deferred); the change is logged with the affected user count.
**Priority:** Critical

### TC-SA-01-07-022 — Audit logging with before/after state and affected user count
**Type:** Positive
**Covers:** 7.2 → Audit logging of every permission change with before/after state and affected user count
**Preconditions:** Permission changes produced; audit log access.
**Steps:**
1. Produce several permission changes (add, remove, role create).
2. Locate all in the audit log.
3. Verify before/after state, the acting admin, and the affected user count on each.
**Expected Result:** Every permission change is audit-logged with the before/after permission sets, the acting admin, and the affected user count; no change lacks its before/after or count; the log supports the separation-of-duties review; the log is exportable.
**Priority:** High

### TC-SA-01-07-023 — Removing an in-use permission ends it on the next action
**Type:** Edge
**Covers:** 7.2 → Rule: removing a permission a user is actively using ends that capability on the next action; in-progress screens may complete the current request
**Preconditions:** A user actively using a permission (e.g., on a billing screen); the permission to remove.
**Steps:**
1. With the user on a screen using the permission, remove it from their role.
2. Verify the in-progress screen can complete its current request.
3. Attempt a new action with the lost permission.
4. Observe the denial.
**Expected Result:** The in-progress screen may complete its current request (not cut off mid-action); the new action with the lost permission is denied (the capability ends on the next action); the denial is clear and logged; the user is not stranded (a clear message, not a crash).
**Priority:** High

### TC-SA-01-07-024 — SoD combinations flagged as warnings, not hard blocks
**Type:** Edge
**Covers:** 7.2 → Rule: critical SoD combinations are flagged as warnings (not hard blocks) for a deliberate choice
**Preconditions:** The role editor; a critical SoD combination to configure.
**Steps:**
1. Enable a critical SoD combination in the role editor.
2. Observe the warning (not a block).
3. Verify the admin can proceed (save) with the warning acknowledged.
4. Verify the warning is logged with the deliberate choice.
**Expected Result:** The critical SoD combination is flagged as a warning (the save is not hard-blocked); the admin can make an informed, deliberate choice to proceed; the warning is clear about the risk; the deliberate choice (proceeding with the warning) is logged; the flag does not silently allow the combination.
**Priority:** Medium

### TC-SA-01-07-025 — Every change audit-logged with before/after, admin, count
**Type:** Positive
**Covers:** 7.2 → Rule: every permission change audit-logged with before/after state, the acting admin, and the affected user count
**Preconditions:** Multiple permission changes by different admins; audit log access.
**Steps:**
1. Produce changes by two different admins.
2. Locate all in the audit log.
3. Verify before/after state, the acting admin, and the affected user count on each.
**Expected Result:** Every permission change is audit-logged with the before/after state, the acting admin (distinguishing which admin), and the affected user count; no change lacks any field; the log supports the security and compliance review; the log is exportable.
**Priority:** High

### TC-SA-01-07-026 — Editing a preset edits it for all holders; preview makes this explicit
**Type:** Edge
**Covers:** 7.2 → Rule: editing a preset role edits that role for all its holders (the preview makes this explicit)
**Preconditions:** A preset role with multiple holders; a permission change to make.
**Steps:**
1. Open the preset role in the editor and stage a change.
2. Verify the change preview explicitly states the change affects all holders of the preset.
3. Save and verify all holders are affected.
**Expected Result:** Editing the preset role changes it for all its holders (not just a copy); the change preview explicitly states the all-holders impact (making the blast radius clear); after saving, all holders reflect the change on their next request; the change is logged with the affected user count.
**Priority:** High

### TC-SA-01-07-027 — A role with no users can still be edited and saved
**Type:** Edge
**Covers:** 7.2 → Rule: a role with no users can still be edited and saved; it takes effect when users are assigned
**Preconditions:** A role with zero users; a permission change to make.
**Steps:**
1. Open the empty role in the editor.
2. Make a permission change and save.
3. Verify the save succeeds (no error for zero users).
4. Assign a user to the role and verify the saved permissions apply.
**Expected Result:** The empty role can be edited and saved (no error for having no users); the saved permissions take effect when users are assigned to the role; the assigned user gets the saved permission set; the change is logged (affected user count = 0 at save time).
**Priority:** Medium

### TC-SA-01-07-028 — The catalog is the single source of truth
**Type:** Negative
**Covers:** 7.2 → Rule: a role cannot grant a permission that is not in the catalog
**Preconditions:** The role editor; a permission that is not in the catalog (a non-existent/invalid permission).
**Steps:**
1. In the role editor, attempt to grant a permission that is not in the catalog.
2. Observe the validation.
3. Verify only catalog permissions are grantable.
**Expected Result:** A non-catalog permission cannot be granted (the editor only offers catalog permissions; a crafted request for a non-catalog permission is rejected); the catalog is the single source of truth; the validation prevents granting unknown permissions; the attempt is logged.
**Priority:** High

## 7.3 Activate, Suspend, and Deactivate Accounts

### TC-SA-01-07-029 — Activate: enable a new or previously suspended account
**Type:** Positive
**Covers:** 7.3 → Activate: enable a new or previously suspended account, restoring access
**Preconditions:** A suspended account (or a new inactive account); Super Admin access to the user record.
**Steps:**
1. Open the suspended account's record.
2. Select "Activate" with a reason.
3. Verify the account can log in and use the platform.
4. Verify the user is notified.
**Expected Result:** The account is activated (access restored); the user can log in and use the platform; the user is notified their account is active again; the activation is logged with actor, target, reason, and timestamp; billing resumes per policy.
**Priority:** Critical

### TC-SA-01-07-030 — Suspend: temporarily block with a reason; data retained; reversible
**Type:** Positive
**Covers:** 7.3 → Suspend: temporarily block access with a reason; data retained; reversible; active sessions ended
**Preconditions:** An active account with sessions; Super Admin access to the user record.
**Steps:**
1. Open the account's record and select "Suspend" with a reason preset + note.
2. Verify the account is blocked from logging in.
3. Verify the active sessions are ended on their next request.
4. Verify the data is retained (not deleted).
5. Verify the user is notified.
**Expected Result:** The account is suspended (cannot log in); the active sessions are ended on their next request; the data is retained (suspension is reversible, not a deletion); the user is notified with the reason and appeal path; the suspension is logged with actor, target, reason, and timestamp.
**Priority:** Critical

### TC-SA-01-07-031 — Deactivate: permanently close; triggers data handling per the privacy policy
**Type:** Positive
**Covers:** 7.3 → Deactivate: permanently close the account; triggers data handling per the privacy policy (retention, anonymization, deletion)
**Preconditions:** An account to close permanently (user requested closure); Super Admin access.
**Steps:**
1. Open the account's record and select "Deactivate".
2. Review the data-handling summary (what is deleted, retained for legal reasons, anonymized; downstream effects).
3. Confirm the deactivation.
4. Verify the account is closed and the data-handling flow is triggered.
**Expected Result:** The account is permanently closed; the data-handling summary is shown before confirmation (deleted vs retained vs anonymized, plus downstream effects like subscription cancellation and seat release); the confirmation triggers the privacy data-handling flow; the deactivation is logged with the data-handling outcome; the account cannot log in.
**Priority:** Critical

### TC-SA-01-07-032 — Bulk status changes for groups of accounts
**Type:** Positive
**Covers:** 7.3 → Bulk status changes for groups of accounts (e.g., a cohort, an institute's departed staff)
**Preconditions:** A group of accounts to change (e.g., an institute's departed staff); Super Admin access.
**Steps:**
1. Select the group of accounts.
2. Initiate a bulk status change (e.g., suspend) with a reason.
3. Review the confirmed preview of the affected account count.
4. Confirm and verify all accounts in the group are changed.
**Expected Result:** The bulk status change applies to all accounts in the group; the confirmed preview shows the affected account count before confirming; the recorded reason is captured; all accounts are changed consistently; the change is audit-logged as a single batch with the member list.
**Priority:** High

### TC-SA-01-07-033 — User notification on status change with reason and appeal path
**Type:** Positive
**Covers:** 7.3 → User notification on status change with the reason and the appeal/contact path
**Preconditions:** Status changes to perform (suspend, activate, deactivate); monitored user channels.
**Steps:**
1. Suspend an account and read the user notification.
2. Activate the account and read the notification.
3. Deactivate an account and read the notification.
4. Verify each includes the reason and the appeal/contact path.
**Expected Result:** Each status change notifies the user with the reason and the appeal/contact path; the suspension notice includes the appeal path; the activation notice confirms access is restored; the deactivation notice confirms the closure; all notifications are delivered and logged.
**Priority:** High

### TC-SA-01-07-034 — Subscription handling on suspension/deactivation
**Type:** Positive
**Covers:** 7.3 → Subscription handling on suspension/deactivation: pause, prorate, or cancel per policy
**Preconditions:** An account with an active subscription; suspension and deactivation to perform.
**Steps:**
1. Suspend the account and verify the subscription is paused (per policy).
2. Activate the account and verify the subscription resumes (per policy).
3. Deactivate a different account and verify the subscription is cancelled (per policy).
**Expected Result:** Suspension pauses the subscription (the user is not charged while blocked); activation resumes it per policy; deactivation cancels it per policy; the handling matches the configured policy (pause/prorate/cancel); the subscription changes are logged.
**Priority:** High

### TC-SA-01-07-035 — Reason presets plus free-text note for every status change
**Type:** Positive
**Covers:** 7.3 → Reason presets plus free-text note for every status change
**Preconditions:** Status changes to perform; the reason presets.
**Steps:**
1. Perform a status change using a reason preset (e.g., "Policy violation").
2. Add a free-text note referencing the incident.
3. Verify both the preset and the note are recorded.
4. Perform a change with only a free-text note (no preset) and verify it is accepted.
**Expected Result:** Every status change captures a reason (preset and/or free-text note); the preset and the note are both recorded; a free-text-only reason is accepted; no status change is recorded without a reason; the reasons are visible in the audit log.
**Priority:** Medium

### TC-SA-01-07-036 — Audit logging of all status changes with actor, target, reason, timestamp
**Type:** Positive
**Covers:** 7.3 → Audit logging of all status changes with actor, target, reason, and timestamp
**Preconditions:** Status changes produced (activate, suspend, deactivate, bulk); audit log access.
**Steps:**
1. Produce individual and bulk status changes.
2. Locate all in the audit log.
3. Verify actor, target, reason, and timestamp on each.
**Expected Result:** Every status change is audit-logged with the actor, the target account, the reason, and the timestamp; bulk changes are logged as a single batch with the member list; the deactivation shows the data-handling outcome; no change is missing; the log is exportable.
**Priority:** High

### TC-SA-01-07-037 — Suspension is immediate; active sessions terminated on the next request
**Type:** Negative
**Covers:** 7.3 → Rule: suspension is immediate; the account cannot log in, and active sessions are terminated on their next request
**Preconditions:** An active account with multiple active sessions; a suspension to perform.
**Steps:**
1. Suspend the account.
2. Attempt a new login (blocked).
3. On each active session, perform the next request.
4. Observe the session terminations.
**Expected Result:** The suspension is immediate (a new login is blocked at once); every active session is terminated on its next request (each fails and is cut off); no session survives the suspension; the terminations are logged; the account cannot access the platform by any path.
**Priority:** Critical

### TC-SA-01-07-038 — Suspension pauses recurring billing; activation resumes it
**Type:** Positive
**Covers:** 7.3 → Rule: suspension pauses recurring billing per policy; resuming activation resumes billing per policy
**Preconditions:** An account with an active recurring subscription; suspension and activation to perform.
**Steps:**
1. Suspend the account and verify the recurring billing is paused (no charge while blocked).
2. Activate the account and verify the recurring billing resumes per policy.
3. Verify the billing state changes are logged.
**Expected Result:** The suspension pauses the recurring billing (the blocked user is not charged); the activation resumes the billing per policy; the billing state changes are consistent with the account state; the changes are logged; no charge occurs during the suspension.
**Priority:** High

### TC-SA-01-07-039 — Deactivation is irreversible from the UI
**Type:** Edge
**Covers:** 7.3 → Rule: deactivation is irreversible from the UI; reactivation requires a new registration or a support-assisted restore within the retention window
**Preconditions:** A deactivated account; the user directory.
**Steps:**
1. Deactivate the account.
2. In the user directory, verify there is no "reactivate" action for the deactivated account.
3. Attempt to log in with the deactivated account's credentials (blocked).
4. Verify the reactivation path is a new registration or a support-assisted restore within the retention window.
**Expected Result:** The deactivation is irreversible from the UI (no reactivate button/action); the deactivated account cannot log in; the only reactivation paths are a new registration or a support-assisted restore within the retention window; the irreversibility is clear; the deactivation is logged.
**Priority:** High

### TC-SA-01-07-040 — Deactivation triggers the privacy data-handling flow
**Type:** Positive
**Covers:** 7.3 → Rule: deactivation triggers the privacy data-handling flow (personal data deleted or anonymized per legal retention, with downstream effects)
**Preconditions:** A deactivated account; the privacy data-handling flow; the downstream effects (subscription, seat).
**Steps:**
1. Deactivate the account.
2. Verify the privacy data-handling flow is triggered (personal data deleted or anonymized per legal retention).
3. Verify the downstream effects (subscription cancellation, institute license seat release) are handled.
4. Inspect the data-handling outcome in the audit log.
**Expected Result:** The deactivation triggers the privacy data-handling flow (personal data is deleted or anonymized per the legal retention rules); the downstream effects (subscription cancellation, seat release) are handled as part of the flow; the data-handling outcome is recorded in the audit log; the flow matches the privacy policy (9.3).
**Priority:** Critical

### TC-SA-01-07-041 — Bulk changes require a confirmed preview and a recorded reason; single batch
**Type:** Edge
**Covers:** 7.3 → Rule: bulk status changes require a confirmed preview of the affected count and a recorded reason; audit-logged as a single batch with the member list
**Preconditions:** A group of accounts; a bulk change to perform.
**Steps:**
1. Initiate a bulk change without confirming the preview (verify it is blocked).
2. Initiate it without a reason (verify it is blocked).
3. Complete it with the confirmed preview and a recorded reason.
4. Verify the audit log shows a single batch with the member list.
**Expected Result:** The bulk change cannot proceed without a confirmed preview of the affected account count; it cannot proceed without a recorded reason; with both, it completes; the audit log records it as a single batch (not one entry per account) with the full member list; the batch is traceable.
**Priority:** High

### TC-SA-01-07-042 — All status changes notify the user with reason and appeal path
**Type:** Positive
**Covers:** 7.3 → Rule: all status changes notify the user with the reason and the appeal/contact path, and are audit-logged
**Preconditions:** Multiple status changes (individual and bulk); monitored user channels.
**Steps:**
1. Perform individual and bulk status changes.
2. Verify each affected user is notified with the reason and the appeal/contact path.
3. Verify each change is audit-logged with actor, reason, and timestamp.
**Expected Result:** Every affected user (individual and bulk) is notified with the reason and the appeal/contact path; no status change is silent; every change is audit-logged with actor, reason, and timestamp; the notifications and the log entries match the actual changes.
**Priority:** High

## 7.4 Account Lockout after Repeated Failed Login Attempts

### TC-SA-01-07-043 — Configurable failure threshold
**Type:** Positive
**Covers:** 7.4 → Configurable failure threshold (e.g., 5 consecutive failed attempts)
**Preconditions:** Super Admin access to the lockout configuration; a test account.
**Steps:**
1. Set the failure threshold to a known value (e.g., 5).
2. Generate (threshold − 1) consecutive failed logins and verify no lockout.
3. Generate the threshold-th failed login and verify the lockout triggers.
**Expected Result:** The lockout triggers exactly at the configured threshold (the (threshold−1)-th failure does not lock; the threshold-th does); the threshold is configurable; the boundary is exact (no off-by-one); the lockout is logged with the threshold crossed.
**Priority:** Critical

### TC-SA-01-07-044 — Configurable lockout duration or progressive lockout
**Type:** Positive
**Covers:** 7.4 → Configurable lockout duration (e.g., 15 minutes) or progressive lockout (longer after repeated lockouts, capped)
**Preconditions:** The lockout configuration; a test account; both duration modes to test.
**Steps:**
1. With a fixed duration, trigger a lockout and verify it lasts the configured duration.
2. Switch to progressive lockout and trigger successive lockouts.
3. Verify each successive lockout is longer (up to the cap).
4. Verify the cap is respected (no unbounded duration).
**Expected Result:** The fixed-duration lockout lasts exactly the configured duration; the progressive lockout increases the duration for successive lockouts (e.g., doubles) up to the cap; the cap is respected (a persistent attacker does not cause unbounded lockout durations); the durations are logged.
**Priority:** High

### TC-SA-01-07-045 — Scope: per account, per IP, or both (independent counters)
**Type:** Positive
**Covers:** 7.4 → Scope: per account, per IP, or both (independent counters)
**Preconditions:** The lockout scope configuration; test accounts and IPs.
**Steps:**
1. With per-account scope, generate failures for one account from one IP and verify the account locks.
2. With per-IP scope, generate failures for many accounts from one IP and verify the IP locks.
3. With both, verify the independent counters (an account can lock from its failures; an IP can lock from its aggregate).
**Expected Result:** The per-account scope locks a single targeted account after its failures; the per-IP scope locks an IP after aggregate failures across accounts; with both, the counters are independent (each locks on its own threshold); the scope matches the configuration; the lockouts are logged with the scope.
**Priority:** High

### TC-SA-01-07-046 — Lockout notice with remaining time and support contact
**Type:** Positive
**Covers:** 7.4 → Lockout notice shown to the user with the remaining time and the support contact
**Preconditions:** A lockout to trigger; a monitored user.
**Steps:**
1. Trigger a lockout for the user.
2. Attempt a login during the lockout.
3. Read the lockout notice.
4. Verify the remaining time and the support contact are present.
**Expected Result:** The lockout notice is shown to the user with the remaining lockout time and the support contact; the notice is clear (not a generic failure); the remaining time is accurate; the support contact is actionable; the lockout is logged.
**Priority:** High

### TC-SA-01-07-047 — Admin view of locked accounts with filters and one-click unlock
**Type:** Positive
**Covers:** 7.4 → Admin view of locked accounts with filters (by account, IP, time) and one-click unlock after identity verification
**Preconditions:** Locked accounts (by account and by IP); Super Admin access to the locked-accounts view.
**Steps:**
1. Open the locked-accounts view.
2. Filter by account, IP, and time and verify the filters work.
3. Select a legitimate locked user, verify their identity, and perform the one-click unlock.
4. Verify the user can log in again.
**Expected Result:** The admin view lists locked accounts with filters by account, IP, and time; the filters return the correct results; the one-click unlock (after identity verification) clears the lockout; the unlocked user can log in; the unlock is logged with the acting admin and reason.
**Priority:** High

### TC-SA-01-07-048 — Alert in security monitoring when lockouts spike
**Type:** Positive
**Covers:** 7.4 → Alert in security monitoring when lockouts spike (possible attack)
**Preconditions:** Security monitoring access; a lockout spike to generate.
**Steps:**
1. Generate a spike in account lockouts (e.g., from a single IP range over an hour).
2. Observe the alert in security monitoring.
3. Drill into the alert and verify the pattern (many emails, one IP).
**Expected Result:** The lockout spike raises an alert in security monitoring (flagged as a possible attack); the alert shows the pattern (the originating IP range, the affected accounts, the time window); drilling in reveals the credential-stuffing pattern; the alert is actionable (link to the locked-accounts view); the alert is logged.
**Priority:** High

### TC-SA-01-07-049 — Separate counting for password and OTP failures
**Type:** Positive
**Covers:** 7.4 → Separate counting for password failures and OTP failures
**Preconditions:** A test account; the separate counters; password and OTP failure scenarios.
**Steps:**
1. Generate OTP failures (bad/expired SMS) up to just below the threshold and verify the password path is not locked.
2. Generate password failures up to the threshold and verify the password path locks.
3. Verify the OTP failures did not contribute to the password lockout.
**Expected Result:** OTP failures are counted separately from password failures; a series of bad/expired OTPs does not lock the account's password path; the password path locks only on password failures reaching its threshold; the separate counters are independent; the lockouts are logged with the failure type.
**Priority:** High

### TC-SA-01-07-050 — Audit logging of all lockout and unlock events
**Type:** Positive
**Covers:** 7.4 → Audit logging of all lockout and unlock events
**Preconditions:** Lockout and unlock events produced; audit log access.
**Steps:**
1. Produce several lockout events (per account and per IP) and unlock events.
2. Locate all in the audit log.
3. Verify account, IP, threshold crossed, duration, and (for unlocks) the acting admin on each.
**Expected Result:** Every lockout and unlock event is audit-logged with the account, IP, threshold crossed, and duration; unlock events additionally record the acting admin and reason; no event is missing; the log supports the incident review; the log is exportable.
**Priority:** High

### TC-SA-01-07-051 — Counters reset after a successful login or after the window elapses
**Type:** Edge
**Covers:** 7.4 → Rule: lockout counters reset after a successful login or after the lockout window elapses, per the counter scope
**Preconditions:** A test account; a partial failure count; a successful login and a window elapse to test.
**Steps:**
1. Generate some (below-threshold) failures, then a successful login; verify the counter resets.
2. Generate some failures, trigger a lockout, let the window elapse; verify the counter resets.
3. Verify the reset matches the counter scope.
**Expected Result:** The failure counter resets after a successful login (the prior failures do not carry over); the counter also resets after the lockout window elapses; the reset behavior matches the counter scope (per account / per IP); the resets are consistent; the events are logged.
**Priority:** Medium

### TC-SA-01-07-052 — Progressive lockout increases duration up to a cap
**Type:** Edge
**Covers:** 7.4 → Rule: each successive lockout within a defined period increases the duration (e.g., doubles), up to a cap
**Preconditions:** Progressive lockout enabled; a test account; successive lockouts to trigger.
**Steps:**
1. Trigger the first lockout and record its duration.
2. Trigger successive lockouts within the defined period and record each duration.
3. Verify each is longer (e.g., doubles) up to the cap.
4. Verify the cap is the maximum (no duration exceeds it).
**Expected Result:** Each successive lockout within the period has a longer duration (e.g., doubling); the duration increases up to the configured cap; no lockout exceeds the cap (a persistent attacker faces longer locks but victims are not subject to unbounded durations); the progression and the cap are logged.
**Priority:** High

### TC-SA-01-07-053 — OTP failures do not lock the password path
**Type:** Negative
**Covers:** 7.4 → Rule: OTP failures are counted separately; a single bad or expired SMS does not lock the account's password path
**Preconditions:** A test account; a bad/expired SMS scenario.
**Steps:**
1. Generate a single bad or expired OTP (one failure).
2. Verify the account's password path is not locked.
3. Generate multiple OTP failures (below the password threshold) and verify the password path is still not locked.
**Expected Result:** A single bad/expired SMS does not lock the account's password path; multiple OTP failures (counted separately) do not contribute to the password lockout; the password path remains usable; the OTP failures are logged separately; the separation is exact.
**Priority:** High

### TC-SA-01-07-054 — Per-IP and per-account lockouts active independently
**Type:** Edge
**Covers:** 7.4 → Rule: per-IP lockouts protect against multi-account attacks; per-account protect a targeted account; both can be active independently
**Preconditions:** Both per-IP and per-account lockouts enabled; a multi-account attack from one IP and a targeted attack on one account.
**Steps:**
1. Generate a multi-account attack from one IP (many accounts, one IP) and verify the per-IP lockout.
2. Generate a targeted attack on one account (one account, many IPs) and verify the per-account lockout.
3. Verify the two lockouts are independent (each triggers on its own counter).
**Expected Result:** The per-IP lockout triggers on the multi-account attack (protecting against credential stuffing); the per-account lockout triggers on the targeted attack (protecting a single account); the two are independent (each has its own counter and threshold); both can be active at the same time; the lockouts are logged with their scope.
**Priority:** High

### TC-SA-01-07-055 — One-click unlock requires identity verification and a recorded reason
**Type:** Negative
**Covers:** 7.4 → Rule: one-click unlock always requires identity verification of the legitimate user and a recorded reason; audit-logged
**Preconditions:** A locked legitimate account; Super Admin access to the unlock.
**Steps:**
1. Attempt the one-click unlock without identity verification (verify it is blocked).
2. Attempt it with verification but without a reason (verify it is blocked).
3. Complete it with verification and a recorded reason.
4. Inspect the audit entry.
**Expected Result:** The unlock cannot proceed without identity verification of the legitimate user; it cannot proceed without a recorded reason; with both, it completes and clears the lockout; the audit entry records the acting admin, the user, the reason, and the timestamp; no unlock exists without verification and a reason.
**Priority:** Critical

### TC-SA-01-07-056 — All lockout/unlock events audit-logged with full fields
**Type:** Positive
**Covers:** 7.4 → Rule: all lockout and unlock events audit-logged with account, IP, threshold crossed, duration, and (for unlocks) the acting admin
**Preconditions:** Multiple lockout and unlock events; audit log access.
**Steps:**
1. Produce lockout events (per account and per IP) and unlock events.
2. Locate all in the audit log.
3. Verify account, IP, threshold crossed, duration, and (for unlocks) the acting admin on each.
**Expected Result:** Every lockout and unlock event is audit-logged with the account, IP, threshold crossed, and duration; unlock events additionally record the acting admin (and reason); no event lacks a required field; the log supports the incident summary (failure pattern, actions, outcome); the log is exportable.
**Priority:** High
