Lesson 5 of 5
Testing authorization
10 min
Listen to this lesson
Authorization defects are among the most expensive defects to find in production, and the easiest to miss in test, because testers usually work with generous test users and only check that things work. A complete authorization test proves two things: that each role can do what it needs (positive tests) and that it cannot do anything else (negative tests). Priya's rule for the fleet app: every row of the expected-results table from lesson 3 becomes at least one automated test.
Build the test matrix
List the roles down one side and the operations across the top: each entity's READ, CREATE, UPDATE and DELETE, each action, and each service. Add anonymous access and a user with no role at all. For instance-based rules, add a column per data owner: Great Lakes Fleet Services' contact reading their own inquiries, and the same contact reading another customer's. Fill in the expected result for each cell, including the exact HTTP status (401, 403, 404 or 200) and, for reads, which rows should come back.
Test locally with mocked users
Most of the matrix can run on every build with CAP's mocked authentication. Contoso configures mock users for tests under cds.requires.auth.users: agent with role FleetAgent, supervisor with FleetSupervisor, glfs with FleetCustomer and attribute customer set to Great Lakes Fleet Services' business partner number, and other with FleetCustomer and a different customer. CAP's defaults also allow logins by other user names (the setting "*": true); Contoso overrides it with "*": false in tests so that a typo in a user name cannot silently produce a passing test. Tests then call the service as each user, for example with cds.test and { auth: { username: 'agent' } }, and assert the status and rows.
Test in the deployed subaccount
Local tests prove the annotations; they cannot prove the trust configuration, the security descriptor deployed to XSUAA, the role collections or the group mappings. In cmc-ext-test, the team runs a smaller set with real test identities from the identity provider, one per role, each in the right group: sign in, perform the allowed operations, attempt one forbidden operation through the API, and check the result. They also run two lifecycle tests: remove a test identity from its group and confirm access ends at the next sign-in, and add a new identity to a group and confirm access starts without anyone touching SAP BTP.
| Test | Level | Expected result |
|---|---|---|
| Anonymous GET of FleetService Inquiries | Local and deployed | 401 |
| Agent calls closeInquiry through the API | Local and deployed | 403; inquiry unchanged |
| Supervisor calls closeInquiry with a resolution | Local and deployed | 200; status Closed, closedBy is the supervisor |
| glfs reads CustomerService Inquiries | Local | 200; only inquiries with Great Lakes Fleet Services' customer number |
| glfs reads another customer's inquiry by key | Local | 404 |
| Test identity removed from BTP_FLEET_AGENTS | Deployed (cmc-ext-test) | No access after next sign-in |
Extract from the fleet app's authorization test set.
- Every buildLocal authorization suiteFull matrix with configured mock users; unlisted logins disabled
- Every deployment to cmc-ext-testDeployed smoke setOne allowed and one forbidden API call per role with real test identities
- Each test cycleLifecycle testsGroup joiner and leaver checks; assignment export compared with the agreed table
- Before go-liveProduction reviewMappings, administrator holders and absence of test identities in cmc-ext-prod
- Quarterly after go-liveAccess reviewDirect assignments and powerful collections reviewed with the process owner
Treat authorization evidence like any other test evidence. For each run, record the identity, its role collections at the time, the operation, the expected and actual status, and the build or deployment tested. When a defect is found, add the failing case to the local suite before fixing it, so the same hole cannot reopen in a later release. And when the CDS roles change, regenerate the security descriptor, update the expected-results table and rerun both the local and the deployed sets: a role renamed in the model but not in the subaccount's role collections fails only in the deployed environment.
What would you do?
All authorization tests passed locally. In cmc-ext-test, supervisors get 403 on closeInquiry, although their test identities are in BTP_FLEET_SUPERVISORS. Mocked users with role FleetSupervisor work. What should the team check first?
- Prove both sides: each role can do its job, and cannot do anything else.
- Build a matrix of roles, operations and data owners with exact expected statuses (401, 403, 404, 200).
- Run most tests locally with configured mock users; disable unlisted logins in tests.
- Run a smaller set in the test subaccount with real identities, including group joiner and leaver tests.
- Never use administrator users for functional or authorization evidence.
Check your understanding · not graded
Lesson check
1. What does a negative authorization test prove?
2. Why set "*": false for mocked users in tests?
3. Which defect can only deployed tests in cmc-ext-test reveal?
4. Which test is most often skipped and most important?