Forum Stats

  • 3,840,090 Users
  • 2,262,565 Discussions
  • 7,901,149 Comments

Discussions

Identity propagation from Java Cloud Service - SaaS Extension to Documents Cloud Service using SAML

Aparna Gaonkar-Oracle
Aparna Gaonkar-Oracle Member Posts: 7
edited Aug 4, 2016 11:50PM in Developer Solutions

To use multiple cloud services as one cohesive unit, it is often needed to propagate identity from one PaaS cloud service to another. For e.g. an application in Java Cloud Service or Mobile Cloud Service that needs to access some RESTful resources in another PaaS service like Documents Cloud Service.    In this blog post I will discuss one such case i.e. identity propagation from Java Cloud Service – SaaS Extension (JCS-SX) to Documents Cloud Service (DCS).  The assumption is that both these services are hosted in the same identity domain.

First a note about the two common services that make identity propagation possible.

  • In Oracle Cloud, the Shared Identity Management (SIM) provides Identity Management, Single Sign On, Federation and other services out-of-the-box to Oracle PaaS services.  The SIM also acts as the OAuth Server to handle OAuth flows.

  • Oracle Web Services Manager (OWSM) provides a policy management and enforcement framework to secure and manage Web Services at design time via code or post deployment from administration consoles.  OWSM can be used to manage both REST and SOAP web services.

Our Use Case is based on an application hosted in JCS-SX which has a Servlet (or REST service) accessing a Documents Cloud Service REST API.  Here we have two resources:

  • Resource A in JCS-SX – protected by security-constraint in web.xml, which requires the user to login in order to access the resource.
  • Resource B in Documents Cloud Service – protected by the internal Documents Cloud Service OWSM.

The sequence of events that occur when the user requests the URL of the servlet are as follows:

  1. User tries to access Resource A (servlet URL from the browser)
  2. Since Resource is protected, user asked to login
  3. Post Login, User is redirected to the Resource A
  4. Resource A makes a call to Resource B.  Resource B should work in the context of logged in user. E.g. return folders that belong to the logged in user.

Here the JCS-SX Servlet acts as the REST client w.r.t the Documents Cloud Service.

architecture.png

The Document Cloud Service REST APIs are protected by the oracle/multi_token_over_ssl_rest_service_policy (details here) which enforces one of the following authentication policies, based on the token sent by the client:

  • HTTP Basic over SSL—Extracts username and password credentials from the HTTP header.
  • SAML 2.0 Bearer token in the HTTP header over SSL—Extracts SAML 2.0 Bearer assertion in the HTTP header.
  • HTTP OAM security (non-SSL)—Verifies that the OAM agent has authenticated user and establishes identity.
  • JWT token in the HTTP header over SSL—Extracts username from the JWT token in the HTTP header

So we could use potentially any of these methods to propagate identity from JCS-SX to DCS (However the first one - HTTP Basic over SSL would require putting in credentials again and is not really a true identity propagation method)  Here I will be using the SAML 2.0 Bearer token as the identity propagation method.

The steps to achieve this are as follows:

Step 1 – Create JCS-SX Servlet that calls a Documents Cloud Service REST API

Note – replace example.com with the URL for your Documents Cloud Service instance and the API endpoint as any DCS REST API that you need.  The full list of DCS REST APIs are published here.  In this example I have used the ENDPOINT_URL to point to a special folder called "self".

import com.sun.jersey.api.client.Client;import com.sun.jersey.api.client.ClientResponse;import com.sun.jersey.api.client.WebResource;import com.sun.jersey.api.client.config.ClientConfig;import com.sun.jersey.api.client.config.DefaultClientConfig;import com.sun.jersey.client.urlconnection.HTTPSProperties;import java.io.IOException;import java.io.PrintWriter;import java.security.NoSuchAlgorithmException;import java.util.Map;import javax.net.ssl.SSLContext;import javax.servlet.ServletConfig;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;public class DocsServlet extends HttpServlet { private static final String ENDPOINT_URL =        "https://example.com/documents/api/1.1/folders/self"; public void init(ServletConfig config) throws ServletException {        super.init(config); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {        response.setContentType("text/html;charset=UTF-8");        PrintWriter out = response.getWriter(); out.println("<html><body>");        try {            ClientConfig cc = new DefaultClientConfig();            Map<String, Object> properties = cc.getProperties();            SSLContext sslContext = null;            try {                sslContext = SSLContext.getInstance("TLS");            } catch (NoSuchAlgorithmException e) {                out.println("Exception in setting sslContext is : " + e + " exc messaage is: " + e.getMessage());            } properties.put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(null, sslContext));            Client client = Client.create(cc);            WebResource resource = client.resource(ENDPOINT_URL);            ClientResponse clientResponse = resource.get(ClientResponse.class);            String output = clientResponse.getEntity(String.class); out.println("\n\n<br>----------Output starts--------------"); out.println("<br/>Response body from Service is : " + output);            out.println("<br/>HTTP Status : " + clientResponse.getStatus()); out.println("<br/>--------------Output ends----------------");        } catch (Exception e) {            out.println("Exception is : " + e + " exc messaage is: " + e.getMessage());        } out.println("</body></html>"); } public void destroy() { }       super.destroy(); }

I am using maven as a dependency management tool.  Here is the pom.xml

<?xml version="1.0" encoding="UTF-8" ?><project xmlns="http://maven.apache.org/POM/4.0.0"> <modelVersion>4.0.0</modelVersion> <groupId>com.oracle.idprop</groupId> <artifactId>idproptodocs</artifactId> <version>1.0-SNAPSHOT</version> <description>Project for idproptodocs</description> <packaging>war</packaging> <properties> <jersey-version>1.19.1</jersey-version> </properties> <build>        <resources>            <resource> <directory>${basedir}</directory>                <includes> <include>*</include>                </includes>            </resource>            <resource> <directory>src/main/resources/</directory>                <includes> <include>*</include>                </includes>            </resource>        </resources> </build> <dependencies>        <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId>            <version>2.5</version>            <type>jar</type>            <scope>provided</scope>        </dependency>        <dependency>            <groupId>org.json</groupId> <artifactId>json</artifactId> <version>20140107</version>            <type>jar</type>            <scope>compile</scope>        </dependency>        <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-json</artifactId> <version>${jersey-version}</version>            <type>jar</type>            <scope>compile</scope>            <exclusions>                <exclusion> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId>                </exclusion>                <exclusion> <groupId>com.sun.xml.bind</groupId>                    <artifactId>jaxb-api</artifactId>                </exclusion>            </exclusions>        </dependency>        <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-core</artifactId>            <version>${jersey-version}</version>            <type>jar</type>            <scope>compile</scope>        </dependency>        <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-servlet</artifactId> <version>${jersey-version}</version>            <type>jar</type>            <scope>compile</scope>        </dependency>        <dependency> <groupId>com.sun.jersey</groupId> <artifactId>jersey-bundle</artifactId> <version>${jersey-version}</version>            <type>jar</type>            <scope>compile</scope>            <exclusions>                <exclusion> <groupId>com.sun.xml.bind</groupId>                    <artifactId>jaxb-impl</artifactId>                </exclusion>                <exclusion> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-api</artifactId>                </exclusion>            </exclusions>        </dependency>        <dependency> <groupId>com.sun.jersey.contribs</groupId> <artifactId>jersey-multipart</artifactId> <version>${jersey-version}</version>            <type>jar</type>            <scope>compile</scope>        </dependency> </dependencies></project>

And the following is the web.xml entry.  We use a security-constraint and a CLIENT-CERT login-config element in the web.xml to protect the DocsServlet and make it a Tenant Restricted Page

<?xml version = '1.0' encoding = 'windows-1252'?><web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"         version="2.5"> <servlet> <servlet-name>Servlet1</servlet-name> <servlet-class>com.oracle.rest.DocsServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>Servlet1</servlet-name> <url-pattern>/saml</url-pattern> </servlet-mapping> <security-constraint> <display-name>name</display-name>        <web-resource-collection> <web-resource-name>name</web-resource-name> <url-pattern>/*</url-pattern>        </web-resource-collection> </security-constraint> <login-config> <auth-method>CLIENT-CERT</auth-method> <realm-name>default</realm-name> </login-config></web-app>

And at last - the weblogic.xml entry

<?xml version = '1.0' encoding = 'windows-1252'?><weblogic-web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.7/weblogic-web-app.xsd" xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"> <container-descriptor> <prefer-web-inf-classes>true</prefer-web-inf-classes> </container-descriptor> <context-root>idproptodocs</context-root></weblogic-web-app>

Create the WAR for the application and deploy to JCS-SX via the console.  For details on how to deploy to JCS-SX via the Java console, please consult the documentation

Post deployment, access the Servlet URL from the browser.  It will first prompt for the user to login.

cloud_sign_in.png

On successful login, it should navigate to the Servlet page, which should give output as below:

output1.png

The 401 indicates that the Document Cloud Service API couldn’t authorize the request made by the JCS-SX Servlet.

Step 2 – Import required libraries for OWSM

The wsm-policy-core and wsm-rest-lib jars are needed for compilation purposes in the IDE.  You can find these jars from the 11g local installation of Weblogic in the following location

MW_HOME/oracle_common/modules/oracle.wsm.common_11.1.1/wsm-policy-core.jar

MW_HOME/oracle_common/modules/oracle.wsm.common_11.1.1/wsm-rest-lib.war

Copy these to a specific folder in your local filesystem for use with Maven and then add them in your pom.xml with scope as system.  I have copied the files to C:\temp in my example.  These are referred by Maven only during compilation and are not packaged.

        <!-- owsm -->        <dependency>            <groupId>com.oracle.wsm</groupId> <artifactId>wsm-rest-lib</artifactId>            <version>11.1</version>            <type>jar</type>            <scope>system</scope> <systemPath>C:\temp\wsm-rest-lib.jar</systemPath>        </dependency>        <dependency>            <groupId>com.oracle.wsm</groupId> <artifactId>wsm-policy-core</artifactId>            <version>11.1</version>            <type>jar</type>            <scope>system</scope> <systemPath>C:\temp\wsm-policy-core.jar</systemPath>        </dependency>        <!-- owsm -->

The following two libraries are also needed to be deployed to the JCS-SX instance as libraries if they don’t exist there.  The steps for deploying a shared library are given here.

MW_HOME/oracle_common/modules/oracle.webservices_11.1.1/wls-rest-client.war

MW_HOME/oracle_common/modules/oracle.wsm.common_11.1.1/wsm-rest-lib.war

libraries.png

Step 3 – Attach required policy to the REST Client

Add properties to the ClientConfig to use the respective client side OWSM policies.  Here we would attach a SAML based client policy.  This means that the OWSM would intercept the REST request, inspect the authenticated subject within JCS-SX, generate a SAML based token from the user assertion, and insert the SAML token as a Bearer token as an Authorization header in the REST request.

For this we would use the “oracle/http_saml20_token_bearer_over_ssl_client_policy”.  The following code illustrates this:

          

 //Adding the policy - starts            PolicyReferenceFeature[] samlfeature = new PolicyReferenceFeature[] {                new PolicyReferenceFeature("oracle/http_saml20_token_bearer_over_ssl_client_policy",                                           new PropertyFeature[] { })            }; properties.put(AbstractPolicyFeature.ABSTRACT_POLICY_FEATURE, new PolicySetFeature(samlfeature));            //Adding the policy - finishes                        Client client = Client.create(cc);

To intercept the REST requests and generate and insert the SAML token, attach the OWSM provided out-of-the-box filter for REST Clients called RestClientFilter to the JAX RS Client.

           //Adding the client filter - starts ClientFilter filter = new RESTClientFilter();                 client.addFilter(filter);//Adding the client filter - finishes

Modify the weblogic.xml to refer to the deployed libraries as below

<?xml version = '1.0' encoding = 'windows-1252'?><weblogic-web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app http://xmlns.oracle.com/weblogic/weblogic-web-app/1.7/weblogic-web-app.xsd"                  xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app">    <container-descriptor> <prefer-web-inf-classes>true</prefer-web-inf-classes>    </container-descriptor> <context-root>idproptodocs</context-root>    <library-ref>        <library-name>wsm-rest-lib</library-name>    </library-ref>    <library-ref> <library-name>wls-rest-client</library-name>    </library-ref></weblogic-web-app>

Generate the war file and re-deploy to JCS-SX.

Now re-run the Servlet - You will see the output as below which indicates that the identity of the logged in user was propagated to the DCS REST API.

output2.png

The output is actually the JSON response of GET request issued to the ENDPOINT_URL of Documents Cloud Service instance - which is the folder information of the "self" folder.

The views expressed in this post are my own and do not necessarily reflect the views of Oracle.

Tagged: