Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
rileyrainey
Product and Topic Expert
Product and Topic Expert



Identity integration is just as important as Data integration.


I am working on a longer series of articles describing end-to-end HCP mobile application development, but I wanted to take the opportunity to post a quick solution to a problem I have been researching for some time: How can you effectively capture and incorporate a user's identity in your Olingo OData web services?

Integrated Identity Management services are a cornerstone of the HANA Cloud Platform.  These services can take many forms.  You can use any SAML 2.0 Identity Provider (IdP) as your user base. The IdP can be SAP's Cloud Identity Tennant, a commercial Cloud identity provider such as Microsoft Azure.  It can also integrate with your own on-premise Identity Provider such as AD FS or Shibboleth.  HCP's IdP Trust configuration is fairly straightforward to set up if you are familiar with SAML and an integration can include passing custom Group membership information along with other user attributes into all HCP applications.

In this article, I will give you a quick glimpse into how you can access identity information in an Olingo web service that you might deploy in HCP.

Apache Olingo is a handy open source framework for developing RESTful OData web services. Olingo supports several modes for defining the data model of a web service. I find the JPA mode easy to work with -- Olingo's JPA API allows your choice of either defining a web service object model from scratch by defining your own JPA Java objects or by using Eclipse to automatically generate a JPA model from a set of source SQL tables.

Defining a data model, though, only addresses a portion of the practical challenge of creating a new web service.  Securing the web service is an essential element too, and that often requires integrating user identity tightly into the security implementation.

For example, let's say I wanted to create a web service supporting a messaging application. In order to secure the messages from prying eyes, I might leverage the user's identity to filter the messages down to only those messages the logged-in user is authorized to view. Commonly used OData training samples, such as ESPM or Northwind conveniently overlook such scenarios.

It turns out that it is relatively easy to add such intrinsic "relevance" filtering to an Olingo entity collection.  Let's look at how it works, but let's first elaborate a bit on our use case so that what we construct will make the most sense.

Here's a simple set of rules describing the elements and behavior of our application:

  1. A User can send a Message to one or more other Users.
  2. Messages are grouped into Conversations.
  3. Each Conversation has a list of Users that are members.
  4. A User may be a member of any number of Conversations.
  5. Only members of a Conversation are allowed to view that Conversation's Messages.
  6. A User can only view the Conversations that they are a member of.
  7. When A Message is created, the Message sender is assigned automatically by the web service. (In other words, a User cannot "send" a Message impersonating another User.)
  8. Users may create new Conversations on demand.
  9. A User may add an additional user to any Conversation they belong to.

This list of rules isn't exhaustive by any stretch, but it defines how we would like this web service to behave well enough that what follows should make sense.

Items one through four can be expressed by this entity relationship diagram:

Starting from this ERD, it is easy to use Olingo's JPA API to define Java objects that will manifest this structure in a persistent SQL database and simultaneously create corresponding OData Entity Collections.

Items five, six, and seven all express a need for the web service to use information about the logged-in user to either filter the data to what's authorized or to ensure information integrity.


So, how do we efficiently obtain user information in an Olingo JPA web service running in HCP?


The first step is to obtain the name of the authenticated user.  Olingo's JPA code saves the HttpServletRequest object corresponding to each inbound request in a way that fairly easy to retrieve:


import org.apache.olingo.odata2.jpa.processor.core.ODataJPAContextImpl;
import org.apache.olingo.odata2.api.processor.ODataContext;
import javax.servlet.http.HttpServletRequest;
    .
    .
    .
  ODataContext ctx = ODataJPAContextImpl.getContextInThreadLocal();
  HttpServletRequest r = (HttpServletRequest) ctx.getParameter(ODataContext.HTTP_SERVLET_REQUEST_OBJECT);
  String user = r.getRemoteUser();
  if (user != null) {
      MessageUser u = IdentityInteraction.verifyUser(r, em);
      logger.debug( "username from HttpServletRequest '"+u.getUsername()+"'" );
  }
  else {
      logger.error("Assertion error: There is no authenticated user defined in the HttpServletRequest -- " +
          "check your web.xml application configuration");
  }










That will return the username, but we'd like to be able to access other metadata about that user too -- what about their first name or last name? -- or their e-mail address?  HCP provides some helper classes to access those user attributes.



import com.sap.security.um.user.UnsupportedUserAttributeException;
import com.sap.security.um.user.User;
import com.sap.security.um.user.UserProvider;
import javax.naming.InitialContext;
.
.
.
InitialContext ctx;
  try {
      ctx = new InitialContext();
      UserProvider userProvider;
      userProvider = (UserProvider) ctx.lookup("java:comp/env/user/Provider");
      User user = null;
      if (request.getUserPrincipal() != null) {
          nameId = request.getUserPrincipal().getName();
          user = userProvider.getUser(nameId);
          if ( user != null) {
              try {
                  email = user.getAttribute("email");
              } catch (UnsupportedUserAttributeException e) {
                 // no error
              }
              try {
                  lastName = user.getAttribute("lastname");
              } catch (UnsupportedUserAttributeException e) {
                  // no error
              }
  try {
    firstName = user.getAttribute("firstname");
   } catch (UnsupportedUserAttributeException e) {
  // no error
    }
    }
  }
    } catch (NamingException e1) {
    logger.error("NamingException insde IntentityInteraction housekeeping: " + e1.getMessage());
    e1.printStackTrace();
    } catch (PersistenceException e2) {
  logger.error("PersistenceException insde IntentityInteraction housekeeping: " + e2.getMessage());
  e2.printStackTrace();
   }






In order to take advantage of this lookup, your application's web.xml file must include this resource reference in the web-app definition:


  <resource-ref>
    <res-ref-name>user/Provider</res-ref-name>
    <res-type>com.sap.security.um.user.UserProvider</res-type>
  </resource-ref>






Summary


The Java code shown here can be used to look up user information in any Olingo JPA web service running in HCP.  If you are running in a different container, it will likely have its own way to lookup user attributes.


I am working on a complete working sample based on these code snippets.  Until that's available, hopefully this will help anyone trying to solve this same problem in their own Olingo projects. In a following article, I'll show you exactly where to insert this code into your Olingo JPA web service and how to add extra relevance and security checks into each OData Entity Collection.

4 Comments