/* * LDAPTemplate.java * * Brazil project web application toolkit, * export version: 2.3 * Copyright (c) 2001-2008 Sun Microsystems, Inc. * * Sun Public License Notice * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is included as the file "license.terms", * and also available at http://www.sun.com/ * * The Original Code is from: * Brazil project web application toolkit release 2.3. * The Initial Developer of the Original Code is: cstevens. * Portions created by cstevens are Copyright (C) Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): cstevens, suhler. * * Version: 2.4 * Created by cstevens on 01/01/11 * Last modified by suhler on 08/02/04 13:49:02 * * Version Histories: * * 2.4 08/02/04-13:49:02 (suhler) * - add timeout option * - add ldap passwords readable from a file or stdin * * 2.3 04/11/30-15:19:39 (suhler) * fixed sccs version string * * 2.2 04/01/27-17:19:51 (suhler) * Fixed limit=nn so it works now * * 2.1 02/10/01-16:39:12 (suhler) * version change * * 1.9 01/09/13-09:24:39 (suhler) * remove uneeded import * * 1.8 01/07/16-16:50:04 (suhler) * use new Template convenience methods * * 1.7 01/06/04-11:06:23 (suhler) * added debug option * * 1.6 01/03/23-14:28:44 (cstevens) * NullPointerException when storing LDAP information in hash table. * Theory: looking up a non-existent employee returns null. * Practice: it seems a non-null entry can be returned, but all the fields * in the entry will be null. * * 1.5 01/02/13-13:41:55 (cstevens) * Merged changes between child workspace "/home/cstevens/ws/brazil/naws" and * parent workspace "/export/ws/brazil/naws". * * 1.3.1.1 01/02/12-15:57:39 (cstevens) * Resources: explicitly release LDAP connection rather than waiting for GC to * get it, or can run out of file descriptors. * * 1.4 01/01/14-14:54:40 (suhler) * doc fixes * * 1.3 01/01/12-16:54:16 (cstevens) * move LDAPTemplate to sunlabs.brazil.ldap package. * * 1.2 01/01/12-08:24:26 (suhler) * removed wildcarded imports * * 1.2 01/01/11-17:20:21 (Codemgr) * SunPro Code Manager data about conflicts, renames, etc... * Name history : 2 1 ldap/LDAPTemplate.java * Name history : 1 0 handlers/templates/LDAPTemplate.java * * 1.1 01/01/11-17:20:20 (cstevens) * date and time created 01/01/11 17:20:20 by cstevens * */ package sunlabs.brazil.ldap; import java.util.Enumeration; import java.util.Properties; import java.util.StringTokenizer; import sunlabs.brazil.server.Server; import sunlabs.brazil.util.http.HttpInputStream; import java.io.IOException; import java.io.FileInputStream; import sunlabs.brazil.template.Template; import sunlabs.brazil.template.RewriteContext; import netscape.ldap.LDAPAttribute; import netscape.ldap.LDAPConnection; import netscape.ldap.LDAPEntry; import netscape.ldap.LDAPException; // import netscape.ldap.LDAPSearchConstraints; import netscape.ldap.LDAPSearchResults; import netscape.ldap.LDAPv2; /** * The LDAPTemplate is invoked to process LDAP tags embedded in * a document. This version requires the "ldap40.jar" file from the * Netscape Navigator distribution. *

* The LDAPTemplate uses the following special tag:

*

* When an LDAP tag is seen, the LDAP database is searched and the results * are used to populate the request properties. *

* The following configuration parameters are used to perform the search. * The parameters may appear either in the request properties (preceded by * the prefix of this template as specified in the configuration file) or as * named arguments in the LDAP tag. *

*
prefix *
The string prefix for the property names that will be stored * in the request properties to hold the results. If not specified, * defaults to the prefix of this template as specified in the * configuration file.

* *

dn *
The Distinguished Name (DN) to lookup in the LDAP server. The format * of a DN is described in RFC-1779. The "dn" and "search" options are * mutually exclusive. When "dn" is specified, only zero or one result * will be returned from the LDAP database. The result (if any) will be * stored in the request properties as follows: *
 * <ldap dn="uid=6105,ou=people,o=WebAuth" prefix=name>
 * <property name.dn>
 * <property name.cn>
 * <property name.sn>
 * <property name.objectclass>
* etc. The property name.dn is the DN that was * found. Other properties will be defined as shown, based on the * attributes present in the LDAP record.

* *

search *
The search filter to use when searching the LDAP server. The format * of a search filter is described in RFC-1558. The "search" and "dn" * options are mutually exclusive. When "search" is specified, zero or * more results will be returned from the LDAP database. The results * will be stored in the request properties as follows: *
 * <ldap search="(givenname=scott)" prefix=name>
 * <property name.rows>
 * <property name.rowcount>
 * 
 * <property name.0.dn>
 * <property name.0.cn>
 * <property name.0.mail>
 *
 * <property name.1.dn>
 * <property name.1.cn>
 * <property name.1.pager>
* etc. The property name.rows is set to the list * of record indices found, and can be used by the BSL tag * <foreach name=x property=name.rows> to * iterate over all records. Other properties will be defined for * each of the records found as shown, based on the attributes present * in the each of the LDAP records.

* *

base *
The Distinguished Name of the base record that forms the root of the * search tree in the LDAP database. Used only with the "search" option. * Defaults to "". This would be a good option to specify in the * configuration file rather than in the LDAP tag.

* *

scope *
The scope of the LDAP search, one of * Used only with the "search" option. Defaults to "sub". This would * be a good option to specify in the configuration file rather than in * the LDAP tag.

* *

attributes *
The space-delimited list of attribute names to return from the * LDAP "dn" or "search" operation. If empty or unspecified, all * attributes for the record are returned. Not all records in the * LDAP database have the same attributes. Defaults to "".

* *

host *
The hostname of the LDAP server, of the form "host" * or "host:port" if the server is not running on the * standard LDAP port. Defaults to "". This would be a good option to * specify in the configuration file rather than in the LDAP tag.

* *

authenticate *
The Distinguished Name used for authenticating to the LDAP server, * if necessary. Defaults to "". This would be a good option to specify * in the configuration file rather than in the LDAP tag.

* *

password *
The password sent when the "authenticate" option is used. Defaults * to "". If it begins with a '@', * The rest of the password is taken as the name of a file containing the * password. The password "@-" causes the password to be read from stdin. *
limit *
The maxumum number of records returned. defaults to 1000. *
timeout *
The maxumum time to wait for a response, in ms. Defaults to * 30000 (30s). *
* * @author Colin Stevens (colin.stevens@sun.com) * @version %I% */ public class LDAPTemplate extends Template { private static String[] split(String str) { if (str == null) { return null; } StringTokenizer st = new StringTokenizer(str); int n = st.countTokens(); String[] strs = new String[n]; for (int i = 0; i < n; i++) { strs[i] = st.nextToken(); } return strs; } /** * Process <ldap> tags. */ public void tag_ldap(RewriteContext hr) { String host = hr.get("host", ""); String auth = hr.get("authenticate", null); String password = getPassword(hr); debug(hr); hr.killToken(); String name = hr.get("prefix"); name = (name == null) ? hr.prefix : name; if (name.endsWith(".") == false) { name += "."; } String[] attrs = split(hr.get("attributes")); LDAPConnection ld = new LDAPConnection(); int i = 0; StringBuffer sb = new StringBuffer(); try { ld.connect(host, LDAPv2.DEFAULT_PORT, auth, password); String dn = hr.get("dn"); if (dn != null) { LDAPEntry entry = ld.read(dn, attrs); /* * Empirically: An entry != null may be returned even if no * match was found. */ if ((entry != null) && (entry.getDN() != null)) { shove(hr.request.props, name, entry, attrs); } return; } String base = hr.get("base", null); String str = hr.get("scope", null); int scope = LDAPv2.SCOPE_SUB; if ("base".equals(str)) { scope = LDAPv2.SCOPE_BASE; } else if ("one".equals(str)) { scope = LDAPv2.SCOPE_ONE; } String search = hr.get("search"); try { ld.setOption(LDAPv2.SIZELIMIT, Integer.decode(hr.get("limit","1000"))); } catch (Exception e) { System.out.println("Ldap error: " + e); } try { ld.setOption(LDAPv2.TIMELIMIT, Integer.decode(hr.get("timeout","30000"))); } catch (Exception e) { System.out.println("Ldap error: " + e); } LDAPSearchResults results = ld.search(base, scope, search, attrs, false); for (i = 0; results.hasMoreElements(); i++) { shove(hr.request.props, name + i + ".", results.next(), attrs); sb.append(i).append(' '); } ld.disconnect(); } catch (LDAPException e) { hr.request.props.put(name + "error", e.errorCodeToString()); hr.request.props.put(name + "errorCode", "" +e.getLDAPResultCode()); System.out.println("LDAP error: " + e); } finally { hr.request.props.put(name + "rows", sb.toString().trim()); hr.request.props.put(name + "rowcount", "" + i); try { ld.disconnect(); } catch (Exception e) { System.out.println("LDAP disconnect error: " + e); } } } private static void shove(Properties props, String prefix, LDAPEntry entry, String[] names) { props.put(prefix + "dn", entry.getDN()); if (names == null) { Enumeration e = entry.getAttributeSet().getAttributes(); while (e.hasMoreElements()) { LDAPAttribute attr = (LDAPAttribute) e.nextElement(); props.put(prefix + attr.getName(), getAttributeValue(attr)); } } else { for (int i = 0; i < names.length; i++) { LDAPAttribute attr = entry.getAttribute(names[i]); String value = (attr == null) ? "" : getAttributeValue(attr); props.put(prefix + names[i], value); } } } private static String getAttributeValue(LDAPAttribute attr) { StringBuffer sb = new StringBuffer(); Enumeration e = attr.getStringValues(); while (e.hasMoreElements()) { sb.append(e.nextElement()).append('|'); } if (sb.length() > 0) { sb.setLength(sb.length() - 1); } return sb.toString(); } // stolen from SslHandler - should be a utility method XXX static String getPassword(RewriteContext hr) { String pass = hr.get("password"); if (pass==null || !pass.startsWith("@")) { return pass; } String file = pass.substring(1); HttpInputStream in; try { if (file.equals("-")) { in = new HttpInputStream(System.in); pass = in.readLine(); } else { in = new HttpInputStream(new FileInputStream(file)); pass = in.readLine(); in.close(); } } catch (IOException e) { hr.request.log(Server.LOG_WARNING, hr.prefix, "Can't read password file: " + e); return null; } return pass; } }