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