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