1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.security.ldap;
24  
25  import com.liferay.portal.NoSuchUserException;
26  import com.liferay.portal.NoSuchUserGroupException;
27  import com.liferay.portal.SystemException;
28  import com.liferay.portal.kernel.log.Log;
29  import com.liferay.portal.kernel.log.LogFactoryUtil;
30  import com.liferay.portal.kernel.log.LogUtil;
31  import com.liferay.portal.kernel.util.CalendarFactoryUtil;
32  import com.liferay.portal.kernel.util.InstancePool;
33  import com.liferay.portal.kernel.util.PropertiesUtil;
34  import com.liferay.portal.kernel.util.StringPool;
35  import com.liferay.portal.kernel.util.StringUtil;
36  import com.liferay.portal.kernel.util.Validator;
37  import com.liferay.portal.model.Company;
38  import com.liferay.portal.model.CompanyConstants;
39  import com.liferay.portal.model.Contact;
40  import com.liferay.portal.model.User;
41  import com.liferay.portal.model.UserGroup;
42  import com.liferay.portal.security.auth.ScreenNameGenerator;
43  import com.liferay.portal.service.CompanyLocalServiceUtil;
44  import com.liferay.portal.service.UserGroupLocalServiceUtil;
45  import com.liferay.portal.service.UserLocalServiceUtil;
46  import com.liferay.portal.util.PrefsPropsUtil;
47  import com.liferay.portal.util.PropsKeys;
48  import com.liferay.portal.util.PropsValues;
49  import com.liferay.util.ldap.LDAPUtil;
50  import com.liferay.util.ldap.Modifications;
51  
52  import java.text.DateFormat;
53  import java.text.ParseException;
54  import java.text.SimpleDateFormat;
55  
56  import java.util.ArrayList;
57  import java.util.Calendar;
58  import java.util.Date;
59  import java.util.List;
60  import java.util.Locale;
61  import java.util.Properties;
62  
63  import javax.naming.Binding;
64  import javax.naming.Context;
65  import javax.naming.NameNotFoundException;
66  import javax.naming.NamingEnumeration;
67  import javax.naming.directory.Attribute;
68  import javax.naming.directory.Attributes;
69  import javax.naming.directory.ModificationItem;
70  import javax.naming.directory.SearchControls;
71  import javax.naming.directory.SearchResult;
72  import javax.naming.ldap.InitialLdapContext;
73  import javax.naming.ldap.LdapContext;
74  
75  /**
76   * <a href="PortalLDAPUtil.java.html"><b><i>View Source</i></b></a>
77   *
78   * @author Michael Young
79   * @author Brian Wing Shun Chan
80   * @author Jerry Niu
81   * @author Scott Lee
82   * @author Hervé Ménage
83   *
84   */
85  public class PortalLDAPUtil {
86  
87      public static final String IMPORT_BY_USER = "user";
88  
89      public static final String IMPORT_BY_GROUP = "group";
90  
91      public static void exportToLDAP(Contact contact) throws Exception {
92          long companyId = contact.getCompanyId();
93  
94          if (!isAuthEnabled(companyId) || !isExportEnabled(companyId)) {
95              return;
96          }
97  
98          LdapContext ctx = getContext(companyId);
99  
100         try {
101             if (ctx == null) {
102                 return;
103             }
104 
105             User user = UserLocalServiceUtil.getUserByContactId(
106                 contact.getContactId());
107 
108             Properties userMappings = getUserMappings(companyId);
109             Binding binding = getUser(
110                 contact.getCompanyId(), user.getScreenName());
111             String name = StringPool.BLANK;
112 
113             if (binding == null) {
114 
115                 // Generate full DN based on user DN
116 
117                 StringBuilder sb = new StringBuilder();
118 
119                 sb.append(userMappings.getProperty("screenName"));
120                 sb.append(StringPool.EQUAL);
121                 sb.append(user.getScreenName());
122                 sb.append(StringPool.COMMA);
123                 sb.append(getUsersDN(companyId));
124 
125                 name = sb.toString();
126 
127                 // Create new user in LDAP
128 
129                 LDAPUser ldapUser = (LDAPUser)Class.forName(
130                     PropsValues.LDAP_USER_IMPL).newInstance();
131 
132                 ldapUser.setUser(user);
133 
134                 ctx.bind(name, ldapUser);
135             }
136             else {
137 
138                 // Modify existing LDAP user record
139 
140                 name = getNameInNamespace(companyId, binding);
141 
142                 Modifications mods = Modifications.getInstance();
143 
144                 mods.addItem(
145                     userMappings.getProperty("firstName"),
146                     contact.getFirstName());
147                 mods.addItem(
148                     userMappings.getProperty("lastName"),
149                     contact.getLastName());
150 
151                 String fullNameMapping = userMappings.getProperty("fullName");
152 
153                 if (Validator.isNotNull(fullNameMapping)) {
154                     mods.addItem(fullNameMapping, contact.getFullName());
155                 }
156 
157                 String jobTitleMapping = userMappings.getProperty("jobTitle");
158 
159                 if (Validator.isNotNull(jobTitleMapping)) {
160                     mods.addItem(jobTitleMapping, contact.getJobTitle());
161                 }
162 
163                 ModificationItem[] modItems = mods.getItems();
164 
165                 ctx.modifyAttributes(name, modItems);
166             }
167         }
168         catch (Exception e) {
169             throw e;
170         }
171         finally {
172             if (ctx != null) {
173                 ctx.close();
174             }
175         }
176     }
177 
178     public static void exportToLDAP(User user) throws Exception {
179         long companyId = user.getCompanyId();
180 
181         if (!isAuthEnabled(companyId) || !isExportEnabled(companyId)) {
182             return;
183         }
184 
185         LdapContext ctx = getContext(companyId);
186 
187         try {
188             if (ctx == null) {
189                 return;
190             }
191 
192             Properties userMappings = getUserMappings(companyId);
193             Binding binding = getUser(
194                 user.getCompanyId(), user.getScreenName());
195             String name = StringPool.BLANK;
196 
197             if (binding == null) {
198 
199                 // User is not exported until contact is updated
200 
201                 return;
202             }
203 
204             // Modify existing LDAP user record
205 
206             name = getNameInNamespace(companyId, binding);
207 
208             Modifications mods = Modifications.getInstance();
209 
210             mods.addItem(
211                 userMappings.getProperty("firstName"), user.getFirstName());
212             mods.addItem(
213                 userMappings.getProperty("lastName"), user.getLastName());
214 
215             String fullNameMapping = userMappings.getProperty("fullName");
216 
217             if (Validator.isNotNull(fullNameMapping)) {
218                 mods.addItem(fullNameMapping, user.getFullName());
219             }
220 
221             if (user.isPasswordModified() &&
222                 Validator.isNotNull(user.getPasswordUnencrypted())) {
223 
224                 mods.addItem(
225                     userMappings.getProperty("password"),
226                     user.getPasswordUnencrypted());
227             }
228 
229             mods.addItem(
230                 userMappings.getProperty("emailAddress"),
231                 user.getEmailAddress());
232 
233             String jobTitleMapping = userMappings.getProperty("jobTitle");
234 
235             if (Validator.isNotNull(jobTitleMapping)) {
236                 mods.addItem(jobTitleMapping, user.getJobTitle());
237             }
238 
239             ModificationItem[] modItems = mods.getItems();
240 
241             ctx.modifyAttributes(name, modItems);
242         }
243         catch (Exception e) {
244             _log.error(e, e);
245         }
246         finally {
247             if (ctx != null) {
248                 ctx.close();
249             }
250         }
251     }
252 
253     public static String getAuthSearchFilter(
254             long companyId, String emailAddress, String screenName,
255             String userId)
256         throws SystemException {
257 
258         String filter = PrefsPropsUtil.getString(
259             companyId, PropsKeys.LDAP_AUTH_SEARCH_FILTER);
260 
261         if (_log.isDebugEnabled()) {
262             _log.debug("Search filter before transformation " + filter);
263         }
264 
265         filter = StringUtil.replace(
266             filter,
267             new String[] {
268                 "@company_id@", "@email_address@", "@screen_name@", "@user_id@"
269             },
270             new String[] {
271                 String.valueOf(companyId), emailAddress, screenName,
272                 userId
273             });
274 
275         if (_log.isDebugEnabled()) {
276             _log.debug("Search filter after transformation " + filter);
277         }
278 
279         return filter;
280     }
281 
282     public static LdapContext getContext(long companyId) throws Exception {
283         String baseProviderURL = PrefsPropsUtil.getString(
284             companyId, PropsKeys.LDAP_BASE_PROVIDER_URL);
285         String pricipal = PrefsPropsUtil.getString(
286             companyId, PropsKeys.LDAP_SECURITY_PRINCIPAL);
287         String credentials = PrefsPropsUtil.getString(
288             companyId, PropsKeys.LDAP_SECURITY_CREDENTIALS);
289 
290         return getContext(companyId, baseProviderURL, pricipal, credentials);
291     }
292 
293     public static LdapContext getContext(
294             long companyId, String providerURL, String pricipal,
295             String credentials)
296         throws Exception {
297 
298         Properties env = new Properties();
299 
300         env.put(
301             Context.INITIAL_CONTEXT_FACTORY,
302             PrefsPropsUtil.getString(
303                 companyId, PropsKeys.LDAP_FACTORY_INITIAL));
304         env.put(Context.PROVIDER_URL, providerURL);
305         env.put(Context.SECURITY_PRINCIPAL, pricipal);
306         env.put(Context.SECURITY_CREDENTIALS, credentials);
307         env.put(
308             Context.REFERRAL,
309             PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_REFERRAL));
310 
311         // Enable pooling
312 
313         env.put("com.sun.jndi.ldap.connect.pool", "true");
314         env.put("com.sun.jndi.ldap.connect.pool.maxsize","50");
315         env.put("com.sun.jndi.ldap.connect.pool.timeout", "10000");
316 
317         LogUtil.debug(_log, env);
318 
319         LdapContext ctx = null;
320 
321         try {
322             ctx = new InitialLdapContext(env, null);
323         }
324         catch (Exception e) {
325             if (_log.isWarnEnabled()) {
326                 _log.warn("Failed to bind to the LDAP server");
327             }
328 
329             if (_log.isDebugEnabled()) {
330                 _log.debug(e);
331             }
332         }
333 
334         return ctx;
335     }
336 
337     public static Attributes getGroupAttributes(
338             long companyId, LdapContext ctx, String fullDistinguishedName)
339         throws Exception {
340 
341         return getGroupAttributes(companyId, ctx, fullDistinguishedName, false);
342     }
343 
344     public static Attributes getGroupAttributes(
345             long companyId, LdapContext ctx, String fullDistinguishedName,
346             boolean includeReferenceAttributes)
347         throws Exception {
348 
349         Properties groupMappings = getGroupMappings(companyId);
350 
351         List<String> mappedGroupAttributeIds = new ArrayList<String>();
352 
353         mappedGroupAttributeIds.add(groupMappings.getProperty("groupName"));
354         mappedGroupAttributeIds.add(groupMappings.getProperty("description"));
355 
356         if (includeReferenceAttributes) {
357             mappedGroupAttributeIds.add(groupMappings.getProperty("user"));
358         }
359 
360         return _getAttributes(
361             ctx, fullDistinguishedName,
362             mappedGroupAttributeIds.toArray(new String[0]));
363     }
364 
365     public static Properties getGroupMappings(long companyId)
366         throws Exception {
367 
368         Properties groupMappings = PropertiesUtil.load(
369             PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_GROUP_MAPPINGS));
370 
371         LogUtil.debug(_log, groupMappings);
372 
373         return groupMappings;
374     }
375 
376     public static NamingEnumeration<SearchResult> getGroups(
377             long companyId, LdapContext ctx, int maxResults)
378         throws Exception {
379 
380         String baseDN = PrefsPropsUtil.getString(
381             companyId, PropsKeys.LDAP_BASE_DN);
382         String groupFilter = PrefsPropsUtil.getString(
383             companyId, PropsKeys.LDAP_IMPORT_GROUP_SEARCH_FILTER);
384 
385         return getGroups(companyId, ctx, maxResults, baseDN, groupFilter);
386     }
387 
388     public static NamingEnumeration<SearchResult> getGroups(
389             long companyId, LdapContext ctx, int maxResults, String baseDN,
390             String groupFilter)
391         throws Exception {
392 
393         SearchControls cons = new SearchControls(
394             SearchControls.SUBTREE_SCOPE, maxResults, 0, null, false, false);
395 
396         return ctx.search(baseDN, groupFilter, cons);
397     }
398 
399     public static String getNameInNamespace(long companyId, Binding binding)
400         throws Exception {
401 
402         String baseDN = PrefsPropsUtil.getString(
403             companyId, PropsKeys.LDAP_BASE_DN);
404 
405         if (Validator.isNull(baseDN)) {
406             return binding.getName();
407         }
408         else {
409             StringBuilder sb = new StringBuilder();
410 
411             sb.append(binding.getName());
412             sb.append(StringPool.COMMA);
413             sb.append(baseDN);
414 
415             return sb.toString();
416         }
417     }
418 
419     public static Binding getUser(long companyId, String screenName)
420         throws Exception {
421 
422         LdapContext ctx = getContext(companyId);
423 
424         NamingEnumeration<SearchResult> enu = null;
425 
426         try {
427             if (ctx == null) {
428                 return null;
429             }
430 
431             String baseDN = PrefsPropsUtil.getString(
432                 companyId, PropsKeys.LDAP_BASE_DN);
433 
434             Properties userMappings = getUserMappings(companyId);
435 
436             StringBuilder filter = new StringBuilder();
437 
438             filter.append(StringPool.OPEN_PARENTHESIS);
439             filter.append(userMappings.getProperty("screenName"));
440             filter.append(StringPool.EQUAL);
441             filter.append(screenName);
442             filter.append(StringPool.CLOSE_PARENTHESIS);
443 
444             SearchControls cons = new SearchControls(
445                 SearchControls.SUBTREE_SCOPE, 1, 0, null, false, false);
446 
447             enu = ctx.search(
448                 baseDN, filter.toString(), cons);
449         }
450         catch (Exception e) {
451             throw e;
452         }
453         finally {
454             if (ctx != null) {
455                 ctx.close();
456             }
457         }
458 
459         if (enu.hasMoreElements()) {
460             Binding binding = enu.nextElement();
461 
462             enu.close();
463 
464             return binding;
465         }
466         else {
467             return null;
468         }
469     }
470 
471     public static Attributes getUserAttributes(
472             long companyId, LdapContext ctx, String fullDistinguishedName)
473         throws Exception {
474 
475         Properties userMappings = getUserMappings(companyId);
476 
477         String[] mappedUserAttributeIds = {
478             userMappings.getProperty("screenName"),
479             userMappings.getProperty("emailAddress"),
480             userMappings.getProperty("fullName"),
481             userMappings.getProperty("firstName"),
482             userMappings.getProperty("middleName"),
483             userMappings.getProperty("lastName"),
484             userMappings.getProperty("jobTitle"),
485             userMappings.getProperty("group")
486         };
487 
488         return _getAttributes(
489             ctx, fullDistinguishedName, mappedUserAttributeIds);
490     }
491 
492     public static Properties getUserMappings(long companyId) throws Exception {
493         Properties userMappings = PropertiesUtil.load(
494             PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_USER_MAPPINGS));
495 
496         LogUtil.debug(_log, userMappings);
497 
498         return userMappings;
499     }
500 
501     public static NamingEnumeration<SearchResult> getUsers(
502             long companyId, LdapContext ctx, int maxResults)
503         throws Exception {
504 
505         String baseDN = PrefsPropsUtil.getString(
506             companyId, PropsKeys.LDAP_BASE_DN);
507         String userFilter = PrefsPropsUtil.getString(
508             companyId, PropsKeys.LDAP_IMPORT_USER_SEARCH_FILTER);
509 
510         return getUsers(companyId, ctx, maxResults, baseDN, userFilter);
511     }
512 
513     public static NamingEnumeration<SearchResult> getUsers(
514             long companyId, LdapContext ctx, int maxResults, String baseDN,
515             String userFilter)
516         throws Exception {
517 
518         SearchControls cons = new SearchControls(
519             SearchControls.SUBTREE_SCOPE, maxResults, 0, null, false, false);
520 
521         return ctx.search(baseDN, userFilter, cons);
522     }
523 
524     public static String getUsersDN(long companyId) throws Exception {
525         return PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_USERS_DN);
526     }
527 
528     public static boolean hasUser(long companyId, String screenName)
529         throws Exception {
530 
531         if (getUser(companyId, screenName) != null) {
532             return true;
533         }
534         else {
535             return false;
536         }
537     }
538 
539     public static void importFromLDAP() throws Exception {
540         List<Company> companies = CompanyLocalServiceUtil.getCompanies(false);
541 
542         for (Company company : companies) {
543             importFromLDAP(company.getCompanyId());
544         }
545     }
546 
547     public static void importFromLDAP(long companyId) throws Exception {
548         if (!isImportEnabled(companyId)) {
549             return;
550         }
551 
552         LdapContext ctx = getContext(companyId);
553 
554         if (ctx == null) {
555             return;
556         }
557 
558         try {
559             String importMethod = PrefsPropsUtil.getString(
560                 companyId, PropsKeys.LDAP_IMPORT_METHOD);
561 
562             if (importMethod.equals(IMPORT_BY_USER)) {
563                 NamingEnumeration<SearchResult> enu = getUsers(
564                     companyId, ctx, 0);
565 
566                 // Loop through all LDAP users
567 
568                 while (enu.hasMoreElements()) {
569                     SearchResult result = enu.nextElement();
570 
571                     Attributes attributes = getUserAttributes(
572                         companyId, ctx, getNameInNamespace(companyId, result));
573 
574                     importLDAPUser(
575                         companyId, ctx, attributes, StringPool.BLANK, true);
576                 }
577 
578                 enu.close();
579             }
580             else if (importMethod.equals(IMPORT_BY_GROUP)) {
581                 NamingEnumeration<SearchResult> enu = getGroups(
582                     companyId, ctx, 0);
583 
584                 // Loop through all LDAP groups
585 
586                 while (enu.hasMoreElements()) {
587                     SearchResult result = enu.nextElement();
588 
589                     Attributes attributes = getGroupAttributes(
590                         companyId, ctx, getNameInNamespace(companyId, result),
591                         true);
592 
593                     importLDAPGroup(companyId, ctx, attributes, true);
594                 }
595 
596                 enu.close();
597             }
598         }
599         catch (Exception e) {
600             _log.error("Error importing LDAP users and groups", e);
601         }
602         finally {
603             if (ctx != null) {
604                 ctx.close();
605             }
606         }
607     }
608 
609     public static UserGroup importLDAPGroup(
610             long companyId, LdapContext ctx, Attributes attributes,
611             boolean importGroupMembership)
612         throws Exception {
613 
614         AttributesTransformer attributesTransformer =
615             AttributesTransformerFactory.getInstance();
616 
617         attributes = attributesTransformer.transformGroup(attributes);
618 
619         Properties groupMappings = getGroupMappings(companyId);
620 
621         LogUtil.debug(_log, groupMappings);
622 
623         String groupName = LDAPUtil.getAttributeValue(
624             attributes, groupMappings.getProperty("groupName")).toLowerCase();
625         String description = LDAPUtil.getAttributeValue(
626             attributes, groupMappings.getProperty("description"));
627 
628         // Get or create user group
629 
630         UserGroup userGroup = null;
631 
632         try {
633             userGroup = UserGroupLocalServiceUtil.getUserGroup(
634                 companyId, groupName);
635 
636             UserGroupLocalServiceUtil.updateUserGroup(
637                 companyId, userGroup.getUserGroupId(), groupName, description);
638         }
639         catch (NoSuchUserGroupException nsuge) {
640             if (_log.isDebugEnabled()) {
641                 _log.debug("Adding user group to portal " + groupName);
642             }
643 
644             long defaultUserId = UserLocalServiceUtil.getDefaultUserId(
645                 companyId);
646 
647             try {
648                 userGroup = UserGroupLocalServiceUtil.addUserGroup(
649                     defaultUserId, companyId, groupName, description);
650             }
651             catch (Exception e) {
652                 if (_log.isWarnEnabled()) {
653                     _log.warn("Could not create user group " + groupName);
654                 }
655 
656                 if (_log.isDebugEnabled()) {
657                     _log.debug(e, e);
658                 }
659             }
660         }
661 
662         // Import users and membership
663 
664         if (importGroupMembership && (userGroup != null)) {
665             Attribute attribute = attributes.get(
666                 groupMappings.getProperty("user"));
667 
668             if (attribute != null) {
669                 _importUsersAndMembershipFromLDAPGroup(
670                     companyId, ctx, userGroup.getUserGroupId(), attribute);
671             }
672         }
673 
674         return userGroup;
675     }
676 
677     public static User importLDAPUser(
678             long companyId, LdapContext ctx, Attributes attributes,
679             String password, boolean importGroupMembership)
680         throws Exception {
681 
682         AttributesTransformer attributesTransformer =
683             AttributesTransformerFactory.getInstance();
684 
685         attributes = attributesTransformer.transformUser(attributes);
686 
687         Properties userMappings = getUserMappings(companyId);
688 
689         LogUtil.debug(_log, userMappings);
690 
691         User defaultUser = UserLocalServiceUtil.getDefaultUser(companyId);
692 
693         boolean autoPassword = false;
694         boolean updatePassword = true;
695 
696         if (password.equals(StringPool.BLANK)) {
697             autoPassword = true;
698             updatePassword = false;
699         }
700 
701         long creatorUserId = 0;
702         boolean passwordReset = false;
703         boolean autoScreenName = false;
704         String screenName = LDAPUtil.getAttributeValue(
705             attributes, userMappings.getProperty("screenName")).toLowerCase();
706         String emailAddress = LDAPUtil.getAttributeValue(
707             attributes, userMappings.getProperty("emailAddress"));
708         Locale locale = defaultUser.getLocale();
709         String firstName = LDAPUtil.getAttributeValue(
710             attributes, userMappings.getProperty("firstName"));
711         String middleName = LDAPUtil.getAttributeValue(
712             attributes, userMappings.getProperty("middleName"));
713         String lastName = LDAPUtil.getAttributeValue(
714             attributes, userMappings.getProperty("lastName"));
715 
716         if (Validator.isNull(firstName) || Validator.isNull(lastName)) {
717             String fullName = LDAPUtil.getAttributeValue(
718                 attributes, userMappings.getProperty("fullName"));
719 
720             String[] names = LDAPUtil.splitFullName(fullName);
721 
722             firstName = names[0];
723             middleName = names[1];
724             lastName = names[2];
725         }
726 
727         int prefixId = 0;
728         int suffixId = 0;
729         boolean male = true;
730         int birthdayMonth = Calendar.JANUARY;
731         int birthdayDay = 1;
732         int birthdayYear = 1970;
733         String jobTitle = LDAPUtil.getAttributeValue(
734             attributes, userMappings.getProperty("jobTitle"));
735         long[] organizationIds = new long[0];
736         boolean sendEmail = false;
737 
738         if (_log.isDebugEnabled()) {
739             _log.debug(
740                 "Screen name " + screenName + " and email address " +
741                     emailAddress);
742         }
743 
744         if (Validator.isNull(screenName) || Validator.isNull(emailAddress)) {
745             if (_log.isWarnEnabled()) {
746                 _log.warn(
747                     "Cannot add user because screen name and email address " +
748                         "are required");
749             }
750 
751             return null;
752         }
753 
754         User user = null;
755 
756         try {
757 
758             // Find corresponding portal user
759 
760             String authType = PrefsPropsUtil.getString(
761                 companyId, PropsKeys.COMPANY_SECURITY_AUTH_TYPE,
762                 PropsValues.COMPANY_SECURITY_AUTH_TYPE);
763 
764             if (authType.equals(CompanyConstants.AUTH_TYPE_SN)) {
765                 user = UserLocalServiceUtil.getUserByScreenName(
766                     companyId, screenName);
767             }
768             else {
769                 user = UserLocalServiceUtil.getUserByEmailAddress(
770                     companyId, emailAddress);
771             }
772 
773             // Skip if is default user
774 
775             if (user.isDefaultUser()) {
776                 return user;
777             }
778 
779             // User already exists in the Liferay database. Skip import if user
780             // fields have been already synced, if import is part of a scheduled
781             // import, or if the LDAP entry has never been modified.
782 
783             Date ldapUserModifiedDate = null;
784 
785             String modifiedDate = LDAPUtil.getAttributeValue(
786                 attributes, "modifyTimestamp");
787 
788             try {
789                 if (Validator.isNull(modifiedDate)) {
790                     if (_log.isInfoEnabled()) {
791                         _log.info(
792                             "LDAP entry never modified, skipping user " +
793                                 user.getEmailAddress());
794                     }
795 
796                     return user;
797                 }
798                 else {
799                     DateFormat dateFormat = new SimpleDateFormat(
800                         "yyyyMMddHHmmss");
801 
802                     ldapUserModifiedDate = dateFormat.parse(modifiedDate);
803                 }
804 
805                 if (ldapUserModifiedDate.equals(user.getModifiedDate()) &&
806                     autoPassword) {
807 
808                     if (_log.isDebugEnabled()) {
809                         _log.debug(
810                             "User is already syncronized, skipping user " +
811                                 user.getEmailAddress());
812                     }
813 
814                     return user;
815                 }
816             }
817             catch (ParseException pe) {
818                 if (_log.isDebugEnabled()) {
819                     _log.debug(
820                         "Unable to parse LDAP modify timestamp " +
821                             modifiedDate);
822                 }
823 
824                 _log.debug(pe, pe);
825             }
826 
827             // LPS-443
828 
829             if (Validator.isNull(screenName)) {
830                 autoScreenName = true;
831             }
832 
833             if (autoScreenName) {
834                 ScreenNameGenerator screenNameGenerator =
835                     (ScreenNameGenerator)InstancePool.get(
836                         PropsValues.USERS_SCREEN_NAME_GENERATOR);
837 
838                 screenName = screenNameGenerator.generate(
839                     companyId, user.getUserId(), emailAddress);
840             }
841 
842             Contact contact = user.getContact();
843 
844             Calendar birthdayCal = CalendarFactoryUtil.getCalendar();
845 
846             birthdayCal.setTime(contact.getBirthday());
847 
848             birthdayMonth = birthdayCal.get(Calendar.MONTH);
849             birthdayDay = birthdayCal.get(Calendar.DATE);
850             birthdayYear = birthdayCal.get(Calendar.YEAR);
851 
852             // User exists so update user information
853 
854             if (updatePassword) {
855                 user = UserLocalServiceUtil.updatePassword(
856                     user.getUserId(), password, password, passwordReset, true);
857             }
858 
859             user = UserLocalServiceUtil.updateUser(
860                 user.getUserId(), password, user.isPasswordReset(), screenName,
861                 emailAddress, user.getLanguageId(), user.getTimeZoneId(),
862                 user.getGreeting(), user.getComments(), firstName, middleName,
863                 lastName, contact.getPrefixId(), contact.getSuffixId(),
864                 contact.getMale(), birthdayMonth, birthdayDay, birthdayYear,
865                 contact.getSmsSn(), contact.getAimSn(), contact.getFacebookSn(),
866                 contact.getIcqSn(), contact.getJabberSn(), contact.getMsnSn(),
867                 contact.getMySpaceSn(), contact.getSkypeSn(),
868                 contact.getTwitterSn(), contact.getYmSn(), jobTitle,
869                 user.getOrganizationIds());
870 
871             if (ldapUserModifiedDate != null) {
872                 UserLocalServiceUtil.updateModifiedDate(
873                     user.getUserId(), ldapUserModifiedDate);
874             }
875         }
876         catch (NoSuchUserException nsue) {
877 
878             // User does not exist so create
879 
880         }
881         catch (Exception e) {
882             _log.error(
883                 "Error updating user with screen name " + screenName +
884                     " and email address " + emailAddress,
885                 e);
886 
887             return null;
888         }
889 
890         if (user == null) {
891             try {
892                 if (_log.isDebugEnabled()) {
893                     _log.debug("Adding user to portal " + emailAddress);
894                 }
895 
896                 user = UserLocalServiceUtil.addUser(
897                     creatorUserId, companyId, autoPassword, password, password,
898                     autoScreenName, screenName, emailAddress, locale, firstName,
899                     middleName, lastName, prefixId, suffixId, male,
900                     birthdayMonth, birthdayDay, birthdayYear, jobTitle,
901                     organizationIds, sendEmail);
902             }
903             catch (Exception e) {
904                 _log.error(
905                     "Problem adding user with screen name " + screenName +
906                         " and email address " + emailAddress,
907                     e);
908             }
909         }
910 
911         // Import user groups and membership
912 
913         if (importGroupMembership && (user != null)) {
914             String userMappingsGroup = userMappings.getProperty("group");
915 
916             if (userMappingsGroup != null) {
917                 Attribute attribute = attributes.get(userMappingsGroup);
918 
919                 if (attribute != null) {
920                     _importGroupsAndMembershipFromLDAPUser(
921                         companyId, ctx, user.getUserId(), attribute);
922                 }
923             }
924         }
925 
926         return user;
927     }
928 
929     public static boolean isAuthEnabled(long companyId) throws SystemException {
930         if (PrefsPropsUtil.getBoolean(
931                 companyId, PropsKeys.LDAP_AUTH_ENABLED,
932                 PropsValues.LDAP_AUTH_ENABLED)) {
933 
934             return true;
935         }
936         else {
937             return false;
938         }
939     }
940 
941     public static boolean isExportEnabled(long companyId)
942         throws SystemException {
943 
944         if (PrefsPropsUtil.getBoolean(
945                 companyId, PropsKeys.LDAP_EXPORT_ENABLED,
946                 PropsValues.LDAP_EXPORT_ENABLED)) {
947 
948             return true;
949         }
950         else {
951             return false;
952         }
953     }
954 
955     public static boolean isImportEnabled(long companyId)
956         throws SystemException {
957 
958         if (PrefsPropsUtil.getBoolean(
959                 companyId, PropsKeys.LDAP_IMPORT_ENABLED,
960                 PropsValues.LDAP_IMPORT_ENABLED)) {
961 
962             return true;
963         }
964         else {
965             return false;
966         }
967     }
968 
969     public static boolean isImportOnStartup(long companyId)
970         throws SystemException {
971 
972         if (PrefsPropsUtil.getBoolean(
973                 companyId, PropsKeys.LDAP_IMPORT_ON_STARTUP)) {
974 
975             return true;
976         }
977         else {
978             return false;
979         }
980     }
981 
982     public static boolean isNtlmEnabled(long companyId)
983         throws SystemException {
984 
985         if (!isAuthEnabled(companyId)) {
986             return false;
987         }
988 
989         if (PrefsPropsUtil.getBoolean(
990                 companyId, PropsKeys.NTLM_AUTH_ENABLED,
991                 PropsValues.NTLM_AUTH_ENABLED)) {
992 
993             return true;
994         }
995         else {
996             return false;
997         }
998     }
999 
1000    public static boolean isPasswordPolicyEnabled(long companyId)
1001        throws SystemException {
1002
1003        if (PrefsPropsUtil.getBoolean(
1004                companyId, PropsKeys.LDAP_PASSWORD_POLICY_ENABLED,
1005                PropsValues.LDAP_PASSWORD_POLICY_ENABLED)) {
1006
1007            return true;
1008        }
1009        else {
1010            return false;
1011        }
1012    }
1013
1014    public static boolean isSiteMinderEnabled(long companyId)
1015        throws SystemException {
1016
1017        if (!isAuthEnabled(companyId)) {
1018            return false;
1019        }
1020
1021        if (PrefsPropsUtil.getBoolean(
1022                companyId, PropsKeys.SITEMINDER_AUTH_ENABLED,
1023                PropsValues.SITEMINDER_AUTH_ENABLED)) {
1024
1025            return true;
1026        }
1027        else {
1028            return false;
1029        }
1030    }
1031
1032    private static Attributes _getAttributes(
1033            LdapContext ctx, String fullDistinguishedName,
1034            String[] attributeIds)
1035        throws Exception {
1036
1037        Attributes attributes = null;
1038
1039        String[] auditAttributeIds = {
1040            "creatorsName", "createTimestamp", "modifiersName",
1041            "modifyTimestamp"
1042        };
1043
1044        if (attributeIds == null) {
1045
1046            // Get complete listing of LDAP attributes (slow)
1047
1048            attributes = ctx.getAttributes(fullDistinguishedName);
1049
1050            NamingEnumeration<? extends Attribute> enu = ctx.getAttributes(
1051                fullDistinguishedName, auditAttributeIds).getAll();
1052
1053            while (enu.hasMoreElements()) {
1054                attributes.put(enu.nextElement());
1055            }
1056
1057            enu.close();
1058        }
1059        else {
1060
1061            // Get specified LDAP attributes
1062
1063            int attributeCount = attributeIds.length + auditAttributeIds.length;
1064
1065            String[] allAttributeIds = new String[attributeCount];
1066
1067            System.arraycopy(
1068                attributeIds, 0, allAttributeIds, 0, attributeIds.length);
1069            System.arraycopy(
1070                auditAttributeIds, 0, allAttributeIds, attributeIds.length,
1071                auditAttributeIds.length);
1072
1073            attributes = ctx.getAttributes(
1074                fullDistinguishedName, allAttributeIds);
1075        }
1076
1077        return attributes;
1078    }
1079
1080    private static void _importGroupsAndMembershipFromLDAPUser(
1081            long companyId, LdapContext ctx, long userId, Attribute attr)
1082        throws Exception {
1083
1084        // Remove all user group membership from user
1085
1086        UserGroupLocalServiceUtil.clearUserUserGroups(userId);
1087
1088        for (int i = 0; i < attr.size(); i++) {
1089
1090            // Find group in LDAP
1091
1092            String fullGroupDN = (String)attr.get(i);
1093
1094            Attributes groupAttrs = null;
1095
1096            try {
1097                groupAttrs = getGroupAttributes(companyId, ctx, fullGroupDN);
1098            }
1099            catch (NameNotFoundException nnfe) {
1100                _log.error(
1101                    "LDAP group not found with fullGroupDN " + fullGroupDN);
1102
1103                _log.error(nnfe, nnfe);
1104
1105                continue;
1106            }
1107
1108            UserGroup userGroup = importLDAPGroup(
1109                companyId, ctx, groupAttrs, false);
1110
1111            // Add user to user group
1112
1113            if (userGroup != null) {
1114                if (_log.isDebugEnabled()) {
1115                    _log.debug(
1116                        "Adding " + userId + " to group " +
1117                            userGroup.getUserGroupId());
1118                }
1119
1120                UserLocalServiceUtil.addUserGroupUsers(
1121                    userGroup.getUserGroupId(), new long[] {userId});
1122            }
1123        }
1124    }
1125
1126    private static void _importUsersAndMembershipFromLDAPGroup(
1127            long companyId, LdapContext ctx, long userGroupId, Attribute attr)
1128        throws Exception {
1129
1130        // Remove all user membership from user group
1131
1132        UserLocalServiceUtil.clearUserGroupUsers(userGroupId);
1133
1134        for (int i = 0; i < attr.size(); i++) {
1135
1136            // Find user in LDAP
1137
1138            String fullUserDN = (String)attr.get(i);
1139
1140            Attributes userAttrs = null;
1141
1142            try {
1143                userAttrs = getUserAttributes(companyId, ctx, fullUserDN);
1144            }
1145            catch (NameNotFoundException nnfe) {
1146                _log.error(
1147                    "LDAP user not found with fullUserDN " + fullUserDN);
1148
1149                _log.error(nnfe, nnfe);
1150
1151                continue;
1152            }
1153
1154            User user = importLDAPUser(
1155                companyId, ctx, userAttrs, StringPool.BLANK, false);
1156
1157            // Add user to user group
1158
1159            if (user != null) {
1160                if (_log.isDebugEnabled()) {
1161                    _log.debug(
1162                        "Adding " + user.getUserId() + " to group " +
1163                            userGroupId);
1164                }
1165
1166                UserLocalServiceUtil.addUserGroupUsers(
1167                    userGroupId, new long[] {user.getUserId()});
1168            }
1169        }
1170    }
1171
1172    private static Log _log = LogFactoryUtil.getLog(PortalLDAPUtil.class);
1173
1174}