Attributes

Attributes are a feature of a Directory service and are not available with simple name servers. Typically, you use attributes with an LDAP server. The J2EE RI JNDI server is a CORBA name server, and it does not support attributes.

An attribute is additional information stored with a name. Storing full name, address, phone number, and e-mail with a person's name is a common use of a directory service. NDS uses attributes to control access to shared network drives and to configure a user's login environment.

A directory service stores attributes as values against a keyword (LDAP calls them IDs). Directory services usually support searching for names (objects) that have certain attributes defined (or not defined). Searching often supports looking for names with certain attributes that have a specific value (often wildcard pattern matching is supported). A simple search of a personnel database under an LDAP server might be to find all names whose surname is Washington.

LDAP uses a schema system to control which attributes an object must define and those that it may define. Any attributes that you add or delete must not break the schema's requirements. LDAP servers may be able to disable schema checking, but this is usually a bad idea because the schema was created for a purpose.

If you want to see the capabilities of attributes, you must have access to a directory server. The rest of this section is based on using an LDAP Directory Server.

Overview of LDAP X.500 Names

LDAP names conform to the X.500 standard that requires a hierarchical namespace. A Distinguished Name (DN) unambiguously identifies each entry in the directory. The DN consists of the concatenation of the names from the root of the directory tree down to the specific entry.

X.500 focuses on the interoperability of different directory services rather than specifying how a directory service should define the DN. Consequently, different implementations of X.500 can each use a different syntax for representing object names (as shown earlier when we compared LDAP and Microsoft Active Directory names).

The official specification for the X.500 Directory Service is available from the International Telecommunications Union (ITU) Web site at http://www.itu.int/rec/recommendation.asp?type=items&lang=e&parent=T-REC-X.500-199708-S.

LDAP uses a comma-separated list of names with the names specified from the lowest entry up the tree to the higher entries.

Names consist of name and value pairs with the names typically being those in the following list. Each name has a short code and a full name; it is usual to only use the short code because the Distinguished Names are fairly long.

  • c (countryName)— ISO two letter code for county such as us, uk, and so forth.

  • o (organizationName)— Organization or company name, such as samspublishing

  • ou (organizationUnitName)— Organizational unit, typically a division or department within an organization

  • l (localityName)— Typically defines a location within an organizational unit

  • cn (commonName)— Common name (sometimes called personal name), usually the name of the user or client

  • dc (domainComponent)— A component part of a domain name (such as DNS names)

  • uid (userid)— Typically represents a login name

An example LDAP DN looks like the following:

cn=Martin Bond, ou=Authors, o=SAMS, c=us

This will be a familiar structure if you work with digital certificates whose names conform to the X.509 standard.

Obtaining an LDAP Server

Using an LDAP Directory Service requires the JNDI properties to specify the JNDI Service provider from Sun Microsystems and to have an LDAP server running.

The J2EE RI does not include an LDAP server, so you will have to obtain one from elsewhere. Only certain operating systems provide LDAP servers. Windows NT, 2000, and XP users will have to purchase the enterprise (or server) editions of the operating system, which are typically significantly more expense than the usual desktop or professional editions. Sun Microsystems' Solaris 8 Operating Environment includes an LDAP server.

Linux and Unix users can download and install the OpenLDAP implementation, which is an open source server available free of charge for personal use. The Open LDAP server can be downloaded from http://www.openldap.org/software/download/.

Users of Microsoft Windows will have to make other arrangements as OpenLDAP is not available for the platform. If an Active Directory server is accessible on the network or you use the Enterprise (or Server) edition of the Operating System, this is a simple solution. Otherwise, Windows users are well advised to find a spare PC and install Linux on that and use OpenLDAP.

Note

Interestingly, with today's low cost of hardware, it may be cheaper for the home user to purchase a second system to run Linux rather then purchase the additional license required to run the Enterprise (or Server) versions of Windows NT/2000/XP.


Using OpenLDAP

Given that many users experimenting with LDAP will probably use OpenLDAP on a Linux server, a brief digression on configuring Linux/LDAP for the programs in the rest of this section is useful. Other users must adapt the configuration for their own LDAP servers.

Note

You only need to install Open LDAP if you want to evaluate the directory service features of JNDI and do not have access to a suitable directory service on your network. The following discussion of LDAP is not necessary for your understanding and use of J2EE.


The rest of this sub-section assumes that you have some knowledge of Linux and OpenLDAP (at the very least, that you have successfully installed OpenLDAP on a Unix server). The following OpenLDAP dicussion assumes that you have installed OpenLDAP in the default location of /usr/local (for example OpenLDAP v2.0.15 will be in the directory /usr/local/openldap- 2.0.15).

If you build and install OpenLDAP according to the supplied instructions, the process results in an empty LDAP directory namespace. The following steps will populate that namespace with a few sample entries. Make sure the slapd (LDAP) server is not running before making the following changes (if you have just installed OpenLDAP then slapd will not be running).

If you install OpenLDAP 2.0 with the recommended Berkeley database, it creates a default empty database with the DN suffix of dc=my-domain,dc=com.

To create a new DN suffix of o=Agency,c=US, you must add the following lines to the end of the slapd configuration file (/usr/local/etc/openldap/slapd.conf):

database        ldbm
suffix          "o=Agency,c=US"
directory       /usr/local/var/openldap-ldbm
rootdn          "cn=Manager,o=Agency,c=US"
rootpw          secret
index           objectclass     eq
index           cn,sn   pres,eq,sub,subany

Having defined the DN, you must add some sample data to the database. Listing 3.9 shows a configuration file for populating the database with sample data for use with the example programs shown in this section.

Listing 3.9. Full Text of agency.ldif
 1: dn: o=Agency,c=us
 2: objectclass: top
 3: objectclass: organization
 4: o: Agency
 5: description: Job Agency
 6:
 7: dn: ou=Customers,o=Agency,c=us
 8: objectclass: top
 9: objectclass: organizationalUnit
10: ou: Customers
11:
12: dn: cn=All Customers,ou=Customers,o=Agency,c=us
13: objectclass: top
14: objectclass: groupofnames
15: member: cn=Winston,ou=Customers,o=Agency,c=us
16: member: cn=George,ou=Customers,o=Agency,c=us
17: cn: Customers
18:
19: dn: cn=Manager,o=Agency,c=us
20: objectclass: top
21: objectclass: person
22: cn: Manager
23: sn: Manager
24:
25: dn: cn=George,ou=Customers,o=Agency,c=us
26: objectclass: top
27: objectclass: person
28: cn: George
29: cn: George Washington
30: description: President
31: sn: Washington
32:
33: dn: cn=Abraham,ou=Customers,o=Agency,c=us
34: objectclass: top
35: objectclass: person
36: cn: Abraham
37: cn: Abraham Lincoln
38: description: President
39: sn: Lincoln
40:
41: dn: cn=Winston,ou=Customers,o=Agency,c=us
42: objectclass: top
43: objectclass: person
44: cn: Winston
45: cn: Winston Churchill
46: description: Prime Minister
47: sn: Churchill
						

Copy this file to the Linux server and call it agency.ldif. Ensure that the slapd LDAP server is not running and, using the following slapdadd commandto install the sample names in the OpenLDAP configuration:

slapadd -b "o=Agency,c=US" –l agency.ldif

You can check the database configuration by using the slapcat command as follows:

slapcat | more

If you make a mistake or want to change the database configuration, you must delete the existing entries (slapadd will not replace an entry that already exists in the database). You can delete the existing OpenLDAP database by removing all of the files in the database directory specified in the slapd.conf configuration file. The following command will delete the default ldbm database used by slapd:

rm /usr/local/var/openldap-ldbm/*

You can now use slapadd to create the new database as shown previously.

You can start the slapd (OpenLDAP) server using the following command:

/usr/local/openldap-2.0.15/services/slapd/slapd –d 1

This will run the server in debug mode with diagnostic messages being displayed to the screen.

In the next section, you will test your LDAP server setup. You will find that the OpenLDAP database now has three entries for the Customer Organizational Unit:

cn=Abraham,ou=Customers,o=Agency,c=us
cn=George,ou=Customers,o=Agency,c=us
cn=Winston,ou=Customers,o=Agency,c=us

You must leave the slapd server running while you evaluate the examples shown in the rest of this section. When you want to stop the server, simply use Ctrl+C to interrupt the server or use the kill command to send the server a terminate signal.

Configuring JNDI to use LDAP

After an LDAP server is available for use, you must configure JNDI to use that server. This requires that you to obtain a JNDI Service Provider for LDAP (this isn't part of the LDAP server) and configure the JNDI properties accordingly.

JDK1.3 and J2EE RI 1.3 include an LDAP Service Provider from Sun Microsystems and all you have to do to use LDAP is to configure the JNDI properties to use the LDAP service. The simplest way to do this is to create an empty text file in the current directory called jndi.properties and add the following lines to this file.

java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
java.naming.provider.url=ldap://localhost:389

If the LDAP server is not running on the current machine, replace the name localhost with the name or IP address of the actual LDAP server. Port number 389 is the default LDAP port number, and you can omit it if LDAP is running on the default port (or replace it by the actual port number if a non-standard port is being used).

Testing the LDAP Server

Before looking at attributes, it is worth checking that the LDAP server is up and running with the sample data. Verify that you can look up one of these sample names by using the simple JNDILoopAny script shown in Listing 3.10. The code reads the JNDI name from the command-line arguments.

Listing 3.10. Full Text of JNDILookupAny.java
 1: import javax.naming.*;
 2: public class JNDILookupAny
 3: {
 4:
 5:     public static void main(String[] args) {
 6:         if (args.length != 1) {
 7:             System.err.println ("Usage: JNDILookupAny JNDIname");
 8:             System.exit(1);
 9:         }
10:         try {
11:             Context ic = new InitialContext();
12:             Object o = ic.lookup(args[0]);
13:             System.out.println(args[0]+"="+o);
14:         }
15:         catch (NamingException ex) {
16:              System.err.println(ex);
17:              System.exit(1);
18:         }
19:         catch (ClassCastException ex) {
20:              System.err.println(ex);
21:              System.exit(1);
22:         }
23:     }
24: }
						

Run the following command to check access to the LDAP server.

java JNDILookupAny "cn=Manager, o=Agency,c=us "

This will display an entry similar to the following:

ou=Customers,o=Agency,c=US=com.sun.jndi.ldap.LdapCtx@42719c

If the test doesn't work, you must check your local LDAP configuration. The most likely problem will relate to security. You may need to provide security credentials to the LDAP server. The “Security” section at the end of today's lesson briefly covers this.

Obtaining a Directory Context

Attributes are only supported by Directory Services and cannot be accessed through the ordinary Context object. Instead, you must use a javax.naming.directory.DirContext class. The DirContext is a sub-class of Context, and you can use it in place of a Context when dealing with a Directory Service where you require directory functionality (such as attributes). For example,

DirContext ic = new InitialDirContext();

The javax.naming.directory package contains the other attribute classes discussed next.

Reading Attributes

Attributes are read from the context just like a name is looked up from the context. The DirContext.getAttributes() method returns a NamingEnumeration that contains a collection of Attribute objects. Each Attribute has an ID (or key) and a list of values (an attribute can have more than one value for the same key). Listing 3.11 shows how all the attributes for an object can be listed.

Listing 3.11. Full Text of JNDIAttributes.java
 1: import javax.naming.*;
 2: import javax.naming.directory.*;
 3: public class JNDIAttributes
 4: {
 5:     public static void main(String[] args) {
 6:         if (args.length != 1) {
 7:             System.err.println ("Usage: JNDIAttributes JNDIname");
 8:             System.exit(1);
 9:         }
10:         try {
11:             DirContext ctx = new InitialDirContext();
12:             Attributes attrs = ctx.getAttributes(args[0]);
13:             NamingEnumeration ae = attrs.getAll();
14:             while (ae.hasMore()) {
15:                 Attribute attr = (Attribute)ae.next();
16:                 System.out.println("  attribute: " + attr.getID());
17:                 NamingEnumeration e = attr.getAll();
18:                 while (e.hasMore())
19:                     System.out.println("    value: " + e.next());
20:             }
21:             System.out.println("END of attributes for "+args[0]);
22:         }
23:         catch (NamingException ex) {
24:             System.out.println (ex);
25:             System.exit(1);
26:         }
27:     }
28: }
						

Running this program against the sample data produces the following result:

> java JNDIAttributes "cn=George,ou=Customers,o=Agency,c=us"
  attribute: description
    value: President
  attribute: objectClass
    value: person
  attribute: sn
    value: Washington
  attribute: cn
    value: George
    value: George Washington
END of attributes for cn=George,ou=Customers,o=Agency,c=us

A second form of the getAttributes() method allows you to provide an array of attribute names, and it only returns the values for those attributes. It is not an error to query an attribute that isn't defined; it simply doesn't return a value for that attribute. The following fragment shows how to find the cn and sn attributes for a name:

String[] IDs = {"sn", "cn"};
Attributes attrs = ctx.getAttributes("cn=George,ou=Customers,o=Agency,c=us",IDs);

Searching for Objects

A powerful and useful feature of attributes is the ability for you to search for names that have specific attributes or names that have attributes of a particular value.

You use the Context.search() method to search for names. There are several overloaded forms of this method, all of which require a DN to define the context in the name tree where the search should begin. The simplest form of search() takes a second parameter that is an Attributes object that contains a list of attributes to find. Each attribute can be just the name or the name and a value for that attribute.

Listing 3.12 shows a simple program to find all names that have a surname (sn) defined and a description of President.

Listing 3.12. Full Text of JNDISearch.java
 1: import javax.naming.*;
 2: import javax.naming.directory.*;
 3: public class JNDISearch
 4: {
 5:     private final static String JNDI = "ou=Customers,o=Agency,c=us";
 6:
 7:     public static void main(String[] args) {
 8:         try {
 9:             DirContext ctx = new InitialDirContext();
10:             // create case insensitive search attributes
11:             Attributes match = new BasicAttributes(true);
12:             match.put(new BasicAttribute("sn"));
13:             match.put(new BasicAttribute("description","president"));
14:             NamingEnumeration enum = ctx.search(JNDI, match);
15:             while (enum.hasMore()) {
16:                 SearchResult res = (SearchResult)enum.next();
17:                 System.out.println(res.getName()+","+JNDI);
18:             }
19:         }
20:         catch (NamingException ex) {
21:             System.out.println (ex);
22:             System.exit(1);
23:         }
24:     }
25: }
						

The search() method returns a NamingEnumeration containing objects of class SearchResult (a sub-class of NameClassPair discussed earlier). The SearchResult encapsulates information about the names found. The example program simply prints out the names (the names in the SearchResult object are relative to the context that was searched).

Running the program in Listing 3.12 will return the following values from the sample data:

cn=George,ou=Customers,o=Agency,c=us
cn=Abraham,ou=Customers,o=Agency,c=us

The SearchResult class also has a getAttributes() method that returns the attributes for the found name. The simple search shown in Listing 3.12 returns all of the name's attributes.

A second form of the search() method takes a third parameter that is an array of String objects specifying the attributes for the method to return. The following code fragment shows how to search and return just the surname and common name attributes:

NamingEnumeration enum = ctx.search(JNDI, match, new String[]{"sn","cn"});

Another form of the search() method takes a String parameter specifying a search filter. The filter uses a simple prefix notation for combining attributes and values. The JNDI API documentation and the JNDI Tutorial from Sun Microsystems provides full details of the search filter syntax. Listing 3.13 shows a search for names with a description or President or Prime Minister.

Listing 3.13. Full Text of JNDIFilter.java
 1: import javax.naming.*;
 2: import javax.naming.directory.*;
 3: public class JNDIFilter
 4: {
 5:     private final static String JNDI = "ou=Customers,o=Agency,c=us";
 6:
 7:     public static void main(String[] args) {
 8:         try {
 9:             DirContext ctx = new InitialDirContext();
10:             SearchControls sc = new SearchControls();
11:             String filter = "(|(description=President)(description=Prime Minister))";
12:             NamingEnumeration enum = ctx.search(JNDI, filter, sc);
13:             while (enum.hasMore()) {
14:                 SearchResult res = (SearchResult)enum.next();
15:                 System.out.println(res.getName()+","+JNDI);
16:             }
17:         }
18:         catch (NamingException ex) {
19:             System.out.println (ex);
20:             System.exit(1);
21:         }
22:     }
23: }
						

You can use the javax.naming.directory.SearchControls argument required by search() to

  • Specify which attributes the method returns (the default is all attributes)

  • Define the scope of the search, such as the depth of tree to search down to

  • Limit the results to a maximum number of names

  • Limit the amount of time for the search

Running the program in Listing 3.13 with the sample data produces the following output:

cn=George,ou=Customers,o=Agency,c=us
cn=abraham,ou=Customers,o=Agency,c=us
cn=Winston,ou=Customers,o=Agency,c=us

Manipulating Attributes

The DirContext. ModifyAttributes() method supports the addition, modification, and deletion of attributes for a name. To manipulate an attribute, the program must have write permission to entries in the LDAP name server. On a live system, the program must supply valid user credentials when obtaining the initial context (see the “Security” section later in this lesson). If you attempt to modify a name's attributes without the requisite permissions, a javax.naming.NoPermissionException is thrown.

If you are using the OpenLDAP server purely for evaluating JNDI, you can easily change the permissions so that all users have write permission. Find the slapd configuration file (by default, this is /usr/local/etc/openladap/slapd.conf) and replace the following line:

access to * by * read

with

access to * by * write

Stop and restart the slapd server for this change to take effect.

You can manipulate attributes in one of two ways. The first, and most functional, is to create an array of javax.naming.directory.ModificationItem objects. Each entry in the array specifies an attribute ID and an operation (one of DirContext.REPLACE_ATTRIBUTE, DirContext.ADD_ATTRIBUTE, and DirContext.REMOVE_ATTRIBUTE). To modify or add a new attribute, the ModifyAttributes() method requires an additional parameter for the value of the attribute.

Listing 3.14 shows how the entry for Abraham can update the description attribute and add a new seeAlso attribute.

Listing 3.14. Full Text of JNDIModify.java
 1: import javax.naming.*;
 2: import javax.naming.directory.*;
 3: public class JNDIModify
 4: {
 5:     private final static String JNDI = "cn=Abraham,ou=Customers,o=Agency,c=us";
 6:
 7:     public static void main(String[] args) {
 8:         try {
 9:             DirContext ctx = new InitialDirContext();
10:             SearchControls sc = new SearchControls();
11:             ModificationItem[] mods = new ModificationItem[2];
12:             mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE,
13:                 new BasicAttribute("description", "Assasinated President"));
14:             mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE,
15:                 new BasicAttribute("seeAlso", "cn=George,ou=Customers,o=Agency,c=us"));
16:             ctx.modifyAttributes(JNDI, mods);
17:         }
18:         catch (NamingException ex) {
19:             System.out.println (ex);
20:             System.exit(1);
21:         }
22:     }
23: }
						

After running this program, you can view the changes by using the JNDIAttributes program shown in listing 3.11.

> java JNDIAttributes "cn=abraham,ou=Customers,o=Agency,c=us"
  attribute: seeAlso
    value: cn=George,ou=Customers,o=Agency,c=us
  attribute: description
    value: Assasinated President
  attribute: objectClass
    value: top
    value: person
  attribute: sn
    value: Lincoln
  attribute: cn
    value: Abraham
    value: Abraham Lincoln
END of attributes for cn=abraham,ou=Customers,o=Agency,c=us

The second method for manipulating attributes is to define the operation and an Attributes list of Attribute objects to be manipulated (with values if appropriate). The next code fragment shows how to delete the seeAlso entry just added (which probably should have referred to John F. Kennedy and not George Washington).

Attributes attrs = new BasicAttributes("seeAlso",null);
ctx.modifyAttributes(JNDI, DirContext.REMOVE_ATTRIBUTE, attrs);

The next example shows how to change the description back to President.

Attributes attrs = new BasicAttributes("description","President");
ctx.modifyAttributes(JNDI, DirContext.REPLACE_ATTRIBUTE, attrs);

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.37.250