001: /* Copyright 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.services.persondir.support;
007:
008: import java.util.Collection;
009: import java.util.Collections;
010: import java.util.HashMap;
011: import java.util.HashSet;
012: import java.util.Iterator;
013: import java.util.LinkedList;
014: import java.util.List;
015: import java.util.Map;
016: import java.util.Set;
017:
018: import javax.naming.NamingEnumeration;
019: import javax.naming.NamingException;
020: import javax.naming.directory.Attribute;
021: import javax.naming.directory.Attributes;
022: import javax.naming.directory.DirContext;
023: import javax.naming.directory.SearchControls;
024: import javax.naming.directory.SearchResult;
025:
026: import org.jasig.portal.ldap.ILdapServer;
027: import org.springframework.dao.DataAccessResourceFailureException;
028: import org.springframework.dao.IncorrectResultSizeDataAccessException;
029:
030: /**
031: * LDAP implementation of {@link org.jasig.portal.services.persondir.IPersonAttributeDao}. This is code copied
032: * from uPortal 2.4 {@link org.jasig.portal.services.PersonDirectory} and
033: * made to implement this DAO interface. Dependent upon JNDI.
034: *
035: * In the case of multi valued attributes, now stores a
036: * {@link java.util.ArrayList} rather than a {@link java.util.Vector}.
037: *
038: * @author andrew.petro@yale.edu
039: * @author Eric Dalquist <a href="mailto:edalquist@unicon.net">edalquist@unicon.net</a>
040: * @version $Revision: 36683 $ $Date: 2006-08-23 15:08:00 -0700 (Wed, 23 Aug 2006) $
041: * @since uPortal 2.5
042: */
043: public class LdapPersonAttributeDaoImpl extends
044: AbstractDefaultQueryPersonAttributeDao {
045:
046: /**
047: * Time limit, in milliseconds, for LDAP query.
048: * Zero means wait indefinitely.
049: */
050: private int timeLimit = 0;
051:
052: /**
053: * The query we should execute.
054: */
055: private String query;
056:
057: /**
058: * Map from LDAP attribute names to uPortal attribute names.
059: */
060: private Map attributeMappings = Collections.EMPTY_MAP;
061:
062: /**
063: * {@link Set} of attributes this DAO may provide when queried.
064: */
065: private Set userAttributes = Collections.EMPTY_SET;
066:
067: /**
068: * List of names of uPortal attributes the values of which
069: * will be used, in order, to populate the parameters of the
070: * LDAP query.
071: */
072: private List queryAttributes = Collections.EMPTY_LIST;
073:
074: /**
075: * The ldap server to use to make the queries against.
076: */
077: private ILdapServer ldapServer;
078:
079: /**
080: * Returned {@link Map} will have values of String or String[] or byte[]
081: *
082: * @see org.jasig.portal.services.persondir.IPersonAttributeDao#getUserAttributes(java.util.Map)
083: */
084: public Map getUserAttributes(final Map seed) {
085: //Checks to make sure the argument & state is valid
086: if (seed == null)
087: throw new IllegalArgumentException(
088: "The query seed Map cannot be null.");
089:
090: if (this .ldapServer == null)
091: throw new IllegalStateException("ILdapServer is null");
092:
093: if (this .query == null)
094: throw new IllegalStateException("query is null");
095:
096: //Ensure the data needed to run the query is avalable
097: if (!((queryAttributes != null && seed.keySet().containsAll(
098: queryAttributes)) || (queryAttributes == null && seed
099: .containsKey(this .getDefaultAttributeName())))) {
100: return null;
101: }
102:
103: //Connect to the LDAP server
104: DirContext context = null;
105: try {
106: context = this .ldapServer.getConnection();
107:
108: if (context == null) {
109: throw new DataAccessResourceFailureException(
110: "No LDAP Connection could be obtained. Aborting ldap person attribute lookup.");
111: }
112:
113: // Search for the userid in the usercontext subtree of the directory
114: // Use the uidquery substituting username for {0}, {1}, ...
115: final SearchControls sc = new SearchControls();
116: sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
117: sc.setTimeLimit(this .timeLimit);
118:
119: //Can't just to a toArray here since the order of the keys in the Map
120: //may not match the order of the keys in the List and it is important to
121: //the query.
122: final Object[] args = new Object[this .queryAttributes
123: .size()];
124: for (int index = 0; index < args.length; index++) {
125: final String attrName = (String) this .queryAttributes
126: .get(index);
127: args[index] = seed.get(attrName);
128: }
129:
130: //Search the LDAP
131: final NamingEnumeration userlist = context.search(
132: this .ldapServer.getBaseDN(), this .query, args, sc);
133: try {
134: if (userlist.hasMoreElements()) {
135: final Map rowResults = new HashMap();
136:
137: final SearchResult result = (SearchResult) userlist
138: .next();
139:
140: //Only allow one result for the query, do the check here to
141: //save on attribute processing time.
142: if (userlist.hasMoreElements()) {
143: throw new IncorrectResultSizeDataAccessException(
144: "More than one result for ldap person attribute search.",
145: 1, -1);
146: }
147:
148: final Attributes ldapAttributes = result
149: .getAttributes();
150:
151: //Iterate through the attributes
152: for (final Iterator ldapAttrIter = this .attributeMappings
153: .keySet().iterator(); ldapAttrIter
154: .hasNext();) {
155: final String ldapAttributeName = (String) ldapAttrIter
156: .next();
157:
158: final Attribute attribute = ldapAttributes
159: .get(ldapAttributeName);
160:
161: //The attribute exists
162: if (attribute != null) {
163: for (final NamingEnumeration attrValueEnum = attribute
164: .getAll(); attrValueEnum.hasMore();) {
165: Object attributeValue = attrValueEnum
166: .next();
167:
168: //Convert everything except byte[] to String
169: //TODO should we be doing this conversion?
170: if (!(attributeValue instanceof byte[])) {
171: attributeValue = attributeValue
172: .toString();
173: }
174:
175: //See if the ldap attribute is mapped
176: Set attributeNames = (Set) attributeMappings
177: .get(ldapAttributeName);
178:
179: //No mapping was found, just use the ldap attribute name
180: if (attributeNames == null)
181: attributeNames = Collections
182: .singleton(ldapAttributeName);
183:
184: //Run through the mapped attribute names
185: for (final Iterator attrNameItr = attributeNames
186: .iterator(); attrNameItr
187: .hasNext();) {
188: final String attributeName = (String) attrNameItr
189: .next();
190:
191: MultivaluedPersonAttributeUtils
192: .addResult(rowResults,
193: attributeName,
194: attributeValue);
195: }
196: }
197: }
198: }
199:
200: return rowResults;
201: } else {
202: return null;
203: }
204: } finally {
205: try {
206: userlist.close();
207: } catch (final NamingException ne) {
208: log
209: .warn(
210: "Error closing ldap person attribute search results.",
211: ne);
212: }
213: }
214: } catch (final Throwable t) {
215: throw new DataAccessResourceFailureException(
216: "LDAP person attribute lookup failure.", t);
217: } finally {
218: this .ldapServer.releaseConnection(context);
219: }
220: }
221:
222: /*
223: * @see org.jasig.portal.services.persondir.support.IPersonAttributeDao#getPossibleUserAttributeNames()
224: */
225: public Set getPossibleUserAttributeNames() {
226: return this .userAttributes;
227: }
228:
229: /**
230: * Get the mapping from LDAP attribute names to uPortal attribute names.
231: * Mapping type is from String to [String | Set of String].
232: * @return Returns the ldapAttributesToPortalAttributes.
233: */
234: public Map getLdapAttributesToPortalAttributes() {
235: return this .attributeMappings;
236: }
237:
238: /**
239: * Set the {@link Map} to use for mapping from a ldap attribute name to a
240: * portal attribute name or {@link Set} of portal attribute names. Ldap
241: * attribute names that are specified but have null mappings will use the
242: * ldap attribute name for the portal attribute name.
243: * Ldap attribute names that are not specified as keys in this {@link Map}
244: * will be ignored.
245: * <br>
246: * The passed {@link Map} must have keys of type {@link String} and values
247: * of type {@link String} or a {@link Set} of {@link String}.
248: *
249: * @param ldapAttributesToPortalAttributesArg {@link Map} from ldap attribute names to portal attribute names.
250: * @throws IllegalArgumentException If the {@link Map} doesn't follow the rules stated above.
251: * @see MultivaluedPersonAttributeUtils#parseAttributeToAttributeMapping(Map)
252: */
253: public void setLdapAttributesToPortalAttributes(
254: final Map ldapAttributesToPortalAttributesArg) {
255: this .attributeMappings = MultivaluedPersonAttributeUtils
256: .parseAttributeToAttributeMapping(ldapAttributesToPortalAttributesArg);
257: final Collection userAttributeCol = MultivaluedPersonAttributeUtils
258: .flattenCollection(this .attributeMappings.values());
259:
260: this .userAttributes = Collections.unmodifiableSet(new HashSet(
261: userAttributeCol));
262:
263: }
264:
265: /**
266: * @return Returns the timeLimit.
267: */
268: public int getTimeLimit() {
269: return this .timeLimit;
270: }
271:
272: /**
273: * @param timeLimit The timeLimit to set.
274: */
275: public void setTimeLimit(int timeLimit) {
276: this .timeLimit = timeLimit;
277: }
278:
279: /**
280: * @return Returns the query.
281: */
282: public String getQuery() {
283: return this .query;
284: }
285:
286: /**
287: * @param uidQuery The query to set.
288: */
289: public void setQuery(String uidQuery) {
290: this .query = uidQuery;
291: }
292:
293: /**
294: * @return Returns the ldapServer.
295: */
296: public ILdapServer getLdapServer() {
297: return this .ldapServer;
298: }
299:
300: /**
301: * @param ldapServer The ldapServer to set.
302: */
303: public void setLdapServer(ILdapServer ldapServer) {
304: this .ldapServer = ldapServer;
305: }
306:
307: /**
308: * @return Returns the queryAttributes.
309: */
310: public List getQueryAttributes() {
311: return this .queryAttributes;
312: }
313:
314: /**
315: * @param queryAttributes The queryAttributes to set.
316: */
317: public void setQueryAttributes(List queryAttributes) {
318: this .queryAttributes = Collections
319: .unmodifiableList(new LinkedList(queryAttributes));
320: ;
321: }
322:
323: public String toString() {
324: StringBuffer sb = new StringBuffer();
325: sb.append(getClass().getName());
326: sb.append(" query=[").append(this .query).append("]");
327: sb.append(" attributeMappings=").append(this .attributeMappings);
328: sb.append(" ldapServer=").append(this.ldapServer);
329:
330: return sb.toString();
331: }
332: }
|