Managing security

Naked Objects supports authentication  and role-based authorization. Within the framework the user is represented by a Session object. From the session object you can get the name of the user and their roles.

The session is used within the distribution mechanism and the security manager. The security manager uses the session to determine if a user can access or manipulate an object member. The distribution mechanism passes the session to the server so the server knows which user is accessing it.

Authentication

An Authoriser processes an AuthorizationRequest and if granted generates a Session object to represent the user.

Authentication through a passwords file

To explicitly use file-based authentication set the following property. (Note, however, that if this property is absent, the framework will anyway assume file-based authentication as the default).

nakedobjects.authentication=file

The location and name of the passwords file may be specified explicitly - if not it defaults to the passwords file, assumed to be in the config directory (or under WEB-INF if running as a web application).

nakedobjects.authentication.file.location= passwords  

The passwords file contains users, passwords and any roles, as follows.

<user>:<password>:<role>|<role>|...

For example, user 'sven', with password 'pass' and roles 'role1' and 'role2' would be

sven:pass:role1|role2 

Authentication through an LDAP server

To authenticate through LDAP set

nakedobjects.authentication=ldap

LDAP needs a URL for the server and a 'dn' of the root of the users on the server. For example,

nakedobjects.authentication.ldap.dn= dc=nakedobjects, dc=org 
nakedobjects.authentication.ldap.server=ldap://localhost:10389

The logon password check will be on uid='username', <dn> - For example, attempting to logon with user 'sven' with the above settings will check against the server on localhost, port = 10389, uid=sven, dc=nakedobjects, dc=org, using the password in the standard userpassword attribute in that entry. Once logged in, roles will be picked up from cn='rolename' where objectclass =organizationalRole in child entries of the user entry.

Authentication through a database

To authenticate through a database set

nakedobjects.authentication=database

Database authentication is dependent upon Hibernate. Authentication tables should exist in the database configured in the hibernate.cfg.xml settings. By default this will check against a set of user and role database tables described as follows.

CREATE TABLE user (id INTEGER  NOT NULL , username VARCHAR(255) NOT NULL,password VARCHAR(255) NOT NULL, PRIMARY KEY (id));
CREATE TABLE role (id INTEGER  NOT NULL , rolename VARCHAR(255) NOT NULL, PRIMARY KEY (id)) ;
CREATE TABLE user_role (id INTEGER  NOT NULL , user INTEGER  NOT NULL,role INTEGER  NOT NULL, PRIMARY KEY (id));

The password should be encrypted using the 'MD5' algorithm. (Example code from sun ).

private String generateHash(String key) {
   MessageDigest md = MessageDigest.getInstance("MD5");
   md.reset();
   md.update(key.getBytes());
   byte[] bytes = md.digest();
   // buffer to write the md5 hash to
   StringBuffer buff = new StringBuffer();
   for (int l=0;l< bytes.length;l++) {
      String hx = Integer.toHexString(0xFF & bytes[l]);
      // make sure the hex string is correct if 1 character
      if(hx.length() == 1) buff.append("0");
      buff.append(hx);
   }
return buff.toString();}

Authorization

As with authentication, authorization may be executed via a file, LDAP, or database. If no authorization mechanism is specified, all domain calls, methods and properties will be available to all users.

Authorization through 'allow' and 'disallow' files

To enable authorization by file including the facet and specifying the authorization implementation in the settings

nakedobjects.reflector.facets.include=\
    org.nakedobjects.runtime.authorization.standard.AuthorizationFacetFactoryImpl
nakedobjects.authorization=file

With file authorization the current user's role will be authorised against a white and (optionally) a black list for each method possibly available to them. The files are identified by properties, e.g.

nakedobjects.authorization.file.whitelist=allow
nakedobjects.authorization.file.blacklist=disallow  

The white list file is mandatory, if there is no property it defaults to 'allow' (picked up from the config directory or from WEB-INF if running as a webapp). The black list file is optional. The presence of the property indicates it is being used. If the white list file default is being used then 'disallow' is suggested as a setting for the black list.

Each file contains a signature to match against and a list of roles. A match in the white list file permits the method and match in the black list forbids it. The black list overrides the white list. Matching occurs at three levels class, method (or property) and parameters. Property matches are to the bean property name e.g. 'phone' for getPhone and setPhone.

Formatted as follows:

If only class is to be matched: <fully qualified class> :role1|role2|..

Class and method: <fully qualified class>#<method>:role1|role2|..

Class and property: <fully qualified class>#<property>:role1|role2|..

Parameters where method takes no parameters: <fully qualified class>#<method>():role1|role2|...

Parameters where method takes one parameter: <fully qualified class>#<method>(fully qualified parameter class):role1|role2|...

Parameters where method takes two or more:

<fully qualified class>#<method>(fully qualified parameter class1, fully qualified parameter class2, ...):role1|role2|...

Each line in the file is a separate match.

So, for example, in the white list file

com.nakedobjectsgroup.expenses.services.hibernate.ClaimRepositoryHibernate:role1 

will permit all actions/properties on com.nakedobjectsgroup.expenses.services.hibernate.ClaimRepositoryHibernate for role1

com.nakedobjectsgroup.expenses.services.hibernate.
    ClaimantRepositoryHibernate#findClaimantByName:role1|role2

will permit all overloaded methods named findClaimantByName on com.nakedobjectsgroup.expenses.services.hibernate.ClaimantRepositoryHibernate for role1 and role2 and

com.nakedobjectsgroup.expenses.services.hibernate.ClaimantRepositoryHibernate#findClaimantByIdentifier(java.lang.String):role3

will permit findClaimantByIdentifier with parameter java.lang.String on com.nakedobjectsgroup.expenses.services.hibernate.ClaimantRepositoryHibernate for role3. In each case an identical entry in the black list file will instead make the action or property unavailable. The combination of white and black list allows some economy in the settings. For example, if all but one method of a large class is to be allowed, that could be configured with one entry per method in the white list file or, better, a single entry for the class in the white list and a single entry for the unavailable method in the black list.

Authorization through LDAP server

To enable authorization by LDAP server include facet for authorization and specifying the ldap implementation for authorization.

nakedobjects.reflector.facets.include=\
    org.nakedobjects.runtime.authorization.standard.AuthorizationFacetFactoryImpl
nakedobjects.authorization=ldap

With LDAP authorization the current user's role will be authorised against an entry in the LDAP server for each method possibly available to them. The URL of the server is obtained from the same property as for authentication.

The authorization entries should exist under a DN configured in the property file. For example.

nakedobjects.authorization.ldap.application.dn= cn=expenses, dc=apps, dc=nakedobjects, dc=org 

This will be checked anonymously.

Entries to be matched against should exist under this dn. Expected configuration is a hierarchy of class, method and parameters. The cn of each entry is expected to be the fully qualified class name, method or parameter list ('()' for an empty parameter list). If an entry has a 'uniquemember' attribute equal to 'role' then that entry and all sub-entries are authorised.

For example

DN: cn=com.nakedobjectsgroup.expenses.services.hibernate.ClaimRepositoryHibernate, cn=expenses, dc=apps, dc=nakedobjects, dc=org

with uniquemember = role1 will authorise all members of com.nakedobjectsgroup.expenses.services.hibernate.ClaimRepositoryHibernate for users with role1.

DN: cn=(com.nakedobjectsgroup.expenses.claimant.Claimant, java.lang.String),

cn=createNewClaim, cn=com.nakedobjectsgroup.expenses.services.hibernate.ClaimRepositoryHibernate, cn=expenses, dc=apps, dc=nakedobjects, dc=org

with uniquemember = role1 will authorise the createNewClaim(com.nakedobjectsgroup.expenses.claimant.Claimant, String) member of com.nakedobjectsgroup.expenses.services.hibernate.ClaimRepositoryHibernate for users with role1.

Authorization through database

To enable authorization through a database table specify:

nakedobjects.reflector.facets.include=\
    org.nakedobjects.runtime.authorization.standard.AuthorizationFacetFactoryImpl
nakedobjects.authorization=database

In the same way as database authentication, database authorization is dependent upon hibernate. The authorization table should exist in the database configured in the hibernate.cfg.xml settings. By default this will check against a permissions table described as follows.

CREATE TABLE permissions (id INTEGER  NOT NULL, role INTEGER  NOT NULL,permission VARCHAR(255) NOT NULL, flags INTEGER, PRIMARY KEY (id));

The functionality is the same as for white list file matching described above, where the permission field in the table contains the signature to match.

Separate visibility and usability authorization

There is an optional capability to authorise the visibility and usability of fields independently. Thus a field may be visible to certain roles but not editable. In order to configure this against a authorization a flag must be added as follows.

File authorization

Add a "-rw" suffix to any role in the allow or disallow files. "-rw" will match (and hence allow in the allow file, disallow in the disallow file) edit authorization requests.

Database authorization

Populate the flags column in the permissions table with the value 0 (for read/write) or 1 (for read only).

Ldap authorization

Add a flags attribute to the entry with the role. If it's set to 'rw' editing will be allowed, any other value (typically 'ro') will disallow editing.

In each case absence of the flag will mean that visibility and usability and are always the same.

Capturing authorization requirements automatically

As a tool for helping to configure the initial security settings there is a 'learn' property.

nakedobjects.authorization.learn=true

When this is present and set, all methods will be authorised for all roles. However as each method is accessed through the user interface authorization will be configured for that method and the current role. Configuration will be saved in either the file (saved on shutdown), database table or LDAP security settings depending upon the reflector setup.

The settings can then be modified to conform to the required security settings.