001: /* Copyright 2001, 2004 The JA-SIG Collaborative. All rights reserved.
002: * See license distributed with this file and
003: * available online at http://www.uportal.org/license.html
004: */
005:
006: package org.jasig.portal.ldap;
007:
008: import java.util.Hashtable;
009:
010: import javax.naming.Context;
011: import javax.naming.NamingException;
012: import javax.naming.directory.DirContext;
013: import javax.naming.directory.InitialDirContext;
014:
015: import org.apache.commons.logging.Log;
016: import org.apache.commons.logging.LogFactory;
017:
018: /**
019: * Default implementation of ILdapServer.
020: * @author edalquist@unicon.net
021: * @author andrew.petro@yale.edu
022: * @version $Revision: 35070 $ $Date: 2004-11-17 12:09:46 -0700 (Wed, 17 Nov 2004) $
023: */
024: public class LdapServerImpl implements ILdapServer {
025:
026: private Log log = LogFactory.getLog(getClass());
027:
028: /**
029: * Name of the class that is the default context factory which will be used
030: * if not overridden.
031: */
032: private static final String DEFAULT_CXT_FACTORY = "com.sun.jndi.ldap.LdapCtxFactory";
033:
034: /**
035: * Name of this LDAP server for identification and debugging purposes.
036: */
037: private final String ldapName;
038:
039: private final String ldapBaseDn;
040:
041: /**
042: * The attribute by which we query using uids.
043: */
044: private final String ldapUidAttribute;
045:
046: /**
047: * Environment, populated at construction.
048: */
049: private Hashtable env = new Hashtable(5, 0.75f);
050:
051: /**
052: * Instantiate an LdapServerImpl using somewhat abtstracted configuration
053: * over that present in the more detailed constructor.
054: * You communicate a desire to use SSL to this method by using an
055: * ldaps//... URL rather than an ldap//... url.
056: * @param name mnemonic name for this Ldap server instance
057: * @param url URL of LDAP server
058: * @param baseDn
059: * @param uidAttribute attribute against which to match for uid queries
060: * @param managerDn principal for LDAP authentication, null implies no
061: * authentication
062: * @param managerPw password for LDAP authentication, null implies no
063: * authentication
064: * @param initialContextFactory the name of the class to use to instantiate
065: * the context.
066: */
067: public LdapServerImpl(String name, String url, String baseDn,
068: String uidAttribute, String managerDn, String managerPw,
069: String initialContextFactory) {
070:
071: if (name == null)
072: throw new IllegalArgumentException("name cannot be null.");
073:
074: this .ldapName = name;
075:
076: this .ldapBaseDn = checkNull(baseDn, "");
077: this .ldapUidAttribute = checkNull(uidAttribute, "");
078:
079: managerDn = checkNull(managerDn, "");
080: managerPw = checkNull(managerPw, "");
081: initialContextFactory = checkNull(initialContextFactory,
082: DEFAULT_CXT_FACTORY);
083:
084: // parse the URL parameter to detect SSL -- in case of SSL
085: // fix the URL and set the useSsl flag.
086:
087: if (url.startsWith("ldaps")) { // Handle SSL connections
088: // remove the 's' from "ldaps"
089: url = "ldap" + url.substring(5);
090: this .env.put(Context.SECURITY_PROTOCOL, "ssl");
091: }
092:
093: this .env.put(Context.INITIAL_CONTEXT_FACTORY,
094: initialContextFactory);
095:
096: this .env.put(Context.PROVIDER_URL, url);
097: this .env.put(Context.SECURITY_AUTHENTICATION, "simple");
098: this .env.put(Context.SECURITY_PRINCIPAL, managerDn);
099: this .env.put(Context.SECURITY_CREDENTIALS, managerPw);
100:
101: if (log.isDebugEnabled()) {
102: log.debug("Instantiated: [" + this + "]");
103: }
104: }
105:
106: /**
107: * Instantiate an LdapServerImpl with the given low-level configuration.
108: * Using this constructor has the advantage of doing some argument checking
109: * on the port number.
110: * @param name mnemonic name for this Ldap server instance
111: * @param host host of the LDAP server
112: * @param port port number. null implies default of 389
113: * @param baseDn
114: * @param uidAttribute attribute against which to match for uid queries
115: * @param managerDn principal for LDAP authentication, null implies no
116: * authentication
117: * @param managerPw password for LDAP authentication
118: * @param useSsl true if we should use SSL, false otherwise.
119: * @param initialContextFactory name of the class to use for building init context,
120: * null defaults to com.sun.jndi.ldap.LdapCtxFactory
121: * @throws IllegalArgumentException when arguments do not specify valid server
122: */
123: public LdapServerImpl(String name, String host, String port,
124: String baseDn, String uidAttribute, String managerDn,
125: String managerPw, boolean useSsl,
126: String initialContextFactory) {
127:
128: this (name, constructLdapUrl(host, port, useSsl), baseDn,
129: uidAttribute, managerDn, managerPw,
130: initialContextFactory);
131: }
132:
133: /**
134: * Construct a URL for an LDAP server.
135: * @param host
136: * @param port
137: * @param useSsl
138: * @return URL constructed from the arguments
139: */
140: private static String constructLdapUrl(String host, String port,
141: boolean useSsl) {
142:
143: if (host == null)
144: throw new IllegalArgumentException("host cannot be null.");
145:
146: port = checkNull(port, "389");
147:
148: int portNum;
149: try {
150: portNum = Integer.parseInt(port);
151: } catch (NumberFormatException nfe) {
152: throw new IllegalArgumentException(
153: "port, if specified, must be a "
154: + "positive integer. It had invalid value ["
155: + port + "]");
156: }
157:
158: if (portNum < 1)
159: throw new IllegalArgumentException(
160: "port, if specified, must be "
161: + "positive. It had invalid value "
162: + portNum);
163:
164: StringBuffer urlBuffer = new StringBuffer();
165: if (useSsl) {
166: urlBuffer.append("ldaps://");
167: } else {
168: urlBuffer.append("ldap://");
169: }
170:
171: urlBuffer.append(host).append(":").append(port);
172:
173: return urlBuffer.toString();
174: }
175:
176: /**
177: * Returns the chckStr argument unless the chkStr argument is null,
178: * in which case returns the defStr (default) argument.
179: * @param chkStr - String to check
180: * @param defStr - fallback default for when the string to check was null
181: * @return chkStr if not null, defStr if chkStr was null
182: */
183: private static String checkNull(String chkStr, String defStr) {
184: if (chkStr == null)
185: return defStr;
186: return chkStr;
187: }
188:
189: public DirContext getConnection() throws NamingException {
190: return new InitialDirContext(this .env);
191: }
192:
193: public String getBaseDN() {
194: return this .ldapBaseDn;
195: }
196:
197: public String getUidAttribute() {
198: return this .ldapUidAttribute;
199: }
200:
201: public void releaseConnection(DirContext conn) {
202: if (conn == null)
203: return;
204:
205: try {
206: conn.close();
207: } catch (Exception e) {
208: log.debug("Error closing the LDAP Connection to server ["
209: + this + "]", e);
210: }
211: }
212:
213: public String toString() {
214: StringBuffer sb = new StringBuffer();
215: sb.append(getClass().getName());
216: sb.append(" name=").append(this .ldapName);
217: sb.append(" url=").append(this .env.get(Context.PROVIDER_URL));
218: sb.append(" uidAttr=").append(this .ldapUidAttribute);
219: sb.append(" baseDn=").append(this .ldapBaseDn);
220: if ("ssl".equals(this .env.get(Context.SECURITY_PROTOCOL)))
221: sb.append(" using SSL");
222: if (this .env.get(Context.SECURITY_PRINCIPAL) != null)
223: sb.append(" authentication principal=").append(
224: this .env.get(Context.SECURITY_PRINCIPAL));
225: if (this .env.get(Context.SECURITY_CREDENTIALS) != null)
226: sb.append(" password=").append(
227: this .env.get(Context.SECURITY_CREDENTIALS));
228: if (!DEFAULT_CXT_FACTORY.equals(this .env
229: .get(Context.INITIAL_CONTEXT_FACTORY)))
230: sb.append(" initialContextFactory=").append(
231: this.env.get(Context.INITIAL_CONTEXT_FACTORY));
232: return sb.toString();
233: }
234: }
|