• ADF
    March 28, 2014

ADF Mobile: the Access Control Service

Guest Author

ADF Mobile applications use standard HTTP mechanisms for authentication.  The HTTP protocol, however, does not handle authorization. Thus, to enable applications to obtain the roles and privileges of a specific user,  you need to implement a REST web service called the Access Control Service. In this post, I will show you how to implement the foundations for that service. 

The product documentation states that the Access Control Service consumes and produces JSON data. The snippet below illustrates how parameters are passed to the service. 

{"userId": "johnsmith","filterMask": ["role", "privilege"],"roleFilter": [ "role1", "role2" ],"privilegeFilter": ["priv1", "priv2", "priv3"] 

All parameters other than userId are optional. FilterMask is used to specify which filters should be applied to the request (role, privilege or both). RoleFilter and privilegeFilter simply enumerate the filter values. If no filters are specified, the web service should return the list of all roles and privileges for the user. Otherwise, the service should only verify if the user belongs to the roles listed in roleFilter and if he has been granted the privileges listed in privilegeFilter.

The ADF Mobile documentation gives the JSON snippet below as an example of a service response.

{"userId": "johnsmith","roles": [ "role1" ],"privileges": ["priv1", "priv3"] 

As you can see, the service returns only the roles and privileges that actually apply to the user. 

The easiest way to implement the service is to take advantage of the POJO mapping feature offered by some JAX-RS implementations, such as Jersey. The first step is to build POJO class definitions for the service request and response. JDeveloper made this very easy. I only had to type a few lines of code for the class attributes and generated the accessors and constructors. 

This is the code for the request class. 

package oracle.sample.maf.accesscontrol.bo;
import javax.xml.bind.annotation.XmlRootElement;
public class ACSRequest {
private String userId;private String[] filterMask;private String[] roleFilter;private String[] privilegeFilter;public ACSRequest() {super();}public ACSRequest(String userId, String[] filterMask, String[] roleFilter, String[] privilegeFilter) {super();this.userId = userId;this.filterMask = filterMask;this.roleFilter = roleFilter;this.privilegeFilter = privilegeFilter;}public void setUserId(String userId) {this.userId = userId;}public String getUserId() {return userId;}public void setFilterMask(String[] filterMask) {this.filterMask = filterMask;}public String[] getFilterMask() {return filterMask;}public void setRoleFilter(String[] roleFilter) {this.roleFilter = roleFilter;}public String[] getRoleFilter() {return roleFilter;}public void setPrivilegeFilter(String[] privilegeFilter) {this.privilegeFilter = privilegeFilter;}public String[] getPrivilegeFilter() {return privilegeFilter;}

The @XmlRootElement annotation makes it very easy to generate both JSON and XML from a single set of objects; I added it to the class even though it wasn't necessary in this specific use case.

The code for the response class is straightforward.  

package oracle.sample.maf.accesscontrol.bo;
import javax.xml.bind.annotation.XmlRootElement;
public class ACSResponse {
private String userId;private String[] roles;private String[] privileges;
public ACSResponse() {super();
roles = new String[0];
privileges = new String[0];}
public ACSResponse(String p_userId, String[] p_roles, String[] p_privileges) {super();
userId = p_userId;
roles = p_roles;
privileges = p_privileges;}
public String getUserId(){return userId;}
public void setUserId(String p_id){
userId = p_id;}
public String[] getRoles(){return roles;}
public void setRoles(String[] p_roles){
roles = p_roles;}public String[] getPrivileges(){return privileges;}
public void setPrivileges(String[] p_privileges){
roles = p_privileges;}

By default, POJO mapping is not enabled in Jersey. It is thus essential to add the following init parameter to the Jersey servlet declaration in the web.xml for the application.


The service itself is implemented as yet another POJO. I generated a skeleton for the class using the REST web service wizard in JDeveloper. The method that will process the request must be configured to process requests made using the POST HTTP verb. Hence, the single method in the class is annotated with @POST.

package oracle.sample.maf.accesscontrol.service;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import oracle.sample.maf.accesscontrol.bo.ACSRequest;
import oracle.sample.maf.accesscontrol.bo.ACSResponse;
public class UserRessource {public UserRessource() {}@POSTpublic Response postData(ACSRequest request) {
// Replace this with actual logic.
ACSResponse rolesAndPrivileges = new ACSResponse(request.getUserId(), new String[] { "user" }, new String[] { "user" });
Response.ResponseBuilder builder = Response.ok(rolesAndPrivileges);return builder.build();}

The @Path annotation on line 12 determines the path to the REST resource, whereas @Consumes and @Produces specify the expected data formats for the method's input and output. 

The sample class above doesn't contain error handling code and returns a hard coded response. A production implementation should use the various static methods in javax.ws.rs.core.Response to build responses. The ok(Object) method will typically be used; other methods such as noContent(), notAcceptable(List) and serverError() will be called instead when specific conditions are met or if exceptions have been raised. 

There are many ways to obtain the data needed to populate the response. You could query a database, for example, or use the OPSS APIs to query an LDAP server. Whatever option you choose, your service implementation should ensure that users cannot query another user's roles and privileges unless they have administrative privileges themselves. In other words: exposing on the internet a web service which enables anybody to identify privileged user accounts for your back-end is a bad idea. Ideally, your service should:

  • Accept connections over HTTPS only
  • Check that the credentials used to establish the SSL / TLS connection match the userId in the service input - unless the user can manipulate the roles and privileges of other users
The Access Control service is an essential component of the ADF Mobile architecture. Since Oracle only specifies the service signature, you can implement it in the way that best fits your infrastructure and the level of authorization granularity you expect.  

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha