001: package org.esupportail.cas.server.handlers.ldap;
002:
003: import java.util.Hashtable;
004:
005: import javax.naming.Context;
006: import javax.naming.AuthenticationException;
007: import javax.naming.NamingException;
008: import javax.naming.directory.DirContext;
009: import javax.naming.directory.InitialDirContext;
010:
011: import org.dom4j.Element;
012: import org.esupportail.cas.server.util.log.Log;
013: import org.esupportail.cas.server.util.RedundantHandler;
014: import org.esupportail.cas.server.util.Server;
015:
016: /**
017: * This abstract class implements an LDAP server class, inherited by
018: * BindLdapServer and FastBindLdapServer.
019: *
020: * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
021: * @author Jean-Baptiste Daniel <danielj at sourceforge.net>
022: */
023: public abstract class LdapServer extends Server {
024:
025: /**
026: * true for a secure access to the LDAP directory, false otherwise.
027: */
028: private boolean secured;
029:
030: /**
031: * the URL of the LDAP directory.
032: */
033: private String url;
034:
035: /**
036: * Constructor.
037: *
038: * @param handlerDebug debugging mode of the handler
039: * @param handler the handler the server will be used by
040: * @param serverElement the XML element that declares the server
041: * @throws Exception Exception
042: */
043: public LdapServer(final Boolean handlerDebug,
044: final RedundantHandler handler, final Element serverElement)
045: throws Exception {
046: super (handlerDebug, handler, serverElement);
047: traceBegin();
048:
049: url = getServerSubElementContent(serverElement, "url", true/*needed*/);
050: trace("url = " + url);
051:
052: secured = url.substring(0, 5).equals("ldaps");
053: trace("secured = " + secured);
054:
055: traceEnd();
056: }
057:
058: /**
059: * A String array used to store input tokens (to be replaced).
060: */
061: private static final String[] INPUT_TOKENS = { "%u", "%U", "%d",
062: "%1", "%2", "%3", "%4", "%5", "%6", "%7", "%8", "%9" };
063:
064: /**
065: * Take a filter (from CASGenericHandler configuration) and a
066: * username, and replace tokens in the filter by their equivalents
067: * in the username. The rules to replace tokens are the following ones:
068: * - %%: %
069: * - %u: user
070: * - %U: user portion of %u (%U = test when %u = test@domain.tld)
071: * - %d: domain portion of %u (%d = domain.tld when %u = test@domain.tld)
072: * - %1-9: domain tokens (%1 = tld, %2 = domain when %d = domain.tld)
073: *
074: * @param username a username (such as test@domain.com)
075: * @param filter a filter string containing tokens to be replaced
076: *
077: * @return a string corresponding to the input filter, where the
078: * predefined tokens have been replaced by their equivalents.
079: */
080: protected final String replaceTokens(final String filter,
081: final String username) {
082: traceBegin();
083:
084: /* A String array used to store output tokens (to replace
085: * input tokens in the replaceTokens() method). */
086: String[] outputTokens = { "", "", "", "", "", "", "", "", "",
087: "", "", "", "" };
088:
089: /* -------------------------------------------------------------
090: * Analyze the username given by the user and deduce the output
091: * tokens (which will replace the input tokens when calling
092: * replaceTokens()).
093: * ------------------------------------------------------------- */
094:
095: // the complete username corresponds to '%u', store it
096: outputTokens[0] = username;
097:
098: // at first cut the username into two parts, seperated by a '@'
099: String[] userDomain = username.split("@");
100: // the first cell of userDomain corresponds to '%U', store it
101: outputTokens[1] = userDomain[0];
102:
103: // if a '@' it present,
104: if (userDomain.length > 1) {
105:
106: //the second cell corresponds to '%d', store it
107: outputTokens[2] = userDomain[1];
108:
109: // now look at the '%n' tokens. Split the domain name into pieces
110: String[] dcArray = userDomain[1].split("\\.");
111:
112: // iterate on the array to fill the end of _outputTokens
113: for (int i = 0; i < dcArray.length; i++) {
114: outputTokens[i + 3] = dcArray[dcArray.length - 1 - i];
115: }
116: }
117:
118: /* -------------------------------------------------------------
119: * Replace the input tokens by their corresponding parts.
120: * ------------------------------------------------------------- */
121: StringBuffer result = new StringBuffer("");
122:
123: // at first split _username into parts separated by "%%"
124: String[] parts = filter.split("%%");
125:
126: // for each part, replace the tokens and concatenate to result
127: for (int i = 0; i < parts.length; i++) {
128: if (i != 0) {
129: result.append("%");
130: }
131: for (int j = 0; j < INPUT_TOKENS.length; j++) {
132: parts[i] = parts[i].replaceAll(INPUT_TOKENS[j],
133: outputTokens[j]);
134: }
135: result.append(parts[i]);
136: }
137: traceEnd(result.toString());
138: return result.toString();
139: }
140:
141: /**
142: * Connect to the LDAP server using specified username and password.
143: *
144: * @param bindDn the DN to use for the connection
145: * @param bindPassword the associated password
146: *
147: * @return a Connection object on success, null on error. When the function
148: * returns false, the error code can be retrieved with the connectError()
149: * method.
150: */
151: protected final DirContext connect(final String bindDn,
152: final String bindPassword) {
153: DirContext connection = null;
154: traceBegin();
155:
156: try {
157: Hashtable hashtable = new Hashtable(5, 0.75f);
158: hashtable.put(Context.INITIAL_CONTEXT_FACTORY,
159: "com.sun.jndi.ldap.LdapCtxFactory");
160: hashtable.put(Context.PROVIDER_URL, url);
161: hashtable.put(Context.SECURITY_AUTHENTICATION, "simple");
162: hashtable.put(Context.SECURITY_PRINCIPAL, bindDn);
163: hashtable.put(Context.SECURITY_CREDENTIALS, bindPassword
164: .getBytes());
165: if (secured) {
166: hashtable.put(Context.SECURITY_PROTOCOL, "ssl");
167: }
168: trace("Connecting to the LDAP directory (url=`" + url
169: + "', username=`" + bindDn + "')...");
170: connection = new InitialDirContext(hashtable);
171: setConnectError(CONNECT_SUCCESS);
172: trace("Connection succeeded.");
173: traceEnd("ok");
174: return connection;
175: } catch (AuthenticationException e) {
176: trace("Connection failed: " + e.getMessage());
177: setConnectError(CONNECT_NOAUTH);
178: } catch (NamingException e) {
179: Log.warn("Could not connect to \"" + url + "\"");
180: trace("Connection failed: '" + e.getMessage() + "')");
181: setConnectError(CONNECT_FAILURE);
182: }
183:
184: // connection failed, but try to close the connection however
185: if (connection != null) {
186: try {
187: trace("Closing LDAP connection...");
188: connection.close();
189: } catch (NamingException e2) {
190: Log.warn("Could not close LDAP connection.");
191: }
192: }
193:
194: traceEnd("null");
195: return null;
196: }
197:
198: /**
199: * Connect to the LDAP server using specified username and password
200: * and immediatly close the connection. The error code can be retrieved
201: * with the connectError() method.
202: *
203: * @param bindDn the DN to use for the connection
204: * @param bindPassword the associated password
205: *
206: * @return true if the connection succeeded, false otherwise.
207: */
208: protected final boolean connectAndClose(final String bindDn,
209: final String bindPassword) {
210: traceBegin();
211:
212: DirContext connection = connect(bindDn, bindPassword);
213: if (connection != null) {
214: try {
215: trace("Closing LDAP connection...");
216: connection.close();
217: return true;
218: } catch (NamingException e) {
219: Log.warn("Could not close LDAP connection.");
220: return false;
221: }
222: } else {
223: return false;
224: }
225: }
226: }
|