Skip to Content

More info on CDS 'requires' statement (Cloud Application Programming Model CAPM)

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

Add comment
10|10000 characters needed characters exceeded

  • Follow
  • Get RSS Feed

2 Answers

  • Apr 25 at 05:15 PM

    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

    Add comment
    10|10000 characters needed characters exceeded

  • May 02 at 10:00 PM

    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:

    ---------

    Restrictions

    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.

    Privileges

    … 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 to
    • where: (optional) a condition that further restricts access

    Within 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.

    User Roles

    … 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.

    Pseudo roles

    The following are pre-defined pseudo roles:

    • authenticated-user refers to users identified by login
    • identified-user is identified by weaker mechanisms such as cookies
    • system-user is for client systems calling in with unnamed, technical users
    • any refers to all users including non-identified ones

    identified-users is a superset of authenticated-users.
    any is a superset of all others.

    Enforcement

    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…

    • rejecting incoming requests if the conditions are not met in case of static authorizations
    • adding corresponding filters to the queries’s where clauses in case of read queries and instance-based authorizations

    Alternatively, you can programmatically enforce restrictions in custom request handlers using this methods in you event handlers:

    Enforcement API

    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 queries
    • req.user.id – access the current user’s unique ID, an arbitrary string
    • req.user.is(<role>) – check whether the user has assigned the given role
    • req.user.<attr> – access user-related attributes from JWT tokens
    • req.reject(403, ...) – reject a request due to missing authorizations
    • req.query.where(...) – to add a filter to the query’s where clause

    Note: The function req.user.is(<role>) accepts role names introduced in the current application’s service models.

    Add comment
    10|10000 characters needed characters exceeded

    • 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?