Skip to Content

CAP Security

Pre-requisites

Follow the tutorial on how to create the SCP-Business Application (CAP)

The MTA project should have at least three main modules: db, srv & app.

Java run-time and Maven installed on your PC

Valid SAP Marketplace User with software download privileges

AppRouter

After you finished adding the html5 module as explained in the CAP documentation, there is no need to create a NodeJS module and make it load the AppRouter. If you inspect the html5 module it contains the package.json with all node dependencies and the start script to load the application router. This application will be responsible for users authentication in Cloud Foundry. From there you can “share” the JWT token to the service module.

At this point you should have a working application with an UI front-end such as this one:

This application will never request authentication to you, since it is not really deployed onto CF. Instead WebIDE runs it in test mode, by simply pointing the browser to a resource path from your workspace. The only application you should have on your org/space should be another test application which corresponds to your OData service – a Java Spring based application.

In order to test the authentication from your “embedded” AppRouter, you need to:

  • Clean up your trial account (remove any running applications, routes or any other resources). WebIDE will throw a deployment error, but it will not really tell you what’s wrong. If this is the first time you’ve tried CAP or WebIDE full-stack and your CF space is empty, then just hit stop on your service and WebIDE will remove the temporary application instance.
  • Build the whole project – which will generate an mtar file on your workspace folder named “mta_archives”. By right-clicking on it you can select deploy.

Deployment should take a a fairly long time – but you need to keep in mind that it is doing a lot of work behind the scenes for your. For instance: you no longer need to manually create an xsuaa instance with xs-security.json file, etc.

After deploying the application you should see the following apps and service instances:

Test the app module by opening its URL and see that it now requests user authentication:

CF authentication uses the e-mail address to authenticate. So, don’t use the P-User here. Once you open your application the OData service should be called without any issues.

However, the request for authentication here has just protected the front-end UI, not the service.

OData Service Security

As explained before the ‘srv’ module is a Java Spring application. Therefore we need to use the Spring Security configuration to block access when the OData service is being called outside of the the UI and have it accept the JWT from the UI as authentication method.

This can be accomplished in Spring by two means. Either you create a new configuration class with the security beans in place – Spring will take care of making it secure via dependency injection. Or you could configure it via xml artifacts. Here I’ve chosen the xml artifacts, since CAP left a “template” configuration that is almost ready to be used.

To enable Spring Security in our Java application do the following:

  • Open the file “web.xml” that is under the path /srv/src/main/webapp/WEB-INF. The file already comes with the required security filters in place, but you have to enable it by un-commenting last part. Remember to remove only the double dashes and the exclamation mark from the listener and filter mapping tags.

 

  • Update the xsd definitions in the file “security-spring.xml” (under the same folder) to reflect the newer Spring version used by CAP:

Add the following tags to remove CSRF protection (we wont need this protection since all communication will be done internally between the router app and the service app).

Place the following piece of xml right above the tag <sec:intercept-url….> used to intercept the odata/v2 path

<sec:csrf disabled="true"/>

The spring-security.xml file should now look like this:

Once this is done, the java application “coding” is ready intercept all calls to the odata/v2 path and will only allow it to pass when there is a JWT present.

However, the way it configured still won’t work because we haven’t instructed the application where to fetch the required libraries.

XS Java Libraries

WebIDE works with Maven Central by default. Thus, it fetches all required libraries from it. If any dependency is not available in Maven Central, WebIDE won’t be able to build the application.

In order to use the Filter Chain you configured via web.xml, your Java project needs to reference the the HANA 2.0 Java Libraries – more specifically the ones related to security. Since they aren’t available in Maven Central nor at any other known public maven repositories we need “trick” the maven process and make it “think” it has access to the required libraries via the project’s folder structure.

Java Libraries in your PC

We first need to install the libraries in your local maven repository. The following procedure will copy pre-compiled libraries in the expected folder structure under your “.m2” folder.

To do it, download the XS JAVA library from the SAP Marketplace or search it from the following path:

Unzip the XS_JAVA_2-<version>.ZIP to a folder of your choice. Open a terminal window or command prompt and run the following commands:

#]> cd XS_JAVA_2_<version>
#]> mvn clean install

The result should be something very much like this:

Create zip file to import

Now, this is something your need to do on your own. Create two sets of zip files containing the following files/folders (which you should retrieve from your .m2 directory):

NOTE: The paths are important here. So, use your zip utility software to store the paths (the first line where we can clearly see “\com\sap\xs2\secutiry\…” and “\com\sap\security”. Under the shown folders you should see another folder for the library version and then the JAR file, POM, etc will be found as well. I am not going to document this procedure but I am confident that you are able to do it on your own.

XS Java Libraries in WebIDE

On WebIDE, create a new folder under your MTA project named “libs”. At this point you should have the following folder structure:

Right-click the libs folder and import the libs.zip file you created earlier:

Do the same for the libs2.zip – this time the import screen will tell you that the folder isn’t empty. Hit “import” as it is not going to overwrite the imported files from the previous step.

At this point the folder structure should look like this in WebIDE:

Let’s take the java-container-security folder as an example of what you should have in terms of files.

Update you POM.xml file to use of XS Java Libraries

Under the OData Module (folder “srv”) you will find the file pom.xml. Open open a new line between the </properties> tag and the <build> tag:

Then paste the following lines

<repositories>
  <repository>
    <id>my-local-repo</id>
    <url>file://${project.basedir}/../libs</url>
  </repository>
</repositories>
  <dependencies>
		<!-- Authentication and Authorization imports with Spring Security -->
		<dependency>
			<groupId>com.sap.xs2.security</groupId>
			<artifactId>security-commons</artifactId>
			<version>0.28.6</version>
		</dependency>
		<dependency>
		  <groupId>com.sap.xs2.security</groupId>
		  <artifactId>java-container-security</artifactId> <!-- 1 -->
		  <version>0.28.6</version>
		</dependency>
		<dependency>
		  <groupId>com.sap.xs2.security</groupId>
		  <artifactId>java-container-security-api</artifactId>
		  <version>0.28.6</version>
		</dependency>
		<dependency>
		  <groupId>org.springframework.security</groupId>
		  <artifactId>spring-security-jwt</artifactId>
		  <version>1.0.9.RELEASE</version>
		</dependency>
		<dependency>
		  <groupId>org.springframework.security.oauth</groupId>
		  <artifactId>spring-security-oauth2</artifactId>
		  <version>2.3.3.RELEASE</version>
		</dependency>
		<dependency>
		  <groupId>com.sap.security.nw.sso.linuxx86_64.opt</groupId>
		  <artifactId>sapjwt.linuxx86_64</artifactId>
		  <version>1.1.19</version>
		</dependency>  	
  </dependencies>

The file should look like this:

These lines will make the OData service module load the security libraries required for the JWT token and all other security libraries. Keep an eye on the “Problems View” in WebIDE. If your pom.xml is invalid there is a high change your ‘libs’ folder structure isn’t correctly set or you need to update the versions in the XML. This blog was written when version 0.28.6 was the latest available. But you could have downloaded a newer version by now. If you place the cursor over the red marker, it will show the libraries that the system didn’t find.

Checking the WAR file

Build the OData service project and check that the corresponding library jar files were in fact downloaded from your local ‘libs’ folder and stored in the generated war file. The best way to find out is to export the war file from the target folder:

When you open the downloaded WAR file you should see the following newly included ‘libs’ under WEB-INF\lib:

You could also check the the build console and search for these files instead of downloading the whole war file (which is quite huge and may take some time to download).

Checking the security in the OData service:

The security features of your srv module will not work by default as there is no xsuaa dependency for it yet. To make your OData service use the xsuaa service we need to add the following lines on our MTA descriptor (it is exactly the same as the one defined four our ui front-end):

- name: uaa_bookshop2
  properties:
    SAP_JWT_TRUST_ACL: '[{"clientid" : "*", "identityzone" : "*"}]'

Your MTA file should look like this:

Now, if you run the service module, you will get the following error:

This means your service is now protected.

Putting it all together

First we have to make the AppRouter call the OData service destination in an authenticated manner. Therefore, change the property authenticationType from none to xsuaa on the file xs-app.json (this file is at the root of your app’s folder):

Keep in mind that this file is only interpreted when it is running in CF. Remember that AppRouter is a NodeJS module (impossible to run it on Neo). If you try running this directly via WebIDE it is not going to share the authentication between apps, simply because WebIDE web application testing doesn’t deploy and run the AppRouter. It is intended to test the UI, not the security. Maybe the product team will change this in the future, but I don’t hold the answers to such questions.

In a trial instance you must stop the OData service and make sure your org/space is clean of deployed projects due to limited resources.

Build the MTA project again to include the all the changes in the mtar file and deploy it to your org/space.

Check that all applications are really running. Sometimes the db module refuses to start right after deployment has finished. If this happens, just start the db module manually.

Final testing

After deployment is done, you should end-up with the same three applications (db, srv, app):

And an instance of xsuaa service bound to app and srv. In addition to the instance of hanatrial bound to db and srv:

Open the app URL in incognito mode to test the authentication. The CF authentication kicks in and you will be able to load the books table. Notice that the OData service loaded without any errors:

To prove that the OData service will only work after it has been authenticated, you can open the OData end-point from the app application on a new incognito window. It should load the login screen instead.

However, if you try loading the srv URL directly on an incognito window, you should still see the following:

Roles, Scopes, Role Templates and Role Collections

CAP will create a Role Template named “Token_Exchange” with scope named “uaa.user”:

However, there is no “role’ and no “role collection” created. In fact, there is no need for them in this example. We are simply checking for authenticated users. The service isn’t blocking any of the scopes.

If you need a more granular control over the methods of the service then define additional authorization scopes in the AppRouter’s “xs-security.json”. Then perform checks in the odata service via “spring-security.xml” this way:

<sec:intercept-url pattern="/odata/v2/CatalogService/Books" access="#oauth2.hasScope('${xs.appname}.uaa.user')"  method="GET" />
<sec:intercept-url pattern="/odata/v2/CatalogService/Books"  access="#oauth2.hasScope('${xs.appname}.uaa.admin')" method="POST" />

Once you have the ‘app’ module deployed, the new scopes and templates will be available to create the required roles based on the templates you defined in the AppRouter.

Roles are assigned to Role Collections which in turn can be assigned to users. This is done via cockpit at the organization level (not the space). Create the Role Collection and assign Roles to it.

Then on the ‘Security’ open the ‘Trust’ menu and you will find your IdP link. Click on the link and assign a user to the Role Collection. Remember: users in CF are defined as e-mails.

Cheers!

Ivan Mirisola

To report this post you need to login first.

7 Comments

You must be Logged on to comment or reply to a post.

  1. Dominik Trefzer

    Thank you so much, Ivan, for this awesome tutorial! Great explanation of a crucial topic for cloud development!

    I finally get to the login screen after locking down my OData service. Currently I’m facing a weird issue that I can’t login on the login page

    https://authentication.eu10.hana.ondemand.com/login

    with my SAP ID user account. The credentials work just fine in the Cloud Platform Cockpit but not there. I opened a ticket to SAP and I hope it gets resolved soon. But at least I have some hope now to get this all working 🙂

    Cheers,

    Dominik

    (0) 
    1. Ivan Mirisola Post author

      Hi Dominik,

      Are you using your SAP ID user id or the e-mail address to login? CF instances will accept e-mail as login id and not P-Users, S-Users.

      If you are getting errors trying to login, maybe it would be a good idea trying to look into the http traces or the odata service logs to troubleshoot the issue.

      Best regards,
      Ivan

       

       

      (0) 
      1. Dominik Trefzer

        Hi Ivan,

        I used the e-mail address of my S-User account. Those credentials work fine in the cloud cockpit, but not in the xsuaa login. I get the same error messages when I try to open the XSUAA dashboard – can’t get past the login. It’s the same for the users of my colleagues so I guess something with our company CP account is borked here…

        I’ll post if it gets resolved and I can hopefully get the OData service login working.

        Kind regards,

        Dominik

        (1) 
  2. Tri Minh Le

    Thank you for sharing a wonderful blog.

    It just fits my needs.

    I have some questions:

    1. What does the parameter saml2=disabled mean (I see it in the network screenshot)?
    2. Do we really db module since I’m using S/4HANA Cloud and create some custom business objects? My srv module queries data from these custom business objects.
    3. For example, I’m able to secure the application. Then in my Java module, I want to get the user info like email or user id. Is it possible?

    I’m looking forward to your future blogs.

    Cheers,

    Tri

    (1) 
    1. Ivan Mirisola Post author

      Hi Tri,

      1. saml2=disabled means we don’t want to use saml 2.0 authentication procedure for our odata service. We actually really on JWT authentication here. Thus, only the Bearer Schema as authentication should suffice.
      2. This blog is related to the Cloud Platform Business Application (CAP). It does rely on the SDK for Services Development APIs – which are used to create extensions for S/4HANA. However, when you create an MTA based on CAP it does create the db module automatically. You may have to adapt your CAP project to refer to a different data source (other the the db module).
      3. Please take a look the this blog at session entitled “Troubleshooting Json Web Tokens” as it explains how you can read the JWT token to retrieve the e-mail. You could also get scopes from the user by invoking the SecurityContext assessor class.
      UserInfo userInfo = SecurityContext.getUserInfo(); 
      String name = userInfo.getLogonName(); 
      String email = userInfo.getEmail();
      String[] attribute = userInfo.getAttribute("my attribute");
      boolean hasDeleteScope = userInfo.hasLocalScope("Delete");
      

      Best resources to look for such information is the HANA Developer Guide.

      Best regards,
      Ivan

      (1) 
      1. Tri Minh Le

        Hi Ivan,

        Thank you for your information. It’s very helpful.

        Just one more question is regarding user info attribute, where can I find documents about it? I mean what attributes it contains.

        I did have a look at your link to HANA developer guide but I can’t find it anywhere.

        Regards,

        Tri

        (0) 

Leave a Reply