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.
An Authoriser processes an AuthorizationRequest and if granted generates a Session object to represent the user.
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
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.
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();}
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.
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.
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.
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.
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.
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.
Populate the flags column in the permissions table with the value 0 (for read/write) or 1 (for read only).
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.
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.