on 2019 Mar 11 10:45 PM
In the Cloud Application Programming Model (CAPM) Help there is a tantalising glimpse of how to control access to a service by role (in Using Single-Purposed Services😞
service AdminService @(requires:'admin')
I need to control access to services by Cloud Foundry role collection. Can I do that in the same way as above? Do I just put the name of the role collection in there (in place of 'admin')? Can I put a scope in there instead?
I would be very grateful if someone could share some documentation or examples that show what is supported. There doesn't seem to be anything like that in the documentation, even under Annotations.
Thanks!
cc christian.georgi daniel.hutzel
Help others by sharing your knowledge.
AnswerRequest clarification before answering.
Perfect answer philip.mugglestone
Documentation, and with it official release of the feature, is on its way (in new format)... → here's an (unofficial) sneak preview of the respective Guide as available internally:
---------
Restrict access to services by adding @requires or @restrict annotations to services, entities or functions as in this example:
service ReviewsService @(requires: 'identified-user'){
/*...*/
}
service CustomerService @(requires: 'authenticated-user'){
entity Orders @(restrict: [
{ grant: ['READ','WRITE'], to: 'admin' },
{ grant: 'READ', where: 'buyer = $user' },
]){/*...*/}
entity Approval @(restrict: [
{ grant: 'WRITE', where: '$user.level > 2' }
]){/*...*/}
}
The annotations are:
@restrict:
allows fine-grained control through an array of privileges given as records@requires:
allows specifying one or more user roles (as a single string or an array of strings) the current user must be assigned.Remark: Essentially @requires: is just a convenience shortcut, for example:
@requires: 'identified-user'
… is equivalent to:
@restrict: [{ grant:'*', to: 'identified-user' }]
… just more concise and more comprehensible.
… are granted within @restrict:
annotations and support these properties:
grant:
one or more operations (as a string or an array of strings)to:
(optional) one or more user roles the privilege is granted towhere:
(optional) a condition that further restricts accessWithin where:
conditions, use names prefixed with $
to refer to request attributes or attributes in the JWT token. Examples: $user
, $user.foo
or $foo
.
Within where:
conditions CQL syntax is allowed. Initially and
and or
is supported (no subselects).
It is possible to use where:
and to:
together in one grant:
clause.
References to entity elements in where:
conditions establish so-called instance-based authorizations. The filter conditions usually relate attributes of protected entities to user or request attributes.
… can be freely chosen and are local to the current application. For example, in the above sample, the role name admin
reflects the assumed role of bookshop administrators.
Note: While roles are actually mapped to OAuth scopes if OAuth is used, CDS-based Authorization deliberately refrains from using technical concepts in favour of user roles which are closer to the conceptual domain of business applications. This also results in much smaller JWT tokens.
The following are pre-defined pseudo roles:
authenticated-user
refers to users identified by loginidentified-user
is identified by weaker mechanisms such as cookiessystem-user
is for client systems calling in with unnamed, technical usersany
refers to all users including non-identified onesidentified-user
s is a superset of authenticated-user
s.any
is a superset of all others.
The service provider frameworks automatically enforce restrictions in generic handlers. They do so by evaluating the annotations in the CDS models and depending on the operations…
Alternatively, you can programmatically enforce restrictions in custom request handlers using this methods in you event handlers:
Following is the API you can use for implementing your enforcement (given here in Node.js APIs; swift has corresponding ones) — the same are used by the generic enforcement:
req.user
– sortcut for req.user.id
in queriesreq.user.id
– access the current user’s unique ID, an arbitrary stringreq.user.is(<role>)
– check whether the user has assigned the given rolereq.user.<attr>
– access user-related attributes from JWT tokensreq.reject(403, ...)
– reject a request due to missing authorizationsreq.query.where(...)
– to add a filter to the query’s where clauseNote: The function
req.user.is(<role>)
accepts role names introduced in the current application’s service models.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thanks so much Daniel, the documentation looks really good. Will this work with custom handlers (i.e. with the cds.persistence.skip annotation)? I note that the documentation you shared says:
Alternatively, you can programmatically enforce restrictions in custom request handlers
I'm using a Java app with custom handlers which call S/4HANA Cloud using the SDK. I'm thinking that the annotations in my-service.xml won't do anything in my case? Or perhaps the 'service-level' requires statement will still work? I note that the before- and after-operation hooks, for example, aren't called with custom handlers.
If that's the case do you have any references of how to implement the checks in Java rather than node?
Hi daniel.hutzel philip.mugglestone
Thank you very much for the sneak preview of this new functionality in Node.js handlers! I have been working with req.user.is(<role>) and req.user.<attr> and it is very handy!
When I was debugging a request handler, I came across the function
req.user.has( <roles>? )
and I tried to execute this function but always get error message: roles.some is not a function..
Could you please clarify what this function can be used for and how it is called?
I am also looking forward for official release of the new CAPM documentation. I hope this will happen soon.. 🙂
Best regards,
Ben
Hi Mike,
Try scope name but without the $XSAPPNAME. prefix.
For example, with these scopes (as defined in xs-security.json):
"scopes": [{
"name": "$XSAPPNAME.User",
"description": "User"
}, {
"name": "$XSAPPNAME.Admin",
"description": "Administrator"
}],
This should work:
service AdminService @(requires:'Admin')
service CatalogService @(requires:'User')
You'll still need to define role-templates and role collections etc. and assign the role collections to users just like in any Cloud Foundry app.
Hope that helps.
Philip
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi benkrencker ,
The "some" function is a javascript function on Array.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
When you use has, you have to provide an array of scope values. If there is one scope assigned to the current user, has returns true.
req.user.has(["user","admin"]) === req.user.is("user") || req.user.is("admin")
I've added some code to test this to my CAPM authorisation example.
https://github.com/jowavp/SAP-CAPM-Nodejs-Authorisation-example/blob/master/srv/service.js
kr,
Joachim
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
52 | |
6 | |
5 | |
5 | |
5 | |
4 | |
4 | |
3 | |
3 | |
3 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.