1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   * 
13   */
14  
15  package com.liferay.portal.security.ldap;
16  
17  import com.liferay.portal.SystemException;
18  import com.liferay.portal.kernel.log.Log;
19  import com.liferay.portal.kernel.log.LogFactoryUtil;
20  import com.liferay.portal.kernel.log.LogUtil;
21  import com.liferay.portal.kernel.util.GetterUtil;
22  import com.liferay.portal.kernel.util.PropsKeys;
23  import com.liferay.portal.kernel.util.StringBundler;
24  import com.liferay.portal.kernel.util.StringPool;
25  import com.liferay.portal.kernel.util.StringUtil;
26  import com.liferay.portal.kernel.util.Validator;
27  import com.liferay.portal.model.Contact;
28  import com.liferay.portal.model.User;
29  import com.liferay.portal.model.UserGroup;
30  import com.liferay.portal.util.PrefsPropsUtil;
31  import com.liferay.portal.util.PropsValues;
32  
33  import java.util.ArrayList;
34  import java.util.List;
35  import java.util.Properties;
36  
37  import javax.naming.Binding;
38  import javax.naming.CompositeName;
39  import javax.naming.Context;
40  import javax.naming.Name;
41  import javax.naming.NamingEnumeration;
42  import javax.naming.OperationNotSupportedException;
43  import javax.naming.directory.Attribute;
44  import javax.naming.directory.Attributes;
45  import javax.naming.directory.SearchControls;
46  import javax.naming.directory.SearchResult;
47  import javax.naming.ldap.Control;
48  import javax.naming.ldap.InitialLdapContext;
49  import javax.naming.ldap.LdapContext;
50  import javax.naming.ldap.PagedResultsControl;
51  import javax.naming.ldap.PagedResultsResponseControl;
52  
53  /**
54   * <a href="PortalLDAPUtil.java.html"><b><i>View Source</i></b></a>
55   *
56   * @author Michael Young
57   * @author Brian Wing Shun Chan
58   * @author Jerry Niu
59   * @author Scott Lee
60   * @author Hervé Ménage
61   * @author Samuel Kong
62   * @author Ryan Park
63   * @author Wesley Gong
64   */
65  public class PortalLDAPUtil {
66  
67      /**
68       * @deprecated Use {@link PortalLDAPImporter#IMPORT_BY_GROUP}.
69       */
70      public static final String IMPORT_BY_GROUP =
71          PortalLDAPImporter.IMPORT_BY_GROUP;
72  
73      /**
74       * @deprecated Use {@link PortalLDAPImporter#IMPORT_BY_USER}.
75       */
76      public static final String IMPORT_BY_USER =
77          PortalLDAPImporter.IMPORT_BY_USER;
78  
79      /**
80       * @deprecated Use {@link PortalLDAPExporter#exportToLDAP(Contact)}.
81       */
82      public static void exportToLDAP(Contact contact) throws Exception {
83          PortalLDAPExporter.exportToLDAP(contact);
84      }
85  
86      /**
87       * @deprecated Use {@link PortalLDAPExporter#exportToLDAP(User)}.
88       */
89      public static void exportToLDAP(User user) throws Exception {
90          PortalLDAPExporter.exportToLDAP(user);
91      }
92  
93      /**
94       * @deprecated Use {@link LDAPSettingsUtil#getAuthSearchFilter(long, long,
95       *             String, String, String)}.
96       */
97      public static String getAuthSearchFilter(
98              long ldapServerId, long companyId, String emailAddress,
99              String screenName, String userId)
100         throws SystemException {
101 
102         return LDAPSettingsUtil.getAuthSearchFilter(
103             ldapServerId, companyId, emailAddress, screenName, userId);
104     }
105 
106     public static LdapContext getContext(long ldapServerId, long companyId)
107         throws Exception {
108 
109         String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
110 
111         String baseProviderURL = PrefsPropsUtil.getString(
112             companyId, PropsKeys.LDAP_BASE_PROVIDER_URL + postfix);
113         String pricipal = PrefsPropsUtil.getString(
114             companyId, PropsKeys.LDAP_SECURITY_PRINCIPAL + postfix);
115         String credentials = PrefsPropsUtil.getString(
116             companyId, PropsKeys.LDAP_SECURITY_CREDENTIALS + postfix);
117 
118         return getContext(companyId, baseProviderURL, pricipal, credentials);
119     }
120 
121     public static LdapContext getContext(
122             long companyId, String providerURL, String principal,
123             String credentials)
124         throws Exception {
125 
126         Properties env = new Properties();
127 
128         env.put(
129             Context.INITIAL_CONTEXT_FACTORY,
130             PrefsPropsUtil.getString(
131                 companyId, PropsKeys.LDAP_FACTORY_INITIAL));
132         env.put(Context.PROVIDER_URL, providerURL);
133         env.put(Context.SECURITY_PRINCIPAL, principal);
134         env.put(Context.SECURITY_CREDENTIALS, credentials);
135         env.put(
136             Context.REFERRAL,
137             PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_REFERRAL));
138 
139         // Enable pooling
140 
141         env.put("com.sun.jndi.ldap.connect.pool", "true");
142         env.put("com.sun.jndi.ldap.connect.pool.maxsize","50");
143         env.put("com.sun.jndi.ldap.connect.pool.timeout", "10000");
144 
145         LogUtil.debug(_log, env);
146 
147         LdapContext ldapContext = null;
148 
149         try {
150             ldapContext = new InitialLdapContext(env, null);
151         }
152         catch (Exception e) {
153             if (_log.isWarnEnabled()) {
154                 _log.warn("Failed to bind to the LDAP server");
155             }
156 
157             if (_log.isDebugEnabled()) {
158                 _log.debug(e, e);
159             }
160         }
161 
162         return ldapContext;
163     }
164 
165     public static Attributes getGroupAttributes(
166             long ldapServerId, long companyId, LdapContext ldapContext,
167             String fullDistinguishedName)
168         throws Exception {
169 
170         return getGroupAttributes(ldapServerId, companyId, ldapContext,
171             fullDistinguishedName, false);
172     }
173 
174     public static Attributes getGroupAttributes(
175             long ldapServerId, long companyId, LdapContext ldapContext,
176             String fullDistinguishedName, boolean includeReferenceAttributes)
177         throws Exception {
178 
179         Properties groupMappings = LDAPSettingsUtil.getGroupMappings(
180             ldapServerId, companyId);
181 
182         List<String> mappedGroupAttributeIds = new ArrayList<String>();
183 
184         mappedGroupAttributeIds.add(groupMappings.getProperty("groupName"));
185         mappedGroupAttributeIds.add(groupMappings.getProperty("description"));
186 
187         if (includeReferenceAttributes) {
188             mappedGroupAttributeIds.add(groupMappings.getProperty("user"));
189         }
190 
191         return _getAttributes(
192             ldapContext, fullDistinguishedName,
193             mappedGroupAttributeIds.toArray(new String[0]));
194     }
195 
196     /**
197      * @deprecated Use {@link LDAPSettingsUtil#getGroupMappings(long, long)}.
198      */
199     public static Properties getGroupMappings(long ldapServerId, long companyId)
200         throws Exception {
201 
202         return LDAPSettingsUtil.getGroupMappings(ldapServerId, companyId);
203     }
204 
205     public static List<SearchResult> getGroups(
206             long companyId, LdapContext ldapContext, int maxResults,
207             String baseDN, String groupFilter)
208         throws Exception {
209 
210         return searchLDAP(
211             companyId, ldapContext, maxResults, baseDN, groupFilter, null);
212     }
213 
214     public static List<SearchResult> getGroups(
215             long ldapServerId, long companyId, LdapContext ldapContext,
216             int maxResults)
217         throws Exception {
218 
219         String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
220 
221         String baseDN = PrefsPropsUtil.getString(
222             companyId, PropsKeys.LDAP_BASE_DN + postfix);
223         String groupFilter = PrefsPropsUtil.getString(
224             companyId, PropsKeys.LDAP_IMPORT_GROUP_SEARCH_FILTER + postfix);
225 
226         return getGroups(
227             companyId, ldapContext, maxResults, baseDN, groupFilter);
228     }
229 
230     public static long getLdapServerId(long companyId, String screenName)
231         throws Exception {
232 
233         long[] ldapServerIds = StringUtil.split(
234             PrefsPropsUtil.getString(companyId, "ldap.server.ids"), 0L);
235 
236         for (long ldapServerId : ldapServerIds) {
237             if (hasUser(ldapServerId, companyId, screenName)) {
238                 return ldapServerId;
239             }
240         }
241 
242         if (ldapServerIds.length > 0) {
243             return ldapServerIds[0];
244         }
245 
246         return 0;
247     }
248 
249     public static Attribute getMultivaluedAttribute(
250             long companyId, LdapContext ldapContext, String baseDN,
251             String filter, Attribute attribute)
252         throws Exception {
253 
254         if (attribute.size() > 0) {
255             return attribute;
256         }
257 
258         String[] attributeIds = {_getNextRange(attribute.getID())};
259 
260         while (true) {
261             List<SearchResult> searchResults = searchLDAP(
262                 companyId, ldapContext, 0, baseDN, filter, attributeIds);
263 
264             if (searchResults.size() != 1) {
265                 break;
266             }
267 
268             SearchResult searchResult = searchResults.get(0);
269 
270             Attributes attributes = searchResult.getAttributes();
271 
272             if (attributes.size() != 1) {
273                 break;
274             }
275 
276             NamingEnumeration<? extends Attribute> enu = attributes.getAll();
277 
278             if (!enu.hasMoreElements()) {
279                 break;
280             }
281 
282             Attribute curAttribute = enu.nextElement();
283 
284             for (int i = 0; i < curAttribute.size(); i++) {
285                 attribute.add(curAttribute.get(i));
286             }
287 
288             if (StringUtil.endsWith(curAttribute.getID(), StringPool.STAR) ||
289                 (curAttribute.size() < PropsValues.LDAP_RANGE_SIZE)) {
290 
291                 break;
292             }
293 
294             attributeIds[0] = _getNextRange(attributeIds[0]);
295         }
296 
297         return attribute;
298     }
299 
300     public static String getNameInNamespace(
301             long ldapServerId, long companyId, Binding binding)
302         throws Exception {
303 
304         String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
305 
306         String baseDN = PrefsPropsUtil.getString(
307             companyId, PropsKeys.LDAP_BASE_DN + postfix);
308 
309         String name = binding.getName();
310 
311         if (name.startsWith(StringPool.QUOTE) &&
312             name.endsWith(StringPool.QUOTE)) {
313 
314             name = name.substring(1, name.length() - 1);
315         }
316 
317         if (Validator.isNull(baseDN)) {
318             return name.toString();
319         }
320         else {
321             return name.concat(StringPool.COMMA).concat(baseDN);
322         }
323     }
324 
325     public static Binding getUser(
326             long ldapServerId, long companyId, String screenName)
327         throws Exception {
328 
329         String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
330 
331         LdapContext ldapContext = getContext(ldapServerId, companyId);
332 
333         NamingEnumeration<SearchResult> enu = null;
334 
335         try {
336             if (ldapContext == null) {
337                 return null;
338             }
339 
340             String baseDN = PrefsPropsUtil.getString(
341                 companyId, PropsKeys.LDAP_BASE_DN + postfix);
342 
343             Properties userMappings = LDAPSettingsUtil.getUserMappings(
344                 ldapServerId, companyId);
345 
346             StringBundler filter = new StringBundler(5);
347 
348             filter.append(StringPool.OPEN_PARENTHESIS);
349             filter.append(userMappings.getProperty("screenName"));
350             filter.append(StringPool.EQUAL);
351             filter.append(screenName);
352             filter.append(StringPool.CLOSE_PARENTHESIS);
353 
354             SearchControls cons = new SearchControls(
355                 SearchControls.SUBTREE_SCOPE, 1, 0, null, false, false);
356 
357             enu = ldapContext.search(baseDN, filter.toString(), cons);
358         }
359         catch (Exception e) {
360             throw e;
361         }
362         finally {
363             if (ldapContext != null) {
364                 ldapContext.close();
365             }
366         }
367 
368         if (enu.hasMoreElements()) {
369             Binding binding = enu.nextElement();
370 
371             enu.close();
372 
373             return binding;
374         }
375         else {
376             return null;
377         }
378     }
379 
380     public static Attributes getUserAttributes(
381             long ldapServerId, long companyId, LdapContext ldapContext,
382             String fullDistinguishedName)
383         throws Exception {
384 
385         Properties userMappings = LDAPSettingsUtil.getUserMappings(
386             ldapServerId, companyId);
387 
388         String[] mappedUserAttributeIds = {
389             userMappings.getProperty("screenName"),
390             userMappings.getProperty("emailAddress"),
391             userMappings.getProperty("fullName"),
392             userMappings.getProperty("firstName"),
393             userMappings.getProperty("middleName"),
394             userMappings.getProperty("lastName"),
395             userMappings.getProperty("jobTitle"),
396             userMappings.getProperty("group")
397         };
398 
399         return _getAttributes(
400             ldapContext, fullDistinguishedName, mappedUserAttributeIds);
401     }
402 
403     /**
404      * @deprecated Use {@link LDAPSettingsUtil#getUserMappings(long, long)}.
405      */
406     public static Properties getUserMappings(long ldapServerId, long companyId)
407         throws Exception {
408 
409         return LDAPSettingsUtil.getUserMappings(ldapServerId, companyId);
410     }
411 
412     public static List<SearchResult> getUsers(
413             long companyId, LdapContext ldapContext, int maxResults,
414             String baseDN, String userFilter)
415         throws Exception {
416 
417         return searchLDAP(
418             companyId, ldapContext, maxResults, baseDN, userFilter, null);
419     }
420 
421     public static List<SearchResult> getUsers(
422             long ldapServerId, long companyId, LdapContext ldapContext,
423             int maxResults)
424         throws Exception {
425 
426         String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
427 
428         String baseDN = PrefsPropsUtil.getString(
429             companyId, PropsKeys.LDAP_BASE_DN + postfix);
430         String userFilter = PrefsPropsUtil.getString(
431             companyId, PropsKeys.LDAP_IMPORT_USER_SEARCH_FILTER + postfix);
432 
433         return getUsers(companyId, ldapContext, maxResults, baseDN, userFilter);
434     }
435 
436     public static String getUsersDN(long ldapServerId, long companyId)
437         throws Exception {
438 
439         String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
440 
441         return PrefsPropsUtil.getString(
442             companyId, PropsKeys.LDAP_USERS_DN + postfix);
443     }
444 
445     public static boolean hasUser(
446             long ldapServerId, long companyId, String screenName)
447         throws Exception {
448 
449         if (getUser(ldapServerId, companyId, screenName) != null) {
450             return true;
451         }
452         else {
453             return false;
454         }
455     }
456 
457     /**
458      * @deprecated Use {@link PortalLDAPImporter#importFromLDAP}.
459      */
460     public static void importFromLDAP() throws Exception {
461         PortalLDAPImporter.importFromLDAP();
462     }
463 
464     /**
465      * @deprecated Use {@link PortalLDAPImporter#importFromLDAP(long)}.
466      */
467     public static void importFromLDAP(long companyId) throws Exception {
468         PortalLDAPImporter.importFromLDAP(companyId);
469     }
470 
471     /**
472      * @deprecated Use {@link PortalLDAPImporter#importFromLDAP(long, long)}.
473      */
474     public static void importFromLDAP(long ldapServerId, long companyId)
475         throws Exception {
476 
477         PortalLDAPImporter.importFromLDAP(ldapServerId, companyId);
478     }
479 
480     /**
481      * @deprecated Use {@link PortalLDAPImporter#importLDAPGroup(long, long,
482      *             LdapContext, Attributes, boolean)}.
483      */
484     public static UserGroup importLDAPGroup(
485             long ldapServerId, long companyId, LdapContext ldapContext,
486             Attributes attributes, boolean importGroupMembership)
487         throws Exception {
488 
489         return PortalLDAPImporter.importLDAPGroup(
490             ldapServerId, companyId, ldapContext, attributes,
491             importGroupMembership);
492     }
493 
494     /**
495      * @deprecated Use {@link PortalLDAPImporter#importLDAPUser(long, long,
496      *             LdapContext, Attributes, boolean)}.
497      */
498     public static User importLDAPUser(
499             long ldapServerId, long companyId, LdapContext ldapContext,
500             Attributes attributes, String password,
501             boolean importGroupMembership)
502         throws Exception {
503 
504         return PortalLDAPImporter.importLDAPUser(
505             ldapServerId, companyId, ldapContext, attributes, password,
506             importGroupMembership);
507     }
508 
509     /**
510      * @deprecated Use {@link LDAPSettingsUtil#isAuthEnabled(long)}.
511      */
512     public static boolean isAuthEnabled(long companyId) throws SystemException {
513         return LDAPSettingsUtil.isAuthEnabled(companyId);
514     }
515 
516     /**
517      * @deprecated Use {@link LDAPSettingsUtil#isExportEnabled(long)}.
518      */
519     public static boolean isExportEnabled(long companyId)
520         throws SystemException {
521 
522         return LDAPSettingsUtil.isExportEnabled(companyId);
523     }
524 
525     /**
526      * @deprecated Use {@link LDAPSettingsUtil#isImportEnabled(long)}.
527      */
528     public static boolean isImportEnabled(long companyId)
529         throws SystemException {
530 
531         return LDAPSettingsUtil.isImportEnabled(companyId);
532     }
533 
534     /**
535      * @deprecated Use {@link LDAPSettingsUtil#isImportOnStartup(long)}.
536      */
537     public static boolean isImportOnStartup(long companyId)
538         throws SystemException {
539 
540         return LDAPSettingsUtil.isImportOnStartup(companyId);
541     }
542 
543     /**
544      * @deprecated Use {@link LDAPSettingsUtil#isNtlmEnabled(long)}.
545      */
546     public static boolean isNtlmEnabled(long companyId)
547         throws SystemException {
548 
549         return LDAPSettingsUtil.isNtlmEnabled(companyId);
550     }
551 
552     /**
553      * @deprecated Use {@link LDAPSettingsUtil#isPasswordPolicyEnabled(long)}.
554      */
555     public static boolean isPasswordPolicyEnabled(long companyId)
556         throws SystemException {
557 
558         return LDAPSettingsUtil.isPasswordPolicyEnabled(companyId);
559     }
560 
561     /**
562      * @deprecated Use {@link LDAPSettingsUtil#isSiteMinderEnabled(long)}.
563      */
564     public static boolean isSiteMinderEnabled(long companyId)
565         throws SystemException {
566 
567         return LDAPSettingsUtil.isSiteMinderEnabled(companyId);
568     }
569 
570     public static List<SearchResult> searchLDAP(
571             long companyId, LdapContext ldapContext, int maxResults,
572             String baseDN, String filter, String[] attributeIds)
573         throws Exception {
574 
575         List<SearchResult> searchResults = new ArrayList<SearchResult>();
576 
577         SearchControls cons = new SearchControls(
578             SearchControls.SUBTREE_SCOPE, maxResults, 0, attributeIds, false,
579             false);
580 
581         try {
582             byte[] cookie = new byte[0];
583 
584             while (cookie != null) {
585                 if (cookie.length == 0) {
586                     ldapContext.setRequestControls(
587                         new Control[] {
588                             new PagedResultsControl(
589                                 PropsValues.LDAP_PAGE_SIZE, Control.CRITICAL)
590                         });
591                 }
592                 else {
593                     ldapContext.setRequestControls(
594                         new Control[] {
595                             new PagedResultsControl(
596                                 PropsValues.LDAP_PAGE_SIZE, cookie,
597                                 Control.CRITICAL)
598                         });
599                 }
600 
601                 NamingEnumeration<SearchResult> enu = ldapContext.search(
602                     baseDN, filter, cons);
603 
604                 while (enu.hasMoreElements()) {
605                     searchResults.add(enu.nextElement());
606                 }
607 
608                 enu.close();
609 
610                 cookie = _getCookie(ldapContext.getResponseControls());
611             }
612         }
613         catch (OperationNotSupportedException onse) {
614             ldapContext.setRequestControls(null);
615 
616             NamingEnumeration<SearchResult> enu = ldapContext.search(
617                 baseDN, filter, cons);
618 
619             while (enu.hasMoreElements()) {
620                 searchResults.add(enu.nextElement());
621             }
622 
623             enu.close();
624         }
625         finally {
626             ldapContext.setRequestControls(null);
627         }
628 
629         return searchResults;
630     }
631 
632     private static Attributes _getAttributes(
633             LdapContext ldapContext, String fullDistinguishedName,
634             String[] attributeIds)
635         throws Exception {
636 
637         Name fullDN = new CompositeName().add(fullDistinguishedName);
638 
639         Attributes attributes = null;
640 
641         String[] auditAttributeIds = {
642             "creatorsName", "createTimestamp", "modifiersName",
643             "modifyTimestamp"
644         };
645 
646         if (attributeIds == null) {
647 
648             // Get complete listing of LDAP attributes (slow)
649 
650             attributes = ldapContext.getAttributes(fullDN);
651 
652             NamingEnumeration<? extends Attribute> enu =
653                 ldapContext.getAttributes(fullDN, auditAttributeIds).getAll();
654 
655             while (enu.hasMoreElements()) {
656                 attributes.put(enu.nextElement());
657             }
658 
659             enu.close();
660         }
661         else {
662 
663             // Get specified LDAP attributes
664 
665             int attributeCount = attributeIds.length + auditAttributeIds.length;
666 
667             String[] allAttributeIds = new String[attributeCount];
668 
669             System.arraycopy(
670                 attributeIds, 0, allAttributeIds, 0, attributeIds.length);
671             System.arraycopy(
672                 auditAttributeIds, 0, allAttributeIds, attributeIds.length,
673                 auditAttributeIds.length);
674 
675             attributes = ldapContext.getAttributes(fullDN, allAttributeIds);
676         }
677 
678         return attributes;
679     }
680 
681     private static byte[] _getCookie(Control[] controls) {
682         if (controls == null) {
683             return null;
684         }
685 
686         for (Control control : controls) {
687             if (control instanceof PagedResultsResponseControl) {
688                 PagedResultsResponseControl pagedResultsResponseControl =
689                     (PagedResultsResponseControl)control;
690 
691                 return pagedResultsResponseControl.getCookie();
692             }
693         }
694 
695         return null;
696     }
697 
698     private static String _getNextRange(String attributeId) {
699         String originalAttributeId = null;
700         int start = 0;
701         int end = 0;
702 
703         int x = attributeId.indexOf(StringPool.SEMICOLON);
704 
705         if (x < 0) {
706             originalAttributeId = attributeId;
707             end = PropsValues.LDAP_RANGE_SIZE - 1;
708         }
709         else {
710             int y = attributeId.indexOf(StringPool.EQUAL, x);
711             int z = attributeId.indexOf(StringPool.DASH, y);
712 
713             originalAttributeId = attributeId.substring(0, x);
714             start = GetterUtil.getInteger(attributeId.substring(y + 1, z));
715             end = GetterUtil.getInteger(attributeId.substring(z + 1));
716 
717             start += PropsValues.LDAP_RANGE_SIZE;
718             end += PropsValues.LDAP_RANGE_SIZE;
719         }
720 
721         StringBundler sb = new StringBundler(6);
722 
723         sb.append(originalAttributeId);
724         sb.append(StringPool.SEMICOLON);
725         sb.append("range=");
726         sb.append(start);
727         sb.append(StringPool.DASH);
728         sb.append(end);
729 
730         return sb.toString();
731     }
732 
733     private static Log _log = LogFactoryUtil.getLog(PortalLDAPUtil.class);
734 
735 }