Thursday Dec 18, 2014

GeneratedPassword: a hidden MAF gem

The transition from ADF Mobile to the Oracle Mobile Application Framework earlier this year brought us many great new features. Some of them were very visible, such as new Data Visualization components or the new Oracle Alta UI skin. Others are more difficult to find. In this post, I want to introduce you to one of the latter, namely the GeneratedPassword class, which is part of the oracle.adfmf.framework.api package.

The sole function of GeneratedPassword is to generate and manage random passwords. Each of the passwords is identified by a key, which will be used to retrieve it when needed. The passwords are saved to an encrypted credential store, similar to the one used to store user passwords related to login connections. To create a password, simply pass the desired key to the appropriate method along with a seed which will add entropy to the generation algorithm. Subsequent calls to that method using the same key and seed will result in a different password every time. 

At this point, maybe you are asking yourself what those random passwords are good for. Personally, I find them very useful to enhance the protection of encrypted SQLite local databases. In the current version of Oracle MAF, it is necessary to provide a password in order to encrypt or decrypt a database. If you hardcode that password, it will be shared by all deployed instances of your application. If one instance is compromised, then all others will be at risk. Using a random password for that use case is a good mitigation measure, since every instance uses a different encryption key. Compromising multiple instance is thus much more time consuming. 

Friday Mar 28, 2014

ADF Mobile: the Access Control Service

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;

@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;

@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.

        <init-param>
            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            <param-value>true</param-value>
        </init-param>

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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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;

@Path("user")
@Consumes("application/json")
@Produces("application/json")
public class UserRessource {
    public UserRessource() {
    }

    @POST
    public 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.  

About

Frédéric Desbiens

The musings of a member of the Mobility and Development Tools Product Management team.

I focus here on my favorite development frameworks, namely Oracle ADF and the Oracle Mobile Application Framework (MAF), but also have a strong interest in SOA and web services.

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

Search

Archives
« April 2015
SunMonTueWedThuFriSat
   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  
       
Today