Lesson 3 of 5
Enforcing authorization in CAP
10 min
Listen to this lesson
The fleet app is built with CAP, which enforces authorization declaratively: annotations in the model say who may do what, and CAP checks them on every request before custom handlers run. That puts the security rules where reviewers and testers can read them, next to the services they protect, instead of scattered through code.
Authentication strategies
CAP chooses an authentication strategy per profile. In development, the default is mocked authentication: basic authentication with predefined mock users such as alice (with the admin role) and bob, plus any users you configure under cds.requires.auth.users with roles and attributes. CAP's documentation warns that mocked authentication is not suitable for production. In production, jwt or xsuaa uses tokens issued by SAP Authorization and Trust Management, and ias uses SAP Cloud Identity Services. An anonymous request to a protected service gets 401.
@requires and @restrict
@requires names the roles (or pseudo-roles) needed to use a service, entity or action at all. @restrict lists privileges, each with grant (events such as READ, CREATE, UPDATE, DELETE, WRITE, action names or *), to (roles) and an optional where condition. CAP's documentation spells out how they combine: within one privilege all properties must be met; between privileges on the same resource at least one must pass; and across levels (service, entity, action) all must pass. Pseudo-roles include authenticated-user, any (the default for to, including anonymous users) and system-user for technical callers.
The fleet app's rules, in srv/fleet-auth.cds:annotate FleetService with @(requires: ['FleetAgent', 'FleetSupervisor']);annotate FleetService.Inquiries with @(restrict: [ { grant: ['READ', 'CREATE', 'UPDATE'], to: 'FleetAgent' }, { grant: '*', to: 'FleetSupervisor' } ]);annotate FleetService.closeInquiry with @(requires: 'FleetSupervisor');annotate CustomerService.Inquiries with @(restrict: [ { grant: 'READ', to: 'FleetCustomer', where: 'customer = $user.customer' } ]);
- Authenticated?No → 401 Unauthorized
- Service @requires met?No → request rejected (403)
- Entity or action privilege matches event and role?No → rejected (403; a single-entity READ answers 404)
- where conditionREAD results filtered to matching rows; UPDATE or DELETE of non-matching rows rejected
- Handlers and generic providerOnly now do custom handlers and the database run
Instance-based authorization
The where clause makes rules depend on the data. $user.customer refers to the user's customer attribute, which CAP treats as a list: the condition passes if any value matches, and an empty or missing attribute denies access. On READ, the condition filters results, so a Great Lakes Fleet Services contact sees only their own inquiries. CAP's documentation notes a detail testers must know: when a READ of a single entity fails the check, CAP answers 404 Not Found rather than 403, so as not to disclose that the record exists; failed UPDATE and DELETE on single entities answer 403.
From CDS roles to xs-security.json
cds add xsuaa adds the XSUAA service to the project and generates xs-security.json from the model: for every role in the CDS model, one scope and one role template with exactly that name, and one attribute for every user attribute used in conditions. cds compile srv --to xsuaa regenerates it after model changes. CAP's documentation warns that if you edit the file by hand, scope names must still exactly match the role names in CDS, because those names are checked at runtime; CAP strips the $XSAPPNAME prefix when it maps scopes to roles.
| User | GET FleetService Inquiries | POST closeInquiry | GET CustomerService Inquiries |
|---|---|---|---|
| Anonymous | 401 | 401 | 401 |
| Fleet agent | 200, all inquiries | 403 | 403 |
| Fleet supervisor | 200, all inquiries | 200 | 403 |
| Great Lakes Fleet Services contact | 403 | 403 | 200, only own customer's inquiries |
What would you do?
To make closing inquiries "more secure", a developer hides the Close button in the Fiori UI for agents but leaves closeInquiry without any annotation. What should the reviewer say?
- Mocked authentication is for development; production uses jwt, xsuaa or ias, and anonymous calls get 401.
- @requires gates access; @restrict grants events to roles with optional where conditions.
- Privileges on one resource combine with OR; checks across service, entity and action combine with AND.
- where with $user attributes filters reads; a failed single-entity READ answers 404, failed UPDATE or DELETE 403.
- cds add xsuaa generates one scope and role template per CDS role; names must match exactly.
Check your understanding · not graded
Lesson check
1. What does an anonymous request to a protected CAP service return?
2. A fleet customer contact reads one inquiry of another customer by its key. What does CAP return?
3. Which pseudo-role includes anonymous users and is the default for to?
4. What does cds add xsuaa generate for each role used in the CDS model?