By Changhai Ke, Nicolas Peulvast, & Emi Nakamura
 
By default, after you deploy a decision service to Rule Execution Server, anyone can invoke it without authentication or authorization. You can, however, secure the decision service with basic authentication so that anyone calling the decision service must first authenticate themselves.
This article shows you how to add basic authentication to your decision services with WebSphere Liberty as your application server.

Decision service URL patterns

You must be familiar with three runtime URL patterns that are used in Rule Execution Server:

  • REST pattern: https://<host>:<port>/<AppContextRoot>/DecisionService/rest/<rs_path>
  • SOAP pattern: https://<host>:<port>/<AppContextRoot>/DecisionService/ws/<rs_path>
  • TEST pattern: https://<host>:<port>/<AppContextRoot>/DecisionService/run.jsp?<TestParams>

where

  • /rest/ represents REST API
  • /ws/ represents SOAP API
  • /run.jsp? is used for testing rulesets in the Rule Execution Server console.

The test console is accessible through the Ruleset View, click Retrieve HTDS Description File, select REST, and then click Test.  This feature is not available for SOAP API.
The following examples show how these URLs look like when these URL patterns are developed:

  • https://app.example.com:9443/odm/test/DecisionService/rest/loans/1.0/miniloan/1.0
  • https://app.example.com:9443/odm/test/DecisionService/ws/loans/1.0/miniloan/1.0
  • https://app.example.com:9443/odm/test/DecisionService/run.jsp?path=/loans/1.0/miniloan/1.0&trace=false&type=WADL&kind=native

Protecting runtime decision services with basic authentication is achieved based on these URL patterns.
The following example shows a complete URL format with a version:

  • https://<host>:<port>/<AppContextRoot>/DecisionService/rest/v1/<rs_path>

Table 1: Items in the URLs:

Item Name Description
<host>:<port> Hostname and port number You can also configure the server to remove the port number (use a default port number).
Example: app.example.com:9443
<AppContextRoot> Application context root. You can specify one to provide a clearer path. It can be the product name, such as odm, followed by the environment name, such as test.
Example: /odm/test
DecisionService IBM ODM decision service DecisionService is always part of the URL.
rest, ws, or run.jsp Other fixed parts for REST, SOAP, and testing rulesets URLs Followed by a ruleset path, appended in the path or provided as a parameter.
v1 API version Optional.
By default, the latest version of the API is invoked (currently v1). The URLs invoke v2 when a new version v2 is available.
Note: The URL examples above do not include the API version.
<rs_path> Ruleset path

Protecting the test user interface (run.jsp servlet)

Strictly speaking, the TEST pattern is not a runtime API because the servlet is accessible only when you use the testing rulesets feature for interactive testing in the Rule Execution Server console.
Note: The user must sign in to the Rule Execution Server console to access the test panel for REST.
By default, there is no protection for the user interface, therefore you should explicitly grant the access to some roles. You can protect it by adding the <security-constraint> block in the web.xml deployment descriptor file. You must grant access to all three default roles (resAdministrators, resMonitors, and resDeployers) that are predefined in Rule Execution Server.
See the following <security-constraint> block in the web.xml deployment descriptor file:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Decision Service Testing UI</web-resource-name>
        <url-pattern>/run.jsp</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>resAdministrators</role-name>
        <role-name>resMonitors</role-name>
        <role-name>resDeployers</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>
<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>Default</realm-name>
</login-config>

Important: CONFIDENTIAL in the transport-guarantee tag means that the use of HTTPS is required. Only HTTPS is used throughout this article.
For more information about each subelement in the example above, see Specifying Security Constraints.
Reminder: We will define new roles to invoke the decision service APIs later.

Protecting decision services

The REST and SOAP patterns are used to invoke decision services. They can be protected by adding and a new role that is dedicated to calling the APIs and configuring the web.xml file for the decision service WAR file for WebSphere Liberty (often named DecisionService.war):

<security-constraint>
    <web-resource-collection>
         <web-resource-name>Decision Service REST API</web-resource-name>
         <url-pattern>/rest/*</url-pattern>
         <url-pattern>/ws/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>resAdministrators</role-name>
        <role-name>resMonitors</role-name>
        <role-name>resDeployers</role-name>
        <role-name>resServiceUsers</role-name>
     </auth-constraint>
     <user-data-constraint>
          <transport-guarantee>CONFIDENTIAL</transport-guarantee>
     </user-data-constraint>
</security-constraint>
<login-config>
     <auth-method>BASIC</auth-method>
     <realm-name>Default</realm-name>
</login-config>

Here, you granted permissions to two URL patterns and four roles:

  • Three predefines roles resAdministrators, resMonitors and resDeployers: These roles are required because the test user interface in the Rule Execution Server console invokes the REST (/rest/) and SOAP (/ws/) APIs internally.
  • A new role resServiceUsers dedicated for the API calls

It is a good idea to create a new role resServiceUsers. This new role will be authorized to invoke decision services, so that you can implement the basic authentication protection. To make it work, the incoming HTTP request must have a basic authentication header, and the user in the basic authentication must have the resServiceUsers role.
You can specify several security-constraint tags in a single web.xml file, so that you can protect different sets of URLs and use the same or different roles. On the other hand, you can use the login-config tag only once in each web.xml file.

Creating one or several new roles for decision services

You create a new role resServiceUsers for decision services.
In the server.xml file, you must add a new user to the basic registry:

<basicRegistry id="basic" realm="customRealm">
    <user name="resAdmin" password="resAdmin" />
    <user name="resDeployer" password="resDeployer" />
    <user name="resMonitor" password="resMonitor" />
    <user name="resServiceUser" password="resServiceUser" />
    <group name="resAdministrators">
        <member name="resAdmin" />
    </group>
    <group name="resDeployers">
        <member name="resAdmin" />
        <member name="resDeployer" />
    </group>
    <group name="resMonitors">
        <member name="resAdmin" />
        <member name="resDeployer" />
        <member name="resMonitor" />
    </group>
    <group name="resServiceUsers">
        <member name="resServiceUser" />
    </group>
</basicRegistry>
<application type="war" id="DecisionService" name="DecisionService" location="${server.config.dir}/apps/DecisionService.war">
    <application-bnd>
        <security-role name="resAdministrators">
            <group name="resAdministrators" />
        </security-role>
        <security-role name="resDeployers">
            <group name="resDeployers" />
        </security-role>
        <security-role name="resServiceUsers">
            <group name="resServiceUsers" />
        </security-role>
     </application-bnd>
</application>

In this example, you added the new user resServiceUser with the password resServiceUser. The user is declared to be a part of the group resServiceUsers.
Note: You must set a strong password. The password provided above is only for the purpose of this article.
In the application block, you make an association between the group resServiceUsers and the role resServiceUser. The application DecisionService is now configured to use three roles, which are associated with the groups that have the same names.

Protecting at RuleApp level

You can configure security at the RuleApp level. However, setting up the protection at the RuleApp level may not be for everyone, since it requires that you update the web.xml deployment descriptor file every time a new RuleApp is deployed. If you frequently add or modify your RuleApps, you might not want to take this approach, since the protection works only per RuleApp.
Each deployed RuleApp will lead to a different endpoint. For example, loan related applications can be in a RuleApp called "loans", while pricing related applications can be in a RuleApp called "pricing".
When they are deployed, these two RuleApps create different endpoints:

  • https://app.example.com:9443/odm/test/DecisionService/rest/loans/…
  • https://app.example.com:9443/odm/test/DecisionService/rest/pricing/…

The two distinct URL patterns /rest/loans/* and /rest/pricing/* can be used to protect the invocation of the corresponding RuleApps. They can also use different roles.

<security-constraint>
    <web-resource-collection>
        <web-resource-name>REST API for Loan</web-resource-name>
        <url-pattern>/rest/loans/*</url-pattern>
        <url-pattern>/ws/loans/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>resLoansUsers</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>
<security-constraint>
    <web-resource-collection>
        <web-resource-name>REST API for Pricing</web-resource-name>
        <url-pattern>/rest/pricing/*</url-pattern>
        <url-pattern>/ws/pricing/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>resPricingUsers</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

Specifying a per-environment application context root

This advanced configuration should be considered only if you have environments that require these specific setups.
The context root is specified in the server.xml file for all the WAR files run by the server, and it provides an additional path for the URL.
For example:

<server>
    <application type="war" context-root="odm/test/DecisionService"location="${server.config.dir}/apps/DecisionService.war">
        …
    </application>
</server>

When you use this configuration in the server.xml file, the URL for a decision service looks like this:

  • https://app.example.com:9443/odm/test/DecisionService/rest/loans/1.0/miniloan/1.0

Then, the context root can be used to configure the web.xml file for protecting the HTDS on a per-environment basis by using a more specific URL:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Decision Service REST API</web-resource-name>
       <url-pattern>/odm/test/rest/*</url-pattern>
       <url-pattern>/odm/test/ws/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>resServiceTesters</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

Invoking the protected decision services

Before invoking a protected decision service, you must have a username and a password that can be used for the basic authentication header, and the right role that is authorized to invoke the corresponding decision services.

Using Java

The following Java code uses Apache HttpClient to set a basic authentication header:

HttpRequest request = new HttpPost("…");
String baHeader = username + ":" + password;
String base64 = Base64 .getEncoder().encodeToString(baHeader.getBytes());
request.setHeader("Authorization", "Basic " + base64);

Using cURL

Use https://www.blitter.se/utils/basic-authentication-header-generator/ to generate a basic authentication header.
Then use a cURL command to make an invocation. See the following example:

#!/bin/bash
URL= https://myservice.ibmcloud.com/odm/dev
BASIC= dG90bzp0aXRp
echo Invoking: $URL/res/api/ruleapps?count=true
echo Response:
curl -k -v $URL/res/api/ruleapps?count=true \
-H "Authorization: Basic $BASIC"
echo

Other protection methods

In the login-config tag in the web.xml file, you specified BASIC for basic authentication:

  • <auth-method>BASIC</auth-method>

The other possible values for auth-method are:

  • FORM: For form-based authentication. This value is not suitable for API.
  • CLIENT-CERT: Client certificate
  • DIGEST: Digest authentication

You can also use CLIENT-CERT or DIGEST as an alternative for the API protection. However the basic authentication remains by far the simplest and the most convenient protection.

Summary

In this article, you have learned:

  • How to set a basic authentication protection on the REST API while preserving the access to the test panel in the Rule Execution Server console with the predefined roles.
  • How to create a new role that is authorized to invoke the REST API.
  • Other URL configurations that can be used for protecting the API.

Further information

Can we configure security, such as basic authentication, for Hosted Transparent Decision Service? (dW Answers)

Join The Discussion

Your email address will not be published. Required fields are marked *