Saturday, September 27, 2008

OpenDS LDAP and GlassFish Configuration

Introduction

A question was posted to our JUG mailing list on how to lock a directory which contained an Adobe™ PDF file. In this example I want to cover a couple of really cool technologies, and how to implement them.

OpenDS is a project hosted on Java.net. It is an open source directory server which is based on the Sun™ SunOne™ Directory server and its predecessors (Netscape™ iPlanet™). It has a binary, or a Java™ Webstart deployment mechanism. It has a simple quick start setup including SSL based LDAP, and directory replication.

This is a simple to install, easy to use and configure, no-nosense LDAP server. It is a great server for prototyping.

Project Glassfish is an open source Java EE application server. It is simple to install and configure, and easy to use. The chances are pretty good that if you are reading this blog, you are familiar with its enormous benefits.

Case

The developer has a pdf file that is located inside a web project on the web server. The file can be bookmarked by users, but the developer wants to make sure that the user authenticates with the server before the file is displayed.


Software and Files Required


Setup

1. Download and install Glassfish

Note: When Glassfish is installed it creates its own self-signed SSL certificates. We will use this certificate later.
2. Start the installation of OpenDS



3. Create a new instance.

4. Accept the default installation directory. Set the password for the cn=Directory Manager to test.



5. Press the Configure button to configure the secure LDAP connection.
  • Set the Key Store Type to Java Key Store (JKS).
  • Set the key store path to point to the keystore.jks file located in the GLASSFISH_HOME/domains/domain1/config directory.
  • Set the Key Store Pin to changeit.


Note: "changeit" is the default password to the keystore. You will need to "change it" for a production system.




6. Select the default for topology options and continue to Directory Data Next.



  • Directory Data should have the default Directory Base DN: dc=example,dc=com
  • Select Import Data from LDIF file and use this example.ldif file. Next.

Note: I have created a pre-generated list of users previously. In this example, I have created a group and assigned it members. So to simplify the example, please use the example.ldif file.




7. Review the selections and Finish.



8. Authenticate with the server using cn=Directory Manager and password "test".






Note: A message will pop-up asking if you want to accept the certificate for the LDAP server. Use the this session only option.




Congrats. You now have a running LDAP server.

If you want to explore the contents of the directory use a tool like JXplorer, or Apache Directory Studio.

Now we need to configure our LDAP Security Realm in Glassfish. You may have multiple realms in Glassfish to accommodate application requirements.

1. Start Glassfish and login on http://localhost:4848. The default user is admin and password is adminadmin.

2. Go to Configuration --> Security --> Realms. Create a new Realm

  • Name: OpenDS
  • Class: com.sun.enterprise.security.auth.realm.LDAPRealm
  • JAAS context: ldapRealm
  • Directory: ldaps://localhost:1636
  • Base DN: dc=example, dc=com
  • Assign Group: ou=Groups,dc=example,dc=com
3. Add a property called group-target.
  • group-search-filter: member=%d





Note: The group-search-filter defines the attribute to look at when determining members of the groups. It may be member, memberurl, etc. depending on how you define your groups. In the example.ldif file, I have define the group as a groupOfNames which contains member [0...] attributes. The %d format expands to match the Relative Distinguished Name (RDN).


4. Save and you are done.

You have now configured Glassfish to use OpenDS.

Note: Since we configured it to use secure LDAP, remember to configure your server to use SSL to authenticate users. Otherwise the secure LDAP portion is a waste if the users are transmitting their usernames and passwords in cleartext over the network
I have created a sample Netbeans project which takes advantage of the new LDAP authentication. The project creates a page with a link to a PDF file located in a secured directory. The directory requires the user to authenticate using basic authentication implemented in the browser. If you fail to authenticate, it will produce a 403 error. The project is located here.

Once you successfully authenticate, you will see a list of popular software that runs on Glassfish.

10 comments :

telman said...

thank you very much
Regards,
Telman Shahbazov

telman said...

thank you very much
regards
telman shahbazov

Lou Springer said...

This is very close to something I need and was hoping you could help.

I have a JSR-250 @Resource annotation like so:

@Resource(name="wsdl/fileservice")
String fileservice;

I would like satisfied with an LDAP attribute named "uddiAccessPoint". The LDIF for the entry looks like this:

dn: uddiBindingKey=torrid,uddiServiceKey=fileservice,uddiBusinessKey=opendi-louspringer,ou=wsdl,dc=louspringer,dc=com
objectClass: uddiBindingTemplate
objectClass: top
uddiBindingKey: torrid
uddiAccessPoint: http://torrid.home.louspringer.com:8080/FileService/FileService
entryUUID: d267e158-10b0-4fdd-b93e-65630ed52af1
createTimestamp: 20081230195604Z
creatorsName: cn=Directory Manager,cn=Root DNs,cn=config

Do you know if there a simple way to do this, or should I just start coding?

John Yeary said...

Calling the DS to get a value is a trivial task. The Java JNDI tutorial has a number of really good examples. I personally would set up an external JNDI reference and use it to fetch the values you are interested in. There is another blog entry here that shows how to set up an external JNDI reference.

Here is a my @Resource for the LDAP Context:

@Resource(name = "ldap/bluelotussoftware")
LdapContext ctx;

Here is an example of how to fetch attributes:

public String getAttribute(final String uid, final String Attribute) {
final Attributes matchAttrs = new BasicAttributes(true); // ignore case

matchAttrs.put(new BasicAttribute("uid", uid));

String result = null;

try {
final NamingEnumeration answer = ctx.search((searchDN.split(","))[0], matchAttrs);

final SearchResult sr = (SearchResult) answer.next();

result = (String) sr.getAttributes().get(Attribute).get();
} catch (final NamingException e) {
warn("NamingException occurred while trying to fetch attribute: " + e.getMessage());
} catch (final NullPointerException e) {
result = "Attribute Not Defined";
}
return result;
}

soft blogger said...

Dear John,

Your instructions showed the dreaded "you don't get 389. i'll give you 1389 and you'll like it".

Now, i hate getting this. Apple might be right to prevent people from logging in as root, but this is one result of that decision.

My solution is to download the zip and run the following within the unzipped directory:
sudo ./setup

Now I get my 389. A tiny "i win but nobody cares".

John Yeary said...

Thanks for the comment on 389 vs. 1389. It will surely be of help to someone trying to set it up on a Mac, or other systems with the restrictions.

telman said...

it is regarding to "@Resource(name = "ldap/bluelotussoftware")
LdapContext ctx;"

it works fine for searching

but trying to perform

ctx.createSubcontext(dn,attrs)

it fails with error code 50


javax.naming.NoPermissionException: [LDAP: error code 50 - The entry uid=11111111109,ou=People,dc=example,dc=com cannot be added due to insufficient access rights]; remaining name 'uid=11111111109,ou=People,dc=example,dc=com'

ldap/openDS jndi properties that configured in glassfish (Resurces/JNDI/Custom Resources) are :

Resource Type : javax.naming.directory.Directory
Factory Class : com.sun.jndi.ldap.LdapCtxFactory

properties :
java.naming.security.credentials:
cn=Directory Manager
java.naming.security.principal
telman
URL:
ldap://localhost:389


in case of using code access there is
no problem and user has been added to LDAP.

Hashtable env = new Hashtable();
env.put(Context.SECURITY_PRINCIPAL, "cn=Directory Manager");
env.put(Context.SECURITY_CREDENTIALS, "telman");
DirContext initial = new InitialDirContext(env);
DirContext context = (DirContext) initial.lookup("ldap://localhost:389");
context.createSubcontext(LDAPUtil.getUserDN(user.getNationalId()), attributes);


Could you please advise ?

Regards
Telman

John Yeary said...

Have you set SECURITY_AUTHENTICATION mode that you are using to "simple","strong" or "none"? Try using "none", or "simple" and see if that helps.

telman said...

Hi,
setting the group-target to member=%d results as Access denied. I was successed with setting group-search-filter to cn=*. but in that case the request.isUserInRole("some_group") return true for a user wich is not in that "some_group". Could you please advice.
regards,
telman

John Yeary said...

I am not currently using OpenDS, I think that question would be better asked on a forum.

Popular Posts