View
113
Download
3
Category
Tags:
Preview:
DESCRIPTION
For programmers who are already familiar with JNDI and LDAP basics, but wonder how to mix all those ingredients together into collaborative directory-enabled JEE solutions. http://kenlin.com
Citation preview
Lotus Software - Messaging & Collaboration
Confidential | June 2, 2003 | LSM Seminar 2003 © 2002 IBM Corporation
From Now To Next
Ken LinSenior Software Engineerklin@us.ibm.com
Collaborative Cuisine's1 Hour JNDI Cookbook
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
Goals
Review common directory-related collaboration scenariosExamine solutions through algorithms and JNDI codeRefine and improve solutions
Lotus Software - Messaging & Collaboration
Confidential | June 2, 2003 | LSM Seminar 2003 © 2002 IBM Corporation
From Now To Next
StartersMain CoursesDessertsTidy Up
Today's Menu
Lotus Software - Messaging & Collaboration
Confidential | June 2, 2003 | LSM Seminar 2003 © 2002 IBM Corporation
From Now To Next
Locate a Directory ServerResolve and Authenticate a User
Starters
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
Locate a Directory Server
Properties FileConfigured by each application administratorApplication-dependent solutionEasy to code via java.util.PropertiesManageable when few applications
RFC 2782 - A DNS RR for specifying the location of services (DNS SRV)
Configured centrally by DNS administratorApplication-independent solutionEasy to code via JNDIManageable even when many applicationsRecognized by com.sun.jndi.ldap.LdapCtxFactory inJDK 1.4.1
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DNS SRV (RFC 2782)w32 [C:\] \winnt\system32\nslookupDefault Server: ns1.iris.comAddress: 9.95.aaa.bbb
> set q=srv> _ldap._tcp.iris.com you type this
0=highest65535=lowest
Server: ns1.iris.comAddress: 9.95.aaa.bbb
_ldap._tcp.iris.com SRV service location: priority = 0 weight = 0 port = 389 svr hostname = clapton.iris.com_ldap._tcp.iris.com SRV service location: priority = 100 weight = 0 port = 389 svr hostname = little-village.iris.comclapton.iris.com internet address = 9.95.ccc.dddlittle-village.iris.com internet address = 9.95.eee.fff>^Z
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirServer.getServers returns host:port stringspublic static Vector getServers(String domain) throws NamingException {
Vector servers = new Vector();
Hashtable env = new Hashtable();env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
DirContext ctx = new InitialDirContext(env);
Attributes attrs = ctx.getAttributes("_ldap._tcp." + domain, new String[] {"SRV"});Attribute srv = attrs.get("SRV");if (srv != null) {
NamingEnumeration results = srv.getAll();if (results.hasMore()) {
while (results.hasMore()) {String result = (String)results.next();StringTokenizer tokens = new StringTokenizer(result, " ");String priority = tokens.nextToken();String weight = tokens.nextToken();String port = tokens.nextToken();String target = tokens.nextToken();servers.add(target + ":" + port);
}}
}return servers;
}
J2SE 1.4.1 or higher
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
Resolve and Authenticate Users
How do we code applications to authenticate using "friendly" names?
cn=ken lin,ou=westford,o=ibm is not very friendly!
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
Resolve and Authenticate Users AlgorithmDoes the supplied name look like an LDAP DN?
Yes: ask the LDAP server if DN exists?Yes: go to Authenticated BindNo: go to Resolve DN
No: go to Resolve LDAP DNResolve supplied name to LDAP DN
Unauthenticated search the name to obtain its DN (&(objectclass=person)(|(uid={0})(mail={0})(cn={0})))Allow the filter to be customized to fit customer (&(objectclass=person)(telephonenumber={0}))If number of search hits is ...
0: name not found1: go to Authenticated Bindotherwise: ambiguous DN
Authenticated BindUse LDAP DN and supplied password
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirUser.authenticate to Authenticate and Resolve a Nameboolean authenticate(String hostport, String anyname, String password) throws
NamingException{
boolean authenticated = false;DirContext ctx = null;try {
String dn = resolve(anyname);if (dn != null) {
Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, m_ServiceProvider);
env.put(Context.PROVIDER_URL, "ldap://" + hostport);
env.put(Context.SECURITY_AUTHENTICATION, "simple");env.put(Context.SECURITY_PRINCIPAL, dn);env.put(Context.SECURITY_CREDENTIALS, password);
ctx = new InitialDirContext(env);authenticated = true;
}} catch (AuthenticationException e) {
authenticated = false;} finally {
try {if (ctx != null) ctx.close();
} catch (Exception e) {}
}return authenticated;
}
"com.sun.jndi.ldap.LdapCtxFactory"
local DirContext for dn/password
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirUser.resolve to Get a DN from anynameString resolve(String anyname) throws NamingException{
// If anyname looks like it might be a DN, try a base searchif (anyname.indexOf("=") != -1) {
try {Attributes attrs = m_Ctx.getAttributes(anyname);return anyname;
} catch (NameNotFoundException e) {// not a valid or known DN, try filtered search
}}
// anyname must not look like a DN, search for m_FilterPattern with anynameObject args[] = {anyname};String filter = java.text.MessageFormat.format(m_FilterPattern, args);
SearchControls ctls = new SearchControls();ctls.setReturningAttributes(null);ctls.setCountLimit(1);ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration results = m_Ctx.search(m_Base, filter, ctls);if (results.hasMore()) {
SearchResult result = (SearchResult)results.next();return result.getName();
} else {throw new NameNotFoundException("Neither DN nor filter hits");
}}
"(&(objectclass=person)(|(uid={0})(mail={0})(cn={0})))"
DirContext for this application
some vendors require base
so more than 1 result causesSizeLimitExceededException
Lotus Software - Messaging & Collaboration
Confidential | June 2, 2003 | LSM Seminar 2003 © 2002 IBM Corporation
From Now To Next
Find a Member's GroupFind a Group's Members
Main Courses
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
Directory Groups
IETF, Defacto, and Vendor Group DefinitionsDefinitions for Static Groups in IETF RFC 2782
groupOfNames / membergroupOfUniqueNames / uniqueMember
Group operations are coded by the application, not the LDAP server!
Applications must understand schemaApplications must code findMembersApplications must code findGroups
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
dgservlet Demo
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
Two Common Uses for findMembers and findGroupsfindMembers can be used to expand and flatten out a buddy list
findGroups can be used to create Notes-like NamesLists for authorization
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirGroups Classpublic class DirGroups{
protected DirContext m_Ctx = null;
protected NameParser m_Parser = null;protected String m_GroupObjectClasses[] = {"groupOfNames"};protected String m_MemberNames[] = {"member"};protected String m_Base = "";protected String m_MemberFilter = "(&(objectclass=groupOfNames)(member={0}))";protected int m_FindMembersDepth = 10;protected String m_MemberAttrIds[] = {"objectclass", "member", "cn"};
public DirGroups(DirContext ctx) {m_Ctx = ctx;
}
public void findGroups(String memberDN, Hashtable groups) throws NamingException{ described later... }
public void findMembers(Name groupDN, Hashtable groups,Hashtable users, Hashtable unknown) throws NamingException
{ described later... }
etc...}
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
findMembers Algorithm
get entry g from groupDN and verify it is a groupforeach memberDN in g { if memberDN isn't in groups, users, or unknown { get entry m from memberDN if m is a group --> add m to groups and recurse... findMembers(memberDN, groups, users, unknown) if m is a non-group --> add m to users if m's objectclass is unknown --> add m to unknown }}
findMembers(in String groupDN, inout Hashtable groups, inout Hashtable users, inout Hashtable unknown)
Optimize!
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirGroups.findMemberspublic void findMembers(Name groupDN,
Hashtable groups,Hashtable users,Hashtable unknown)
throws NamingException{
// Verify argumentsif (groupDN == null ||
groups == null || users == null || unknown == null){
return;}
// Verify the groupDN is really a groupAttributes attrs = m_Ctx.getAttributes(groupDN, m_MemberAttrIds);if (!isGroup(attrs)) {
throw new NamingException("'objectclass' is not a group");}
findMembers(attrs, m_FindMembersDepth, groups, users, unknown);}
DirContext for this DirGroups object
10
{"objectclass", "member", "cn"}
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirGroups.findMembersprivate void findMembers(Attributes attrs, int depth, Hashtable groups,
Hashtable users,Hashtable unknown) throws NamingException {if (depth <= 0) { return; }for (int i = 0; i < m_MemberNames.length; i++) {
Attribute attr = attrs.get(m_MemberNames[i]);if (attr != null) {
NamingEnumeration members = attr.getAll();while (members.hasMore()) {
Name memberDN = toName((String)members.nextElement());// Process memberDN only if it hasn't yet been encounterredif (!users.containsKey(memberDN) && !groups.containsKey(memberDN) &&
!unknown.containsKey(memberDN)) {try {
if (isGroup(memberDN)) { // memberDN is a groupattrs = m_Ctx.getAttributes(memberDN, m_MemberAttrIds);if (groups.put(memberDN, attrs) == null) {
findMembers(attrs, depth-1, groups, users, unknown);}
} else { // memberDN is a non-groupusers.put(memberDN, memberDN/*null*/);
}} catch (NamingException e) { // memberDN is unknown
unknown.put(memberDN, memberDN/*null*/);}
}} // while
}} // for
}
{"objectclass","member", "cn"}
{"member"}
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirGroups.isGroupprivate boolean isGroup(Name name)
throws NamingException{
if (m_GroupObjectClasses.length == 1) {return compare(name, "objectclass", m_GroupObjectClasses[0]);
} else {Attributes attrs = m_Ctx.getAttributes(name, m_MemberAttrIds);return isGroup(attrs);
}}
private boolean isGroup(Attributes attrs)throws NamingException
{Attribute attr = attrs.get("objectclass");if (attr != null) {
for (int i = 0; i < m_GroupObjectClasses.length; i++) {if (attr.contains(m_GroupObjectClasses[i])) {
return true;}
}return false;
} else {throw new NamingException("'objectclass' attribute not found");
}}
LDAP compare is faster than search
{"groupOfNames"}
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirGroups.compareprivate boolean compare(Name name, String attr, String value)
throws NamingException{
SearchControls ctls = new SearchControls();ctls.setSearchScope(SearchControls.OBJECT_SCOPE);ctls.setReturningAttributes(new String[0]); // no attributes
// LDAP compare operationNamingEnumeration results = m_Ctx.search(name, "(" + attr + "=" + value + ")",
ctls);return results.hasMore(); // if successful, results will contain a single item
}
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
findGroups AlgorithmfindGroups(in String memberDN, inout Hashtable groups)
g = DNs of groups that memberDNis a direct member
tovisit = gwhile tovisit is not empty { pop any groupDN from tovisit if groupDN is not in groups { groups += groupDN g = DNs of groups that groupDN
is a direct member of tovisit += elements of g not in
tovisit or groups }}
1. cn=bugs bunny,o=toons empty
2. cn=looney toons
3. cn=looney toons
4. cn=looney toons empty
5. cn=looney toons
6. cn=all toons
7. cn=all toons
8. cn=all toons
9. cn=all toons empty
10. cn=looney toons cn=all toons11. empty
12. empty
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirGroups.findGroupspublic void findGroups(Name memberDN,
Hashtable groups)throws NamingException
{Hashtable tovisit = new Hashtable(); // DNs of groups to visit
// Initialize tovisit to groups that memberDN is a direct member of.putDirectGroups(memberDN, tovisit, groups);
// groups = union(groups, tovisit)while (!tovisit.isEmpty()) {
// Pop groupDN from tovisitName groupDN = (Name)tovisit.keys().nextElement();tovisit.remove(groupDN);
// Add groupDN to groupsif (groups.put(groupDN, groupDN) == null) {
// Wasn't previously processed, search what groups it is a// member of.try {
putDirectGroups(groupDN, tovisit, groups);} catch (NamingException e) {
e.printStackTrace();}
} else {// Was processed previously, don't do anything
}}
}
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirGroups.putDirectGroupsprivate void putDirectGroups(Name memberDN,
Hashtable tovisit, Hashtable groups)
throws NamingException{
// Find in results the groups that memberDN is a direct member of.NamingEnumeration results = findDirectGroups(memberDN);
// Put each group in tovisit if it doesn't already exist in tovisit or// groups.while (results.hasMore()) {
SearchResult result = (SearchResult)results.next();Name groupDN = toName(result.getName());if (!groups.containsKey(groupDN) && !tovisit.contains(groupDN)) {
tovisit.put(groupDN, groupDN);}
}}
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirGroups.findDirectGroupsprivate NamingEnumeration findDirectGroups(Name memberDN)
throws NamingException{
Object args[] = {memberDN};String filter = java.text.MessageFormat.format(m_MemberFilter, args);
SearchControls ctls = new SearchControls();String[] attrIDs = {};ctls.setReturningAttributes(attrIDs);ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration results = m_Ctx.search(m_Base, filter, ctls);return results;
}
"(&(objectclass=groupOfNames) (member={0}))"
some vendors require base
DirContext for this DirGroups object
Lotus Software - Messaging & Collaboration
Confidential | June 2, 2003 | LSM Seminar 2003 © 2002 IBM Corporation
From Now To Next
Domino LDAP Server TipsClient-side LDAP ImprovementsTwo and Three-Tier Architectures
Desserts
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
DirUser.m_FilterPattern = "(&(objectclass=person)(|(uid={0})(mail={0})(cn={0})))"
More "|" terms increases search timeFT Index Domino Directory if term cannot be serviced by view search
DirGroups.m_GroupObjectClasses = {"groupOfNames"}Uses slightly more efficient LDAP compare instead LDAP searchUse {"groupOfUniqueNames"} when running against Netscape onlyUse {"groupOfNames", "groupOfUniqueNames"} when running against LDAP servers from various vendors
Domino 6 LDAP Query-Results CacheDomino 6.02 LDAP improves DirGroups.findMembers response for attributes with hundreds of valuesUse LDAPDEBUG=1 in Notes.ini to trace LDAP server
Domino LDAP Server Tips
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
Client-side LDAP Improvements
Eliminate trips to LDAP server for duplicated group queriesfindMembers("cn=Iris Directory Team", ...)findGroups("cn=ken lin,ou=westford,o=ibm", ...)
Reduce trips to LDAP server for "similar" group queriesfindMembers("cn=Iris Directory Team", ...) vs.findMembers("cn=LDAP Server Dev', ...)findGroups("cn=ken lin,ou=westford,o=ibm") -> 104 groups vs.findGroups("cn=scott m davidson,ou=westford,o=ibm") -> 135 groups
Eliminate trips to LDAP server for duplicated name resolution queriesTBD - improve DirGroups.isGroup by consulting cached "objectclass=groupOfNames" results (FT Index)TBD - cache LDAP bind requests
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
InitialDirContextCache
Extends javax.naming.InitialDirContextOverrides and caches DirContext's getAttributes and search
DirGroupsor other
JNDI calls
Initial-DirContext-
Cache
Initial DirContext
LDAP ServerApp
Internally, query-results caches are implemented via HashtablesName.compareTo() is important for Hashtable entry comparisonNot a single line of DirGroups or your JNDI calls was changed to take advantage of caching!
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
InitialDirContextCache.getAttributespublic Attributes getAttributes(Name name, String[] attrIds) throws NamingException{
String key = toKey(name, attrIds);Object cached = m_AttributesCache.get(key);Attributes attrs = null;
if (cached == null) {try {
attrs = super.getAttributes(name, attrIds);if (attrs != null) {
Attributes value = (Attributes)attrs.clone();m_AttributesCache.put(key, value);
}} catch (NamingException e) {
m_AttributesCache.put(key, e.toString());throw e;
}} else {
if (cached instanceof String) {throw new NamingException(cached.toString());
} else {attrs = (Attributes)cached;
}}
return attrs;}
cache implemented via Hashtable
performs the real LDAP call
makes a 2-tuple from name/attrIDs
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
Two and Three-Tier Architectures
Two-Tier Architecture
LDAPServer
JNDI orDirGroupsApp
JNDI orDirGroupsApp
JNDI orDirGroupsApp
JNDI orDirGroups
AppBack
AppFront
AppFront
AppFront
LDAPServer
Three-Tier Architecture
e.g., Domino DA, Sametime, J2EE, dgservlet
e.g., Mail clients
Lotus Software - Messaging & Collaboration
Confidential | June 2, 2003 | LSM Seminar 2003 © 2002 IBM Corporation
From Now To Next
ReviewTomorrowQuestions
Tidy Up
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
Review
Algorithms and CodeLocate a Directory Server DirServer.getServers()Resolve and Authenticate a userDirUser.authenticate()Find a Group's Members DirGroups.findMembers()Find a Member's Groups DirGroups.findGroups()
Cache Query Results InitialDirContextCache
Improvements
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
Tomorrow
Run nslookup, look for SRVsDownload seminar samplesRun DirServer.main()Run DirUser.main()Install and run dgservletTry "real" directoriesExperiment!
Lotus Software - Messaging and Collaboration
© 2002 IBM CorporationCollaborative Cuisine's 1 Hour JNDI Cookbook | LSM Seminar 2003
ReferencesCollaborative Cuisine's 1 Hour JNDI Cookbook - LSM 2003 https://www-914.ibm.com/events/lsm/03lsm.nsfBuilding Directory Friendly Applications - LSM 2002 https://www-914.ibm.com/events/lsm/02lsm.nsf/lookupwebpage/AbsPresAppDevJNDI Tutorial - Tips for LDAP Users http://java.sun.com/products/jndi/tutorial/ldap/index.html
Java Servlet Technology http://java.sun.com/j2ee/tutorial/1_3-fcs/doc/Servlets.htmlEclipse - an open extensible IDE http://eclipse.orgDirectory Services Markup Language http://www.oasis-open.org/cover/dsml.html
The String Representation of LDAP Search Filters http://www.ietf.org/rfc/rfc2254.txtA DNS RR for specifying the location of services (DNS SRV) http://www.ietf.org/rfc/rfc2782.txt http://www.ietf.org/internet-drafts/draft-ietf-ldapext-locate-08.txtA Summary of the X.500(96) User Schema for use with LDAPv3 http://www.ietf.org/rfc/rfc2256.txt
Misc.
LDAP
JNDI
Lotus Software - Messaging & Collaboration
Confidential | June 2, 2003 | LSM Seminar 2003 © 2002 IBM Corporation
From Now To Next
Questions?
Recommended