1   /**
2    * Copyright (c) 2000-2008 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 created
200 
201             }
202             else {
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(
237                         jobTitleMapping, user.getContact().getJobTitle());
238                 }
239 
240                 ModificationItem[] modItems = mods.getItems();
241 
242                 ctx.modifyAttributes(name, modItems);
243             }
244         }
245         catch (Exception e) {
246             throw e;
247         }
248         finally {
249             if (ctx != null) {
250                 ctx.close();
251             }
252         }
253     }
254 
255     public static String getAuthSearchFilter(
256             long companyId, String emailAddress, String screenName,
257             String userId)
258         throws SystemException {
259 
260         String filter = PrefsPropsUtil.getString(
261             companyId, PropsKeys.LDAP_AUTH_SEARCH_FILTER);
262 
263         if (_log.isDebugEnabled()) {
264             _log.debug("Search filter before transformation " + filter);
265         }
266 
267         filter = StringUtil.replace(
268             filter,
269             new String[] {
270                 "@company_id@", "@email_address@", "@screen_name@", "@user_id@"
271             },
272             new String[] {
273                 String.valueOf(companyId), emailAddress, screenName,
274                 userId
275             });
276 
277         if (_log.isDebugEnabled()) {
278             _log.debug("Search filter after transformation " + filter);
279         }
280 
281         return filter;
282     }
283 
284     public static LdapContext getContext(long companyId) throws Exception {
285         String baseProviderURL = PrefsPropsUtil.getString(
286             companyId, PropsKeys.LDAP_BASE_PROVIDER_URL);
287         String pricipal = PrefsPropsUtil.getString(
288             companyId, PropsKeys.LDAP_SECURITY_PRINCIPAL);
289         String credentials = PrefsPropsUtil.getString(
290             companyId, PropsKeys.LDAP_SECURITY_CREDENTIALS);
291 
292         return getContext(companyId, baseProviderURL, pricipal, credentials);
293     }
294 
295     public static LdapContext getContext(
296             long companyId, String providerURL, String pricipal,
297             String credentials)
298         throws Exception {
299 
300         Properties env = new Properties();
301 
302         env.put(
303             Context.INITIAL_CONTEXT_FACTORY,
304             PrefsPropsUtil.getString(
305                 companyId, PropsKeys.LDAP_FACTORY_INITIAL));
306         env.put(Context.PROVIDER_URL, providerURL);
307         env.put(Context.SECURITY_PRINCIPAL, pricipal);
308         env.put(Context.SECURITY_CREDENTIALS, credentials);
309         env.put(
310             Context.REFERRAL,
311             PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_REFERRAL));
312 
313         // Enable pooling
314 
315         env.put("com.sun.jndi.ldap.connect.pool", "true");
316         env.put("com.sun.jndi.ldap.connect.pool.maxsize","50");
317         env.put("com.sun.jndi.ldap.connect.pool.timeout", "10000");
318 
319         LogUtil.debug(_log, env);
320 
321         LdapContext ctx = null;
322 
323         try {
324             ctx = new InitialLdapContext(env, null);
325         }
326         catch (Exception e) {
327             if (_log.isWarnEnabled()) {
328                 _log.warn("Failed to bind to the LDAP server");
329             }
330 
331             if (_log.isDebugEnabled()) {
332                 _log.debug(e);
333             }
334         }
335 
336         return ctx;
337     }
338 
339     public static Attributes getGroupAttributes(
340             long companyId, LdapContext ctx, String fullDistinguishedName)
341         throws Exception {
342 
343         return getGroupAttributes(companyId, ctx, fullDistinguishedName, false);
344     }
345 
346     public static Attributes getGroupAttributes(
347             long companyId, LdapContext ctx, String fullDistinguishedName,
348             boolean includeReferenceAttributes)
349         throws Exception {
350 
351         Properties groupMappings = getGroupMappings(companyId);
352 
353         List<String> mappedGroupAttributeIds = new ArrayList<String>();
354 
355         mappedGroupAttributeIds.add(groupMappings.getProperty("groupName"));
356         mappedGroupAttributeIds.add(groupMappings.getProperty("description"));
357 
358         if (includeReferenceAttributes) {
359             mappedGroupAttributeIds.add(groupMappings.getProperty("user"));
360         }
361 
362         return _getAttributes(
363             ctx, fullDistinguishedName,
364             mappedGroupAttributeIds.toArray(new String[0]));
365     }
366 
367     public static Properties getGroupMappings(long companyId)
368         throws Exception {
369 
370         Properties groupMappings = PropertiesUtil.load(
371             PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_GROUP_MAPPINGS));
372 
373         LogUtil.debug(_log, groupMappings);
374 
375         return groupMappings;
376     }
377 
378     public static NamingEnumeration<SearchResult> getGroups(
379             long companyId, LdapContext ctx, int maxResults)
380         throws Exception {
381 
382         String baseDN = PrefsPropsUtil.getString(
383             companyId, PropsKeys.LDAP_BASE_DN);
384         String groupFilter = PrefsPropsUtil.getString(
385             companyId, PropsKeys.LDAP_IMPORT_GROUP_SEARCH_FILTER);
386 
387         return getGroups(companyId, ctx, maxResults, baseDN, groupFilter);
388     }
389 
390     public static NamingEnumeration<SearchResult> getGroups(
391             long companyId, LdapContext ctx, int maxResults, String baseDN,
392             String groupFilter)
393         throws Exception {
394 
395         SearchControls cons = new SearchControls(
396             SearchControls.SUBTREE_SCOPE, maxResults, 0, null, false, false);
397 
398         return ctx.search(baseDN, groupFilter, cons);
399     }
400 
401     public static String getNameInNamespace(long companyId, Binding binding)
402         throws Exception {
403 
404         String baseDN = PrefsPropsUtil.getString(
405             companyId, PropsKeys.LDAP_BASE_DN);
406 
407         if (Validator.isNull(baseDN)) {
408             return binding.getName();
409         }
410         else {
411             StringBuilder sb = new StringBuilder();
412 
413             sb.append(binding.getName());
414             sb.append(StringPool.COMMA);
415             sb.append(baseDN);
416 
417             return sb.toString();
418         }
419     }
420 
421     public static Binding getUser(long companyId, String screenName)
422         throws Exception {
423 
424         LdapContext ctx = getContext(companyId);
425 
426         NamingEnumeration<SearchResult> enu = null;
427 
428         try {
429             if (ctx == null) {
430                 return null;
431             }
432 
433             String baseDN = PrefsPropsUtil.getString(
434                 companyId, PropsKeys.LDAP_BASE_DN);
435 
436             Properties userMappings = getUserMappings(companyId);
437 
438             StringBuilder filter = new StringBuilder();
439 
440             filter.append(StringPool.OPEN_PARENTHESIS);
441             filter.append(userMappings.getProperty("screenName"));
442             filter.append(StringPool.EQUAL);
443             filter.append(screenName);
444             filter.append(StringPool.CLOSE_PARENTHESIS);
445 
446             SearchControls cons = new SearchControls(
447                 SearchControls.SUBTREE_SCOPE, 1, 0, null, false, false);
448 
449             enu = ctx.search(
450                 baseDN, filter.toString(), cons);
451         }
452         catch (Exception e) {
453             throw e;
454         }
455         finally {
456             if (ctx != null) {
457                 ctx.close();
458             }
459         }
460 
461         if (enu.hasMoreElements()) {
462             Binding binding = enu.nextElement();
463 
464             enu.close();
465 
466             return binding;
467         }
468         else {
469             return null;
470         }
471     }
472 
473     public static Attributes getUserAttributes(
474             long companyId, LdapContext ctx, String fullDistinguishedName)
475         throws Exception {
476 
477         Properties userMappings = getUserMappings(companyId);
478 
479         String[] mappedUserAttributeIds = {
480             userMappings.getProperty("screenName"),
481             userMappings.getProperty("emailAddress"),
482             userMappings.getProperty("fullName"),
483             userMappings.getProperty("firstName"),
484             userMappings.getProperty("middleName"),
485             userMappings.getProperty("lastName"),
486             userMappings.getProperty("jobTitle"),
487             userMappings.getProperty("group")
488         };
489 
490         return _getAttributes(
491             ctx, fullDistinguishedName, mappedUserAttributeIds);
492     }
493 
494     public static Properties getUserMappings(long companyId) throws Exception {
495         Properties userMappings = PropertiesUtil.load(
496             PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_USER_MAPPINGS));
497 
498         LogUtil.debug(_log, userMappings);
499 
500         return userMappings;
501     }
502 
503     public static NamingEnumeration<SearchResult> getUsers(
504             long companyId, LdapContext ctx, int maxResults)
505         throws Exception {
506 
507         String baseDN = PrefsPropsUtil.getString(
508             companyId, PropsKeys.LDAP_BASE_DN);
509         String userFilter = PrefsPropsUtil.getString(
510             companyId, PropsKeys.LDAP_IMPORT_USER_SEARCH_FILTER);
511 
512         return getUsers(companyId, ctx, maxResults, baseDN, userFilter);
513     }
514 
515     public static NamingEnumeration<SearchResult> getUsers(
516             long companyId, LdapContext ctx, int maxResults, String baseDN,
517             String userFilter)
518         throws Exception {
519 
520         SearchControls cons = new SearchControls(
521             SearchControls.SUBTREE_SCOPE, maxResults, 0, null, false, false);
522 
523         return ctx.search(baseDN, userFilter, cons);
524     }
525 
526     public static String getUsersDN(long companyId) throws Exception {
527         return PrefsPropsUtil.getString(companyId, PropsKeys.LDAP_USERS_DN);
528     }
529 
530     public static boolean hasUser(long companyId, String screenName)
531         throws Exception {
532 
533         if (getUser(companyId, screenName) != null) {
534             return true;
535         }
536         else {
537             return false;
538         }
539     }
540 
541     public static void importFromLDAP() throws Exception {
542         List<Company> companies = CompanyLocalServiceUtil.getCompanies();
543 
544         for (Company company : companies) {
545             importFromLDAP(company.getCompanyId());
546         }
547     }
548 
549     public static void importFromLDAP(long companyId) throws Exception {
550         if (!isImportEnabled(companyId)) {
551             return;
552         }
553 
554         LdapContext ctx = getContext(companyId);
555 
556         if (ctx == null) {
557             return;
558         }
559 
560         try {
561             String importMethod = PrefsPropsUtil.getString(
562                 companyId, PropsKeys.LDAP_IMPORT_METHOD);
563 
564             if (importMethod.equals(IMPORT_BY_USER)) {
565                 NamingEnumeration<SearchResult> enu = getUsers(
566                     companyId, ctx, 0);
567 
568                 // Loop through all LDAP users
569 
570                 while (enu.hasMoreElements()) {
571                     SearchResult result = enu.nextElement();
572 
573                     Attributes attrs = getUserAttributes(
574                         companyId, ctx, getNameInNamespace(companyId, result));
575 
576                     importLDAPUser(
577                         companyId, ctx, attrs, StringPool.BLANK, true);
578                 }
579 
580                 enu.close();
581             }
582             else if (importMethod.equals(IMPORT_BY_GROUP)) {
583                 NamingEnumeration<SearchResult> enu = getGroups(
584                     companyId, ctx, 0);
585 
586                 // Loop through all LDAP groups
587 
588                 while (enu.hasMoreElements()) {
589                     SearchResult result = enu.nextElement();
590 
591                     Attributes attrs = getGroupAttributes(
592                         companyId, ctx, getNameInNamespace(companyId, result),
593                         true);
594 
595                     importLDAPGroup(companyId, ctx, attrs, true);
596                 }
597 
598                 enu.close();
599             }
600         }
601         catch (Exception e) {
602             _log.error("Error importing LDAP users and groups", e);
603         }
604         finally {
605             if (ctx != null) {
606                 ctx.close();
607             }
608         }
609     }
610 
611     public static UserGroup importLDAPGroup(
612             long companyId, LdapContext ctx, Attributes attrs,
613             boolean importGroupMembership)
614         throws Exception {
615 
616         AttributesTransformer attrsTransformer =
617             AttributesTransformerFactory.getInstance();
618 
619         attrs = attrsTransformer.transformGroup(attrs);
620 
621         Properties groupMappings = getGroupMappings(companyId);
622 
623         LogUtil.debug(_log, groupMappings);
624 
625         String groupName = LDAPUtil.getAttributeValue(
626             attrs, groupMappings.getProperty("groupName")).toLowerCase();
627         String description = LDAPUtil.getAttributeValue(
628             attrs, groupMappings.getProperty("description"));
629 
630         // Get or create user group
631 
632         UserGroup userGroup = null;
633 
634         try {
635             userGroup = UserGroupLocalServiceUtil.getUserGroup(
636                 companyId, groupName);
637 
638             UserGroupLocalServiceUtil.updateUserGroup(
639                 companyId, userGroup.getUserGroupId(), groupName, description);
640         }
641         catch (NoSuchUserGroupException nsuge) {
642             if (_log.isDebugEnabled()) {
643                 _log.debug("Adding user group to portal " + groupName);
644             }
645 
646             long defaultUserId = UserLocalServiceUtil.getDefaultUserId(
647                 companyId);
648 
649             try {
650                 userGroup = UserGroupLocalServiceUtil.addUserGroup(
651                     defaultUserId, companyId, groupName, description);
652             }
653             catch (Exception e) {
654                 if (_log.isWarnEnabled()) {
655                     _log.warn("Could not create user group " + groupName);
656                 }
657 
658                 if (_log.isDebugEnabled()) {
659                     _log.debug(e, e);
660                 }
661             }
662         }
663 
664         // Import users and membership
665 
666         if (importGroupMembership && (userGroup != null)) {
667             Attribute attr = attrs.get(groupMappings.getProperty("user"));
668 
669             if (attr != null) {
670                 _importUsersAndMembershipFromLDAPGroup(
671                     companyId, ctx, userGroup.getUserGroupId(), attr);
672             }
673         }
674 
675         return userGroup;
676     }
677 
678     public static User importLDAPUser(
679             long companyId, LdapContext ctx, Attributes attrs, String password,
680             boolean importGroupMembership)
681         throws Exception {
682 
683         AttributesTransformer attrsTransformer =
684             AttributesTransformerFactory.getInstance();
685 
686         attrs = attrsTransformer.transformUser(attrs);
687 
688         Properties userMappings = getUserMappings(companyId);
689 
690         LogUtil.debug(_log, userMappings);
691 
692         User defaultUser = UserLocalServiceUtil.getDefaultUser(companyId);
693 
694         boolean autoPassword = false;
695         boolean updatePassword = true;
696 
697         if (password.equals(StringPool.BLANK)) {
698             autoPassword = true;
699             updatePassword = false;
700         }
701 
702         long creatorUserId = 0;
703         boolean passwordReset = false;
704         boolean autoScreenName = false;
705         String screenName = LDAPUtil.getAttributeValue(
706             attrs, userMappings.getProperty("screenName")).toLowerCase();
707         String emailAddress = LDAPUtil.getAttributeValue(
708             attrs, userMappings.getProperty("emailAddress"));
709         Locale locale = defaultUser.getLocale();
710         String firstName = LDAPUtil.getAttributeValue(
711             attrs, userMappings.getProperty("firstName"));
712         String middleName = LDAPUtil.getAttributeValue(
713             attrs, userMappings.getProperty("middleName"));
714         String lastName = LDAPUtil.getAttributeValue(
715             attrs, userMappings.getProperty("lastName"));
716 
717         if (Validator.isNull(firstName) || Validator.isNull(lastName)) {
718             String fullName = LDAPUtil.getAttributeValue(
719                 attrs, userMappings.getProperty("fullName"));
720 
721             String[] names = LDAPUtil.splitFullName(fullName);
722 
723             firstName = names[0];
724             middleName = names[1];
725             lastName = names[2];
726         }
727 
728         int prefixId = 0;
729         int suffixId = 0;
730         boolean male = true;
731         int birthdayMonth = Calendar.JANUARY;
732         int birthdayDay = 1;
733         int birthdayYear = 1970;
734         String jobTitle = LDAPUtil.getAttributeValue(
735             attrs, userMappings.getProperty("jobTitle"));
736         long[] organizationIds = new long[0];
737         boolean sendEmail = false;
738 
739         if (_log.isDebugEnabled()) {
740             _log.debug(
741                 "Screen name " + screenName + " and email address " +
742                     emailAddress);
743         }
744 
745         if (Validator.isNull(screenName) || Validator.isNull(emailAddress)) {
746             if (_log.isWarnEnabled()) {
747                 _log.warn(
748                     "Cannot add user because screen name and email address " +
749                         "are required");
750             }
751 
752             return null;
753         }
754 
755         User user = null;
756 
757         try {
758 
759             // Find corresponding portal user
760 
761             String authType = PrefsPropsUtil.getString(
762                 companyId, PropsKeys.COMPANY_SECURITY_AUTH_TYPE,
763                 PropsValues.COMPANY_SECURITY_AUTH_TYPE);
764 
765             if (authType.equals(CompanyConstants.AUTH_TYPE_SN)) {
766                 user = UserLocalServiceUtil.getUserByScreenName(
767                     companyId, screenName);
768             }
769             else {
770                 user = UserLocalServiceUtil.getUserByEmailAddress(
771                     companyId, emailAddress);
772             }
773 
774             // Skip if is default user
775 
776             if (user.isDefaultUser()) {
777                 return user;
778             }
779 
780             // User already exists in the Liferay database. Skip import if user
781             // fields have been already synced, if import is part of a scheduled
782             // import, or if the LDAP entry has never been modified.
783 
784             Date ldapUserModifiedDate = null;
785 
786             String modifiedDate = LDAPUtil.getAttributeValue(
787                 attrs, "modifyTimestamp");
788 
789             try {
790                 if (Validator.isNull(modifiedDate)) {
791                     if (_log.isInfoEnabled()) {
792                         _log.info(
793                             "LDAP entry never modified, skipping user " +
794                                 user.getEmailAddress());
795                     }
796 
797                     return user;
798                 }
799                 else {
800                     DateFormat dateFormat = new SimpleDateFormat(
801                         "yyyyMMddHHmmss");
802 
803                     ldapUserModifiedDate = dateFormat.parse(modifiedDate);
804                 }
805 
806                 if (ldapUserModifiedDate.equals(user.getModifiedDate()) &&
807                     autoPassword) {
808 
809                     if (_log.isDebugEnabled()) {
810                         _log.debug(
811                             "User is already syncronized, skipping user " +
812                                 user.getEmailAddress());
813                     }
814 
815                     return user;
816                 }
817             }
818             catch (ParseException pe) {
819                 if (_log.isDebugEnabled()) {
820                     _log.debug(
821                         "Unable to parse LDAP modify timestamp " +
822                             modifiedDate);
823                 }
824 
825                 _log.debug(pe, pe);
826             }
827 
828             // LPS-443
829 
830             if (Validator.isNull(screenName)) {
831                 autoScreenName = true;
832             }
833 
834             if (autoScreenName) {
835                 ScreenNameGenerator screenNameGenerator =
836                     (ScreenNameGenerator)InstancePool.get(
837                         PropsValues.USERS_SCREEN_NAME_GENERATOR);
838 
839                 screenName = screenNameGenerator.generate(
840                     companyId, user.getUserId(), emailAddress);
841             }
842 
843             Contact contact = user.getContact();
844 
845             Calendar birthdayCal = CalendarFactoryUtil.getCalendar();
846 
847             birthdayCal.setTime(contact.getBirthday());
848 
849             birthdayMonth = birthdayCal.get(Calendar.MONTH);
850             birthdayDay = birthdayCal.get(Calendar.DATE);
851             birthdayYear = birthdayCal.get(Calendar.YEAR);
852 
853             // User exists so update user information
854 
855             if (updatePassword) {
856                 user = UserLocalServiceUtil.updatePassword(
857                     user.getUserId(), password, password, passwordReset,
858                     true);
859             }
860 
861             user = UserLocalServiceUtil.updateUser(
862                 user.getUserId(), password, user.isPasswordReset(), screenName,
863                 emailAddress, user.getLanguageId(), user.getTimeZoneId(),
864                 user.getGreeting(), user.getComments(), firstName, middleName,
865                 lastName, contact.getPrefixId(), contact.getSuffixId(),
866                 contact.getMale(), birthdayMonth, birthdayDay, birthdayYear,
867                 contact.getSmsSn(), contact.getAimSn(), contact.getFacebookSn(),
868                 contact.getIcqSn(), contact.getJabberSn(), contact.getMsnSn(),
869                 contact.getMySpaceSn(), contact.getSkypeSn(),
870                 contact.getTwitterSn(), contact.getYmSn(), jobTitle,
871                 user.getOrganizationIds());
872 
873             if (ldapUserModifiedDate != null) {
874                 UserLocalServiceUtil.updateModifiedDate(
875                     user.getUserId(), ldapUserModifiedDate);
876             }
877         }
878         catch (NoSuchUserException nsue) {
879 
880             // User does not exist so create
881 
882         }
883 
884         if (user == null) {
885             try {
886                 if (_log.isDebugEnabled()) {
887                     _log.debug("Adding user to portal " + emailAddress);
888                 }
889 
890                 user = UserLocalServiceUtil.addUser(
891                     creatorUserId, companyId, autoPassword, password, password,
892                     autoScreenName, screenName, emailAddress, locale, firstName,
893                     middleName, lastName, prefixId, suffixId, male,
894                     birthdayMonth, birthdayDay, birthdayYear, jobTitle,
895                     organizationIds, sendEmail);
896             }
897             catch (Exception e) {
898                 _log.error(
899                     "Problem adding user with screen name " + screenName +
900                         " and email address " + emailAddress,
901                     e);
902             }
903         }
904 
905         // Import user groups and membership
906 
907         if (importGroupMembership && (user != null)) {
908             String userMappingsGroup = userMappings.getProperty("group");
909 
910             if (userMappingsGroup != null) {
911                 Attribute attr = attrs.get(userMappingsGroup);
912 
913                 if (attr != null) {
914                     _importGroupsAndMembershipFromLDAPUser(
915                         companyId, ctx, user.getUserId(), attr);
916                 }
917             }
918         }
919 
920         return user;
921     }
922 
923     public static boolean isAuthEnabled(long companyId) throws SystemException {
924         if (PrefsPropsUtil.getBoolean(
925                 companyId, PropsKeys.LDAP_AUTH_ENABLED,
926                 PropsValues.LDAP_AUTH_ENABLED)) {
927 
928             return true;
929         }
930         else {
931             return false;
932         }
933     }
934 
935     public static boolean isExportEnabled(long companyId)
936         throws SystemException {
937 
938         if (PrefsPropsUtil.getBoolean(
939                 companyId, PropsKeys.LDAP_EXPORT_ENABLED,
940                 PropsValues.LDAP_EXPORT_ENABLED)) {
941 
942             return true;
943         }
944         else {
945             return false;
946         }
947     }
948 
949     public static boolean isImportEnabled(long companyId)
950         throws SystemException {
951 
952         if (PrefsPropsUtil.getBoolean(
953                 companyId, PropsKeys.LDAP_IMPORT_ENABLED,
954                 PropsValues.LDAP_IMPORT_ENABLED)) {
955 
956             return true;
957         }
958         else {
959             return false;
960         }
961     }
962 
963     public static boolean isImportOnStartup(long companyId)
964         throws SystemException {
965 
966         if (PrefsPropsUtil.getBoolean(
967                 companyId, PropsKeys.LDAP_IMPORT_ON_STARTUP)) {
968 
969             return true;
970         }
971         else {
972             return false;
973         }
974     }
975 
976     public static boolean isNtlmEnabled(long companyId)
977         throws SystemException {
978 
979         if (!isAuthEnabled(companyId)) {
980             return false;
981         }
982 
983         if (PrefsPropsUtil.getBoolean(
984                 companyId, PropsKeys.NTLM_AUTH_ENABLED,
985                 PropsValues.NTLM_AUTH_ENABLED)) {
986 
987             return true;
988         }
989         else {
990             return false;
991         }
992     }
993 
994     public static boolean isPasswordPolicyEnabled(long companyId)
995         throws SystemException {
996 
997         if (PrefsPropsUtil.getBoolean(
998                 companyId, PropsKeys.LDAP_PASSWORD_POLICY_ENABLED,
999                 PropsValues.LDAP_PASSWORD_POLICY_ENABLED)) {
1000
1001            return true;
1002        }
1003        else {
1004            return false;
1005        }
1006    }
1007
1008    public static boolean isSiteMinderEnabled(long companyId)
1009        throws SystemException {
1010
1011        if (!isAuthEnabled(companyId)) {
1012            return false;
1013        }
1014
1015        if (PrefsPropsUtil.getBoolean(
1016                companyId, PropsKeys.SITEMINDER_AUTH_ENABLED,
1017                PropsValues.SITEMINDER_AUTH_ENABLED)) {
1018
1019            return true;
1020        }
1021        else {
1022            return false;
1023        }
1024    }
1025
1026    private static Attributes _getAttributes(
1027            LdapContext ctx, String fullDistinguishedName,
1028            String[] attributeIds)
1029        throws Exception {
1030
1031        Attributes attrs = null;
1032
1033        String[] auditAttributeIds = {
1034            "creatorsName", "createTimestamp", "modifiersName",
1035            "modifyTimestamp"
1036        };
1037
1038        if (attributeIds == null) {
1039
1040            // Get complete listing of LDAP attributes (slow)
1041
1042            attrs = ctx.getAttributes(fullDistinguishedName);
1043
1044            NamingEnumeration<? extends Attribute> enu = ctx.getAttributes(
1045                fullDistinguishedName, auditAttributeIds).getAll();
1046
1047            while (enu.hasMoreElements()) {
1048                attrs.put(enu.nextElement());
1049            }
1050
1051            enu.close();
1052        }
1053        else {
1054
1055            // Get specified LDAP attributes
1056
1057            int attributeCount = attributeIds.length + auditAttributeIds.length;
1058
1059            String[] allAttributeIds = new String[attributeCount];
1060
1061            System.arraycopy(
1062                attributeIds, 0, allAttributeIds, 0, attributeIds.length);
1063            System.arraycopy(
1064                auditAttributeIds, 0, allAttributeIds, attributeIds.length,
1065                auditAttributeIds.length);
1066
1067            attrs = ctx.getAttributes(fullDistinguishedName, allAttributeIds);
1068        }
1069
1070        return attrs;
1071    }
1072
1073    private static void _importGroupsAndMembershipFromLDAPUser(
1074            long companyId, LdapContext ctx, long userId, Attribute attr)
1075        throws Exception {
1076
1077        // Remove all user group membership from user
1078
1079        UserGroupLocalServiceUtil.clearUserUserGroups(userId);
1080
1081        for (int i = 0; i < attr.size(); i++) {
1082
1083            // Find group in LDAP
1084
1085            String fullGroupDN = (String)attr.get(i);
1086
1087            Attributes groupAttrs = null;
1088
1089            try {
1090                groupAttrs = getGroupAttributes(companyId, ctx, fullGroupDN);
1091            }
1092            catch (NameNotFoundException nnfe) {
1093                _log.error(
1094                    "LDAP group not found with fullGroupDN " + fullGroupDN);
1095
1096                _log.error(nnfe, nnfe);
1097
1098                continue;
1099            }
1100
1101            UserGroup userGroup = importLDAPGroup(
1102                companyId, ctx, groupAttrs, false);
1103
1104            // Add user to user group
1105
1106            if (userGroup != null) {
1107                if (_log.isDebugEnabled()) {
1108                    _log.debug(
1109                        "Adding " + userId + " to group " +
1110                            userGroup.getUserGroupId());
1111                }
1112
1113                UserLocalServiceUtil.addUserGroupUsers(
1114                    userGroup.getUserGroupId(), new long[] {userId});
1115            }
1116        }
1117    }
1118
1119    private static void _importUsersAndMembershipFromLDAPGroup(
1120            long companyId, LdapContext ctx, long userGroupId, Attribute attr)
1121        throws Exception {
1122
1123        // Remove all user membership from user group
1124
1125        UserLocalServiceUtil.clearUserGroupUsers(userGroupId);
1126
1127        for (int i = 0; i < attr.size(); i++) {
1128
1129            // Find user in LDAP
1130
1131            String fullUserDN = (String)attr.get(i);
1132
1133            Attributes userAttrs = null;
1134
1135            try {
1136                userAttrs = getUserAttributes(companyId, ctx, fullUserDN);
1137            }
1138            catch (NameNotFoundException nnfe) {
1139                _log.error(
1140                    "LDAP user not found with fullUserDN " + fullUserDN);
1141
1142                _log.error(nnfe, nnfe);
1143
1144                continue;
1145            }
1146
1147            User user = importLDAPUser(
1148                companyId, ctx, userAttrs, StringPool.BLANK, false);
1149
1150            // Add user to user group
1151
1152            if (user != null) {
1153                if (_log.isDebugEnabled()) {
1154                    _log.debug(
1155                        "Adding " + user.getUserId() + " to group " +
1156                            userGroupId);
1157                }
1158
1159                UserLocalServiceUtil.addUserGroupUsers(
1160                    userGroupId, new long[] {user.getUserId()});
1161            }
1162        }
1163    }
1164
1165    private static Log _log = LogFactoryUtil.getLog(PortalLDAPUtil.class);
1166
1167}