1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.security.ldap;
16  
17  import com.liferay.portal.NoSuchUserException;
18  import com.liferay.portal.NoSuchUserGroupException;
19  import com.liferay.portal.UserEmailAddressException;
20  import com.liferay.portal.UserScreenNameException;
21  import com.liferay.portal.kernel.log.Log;
22  import com.liferay.portal.kernel.log.LogFactoryUtil;
23  import com.liferay.portal.kernel.log.LogUtil;
24  import com.liferay.portal.kernel.util.ArrayUtil;
25  import com.liferay.portal.kernel.util.CalendarFactoryUtil;
26  import com.liferay.portal.kernel.util.DateFormatFactoryUtil;
27  import com.liferay.portal.kernel.util.PropsKeys;
28  import com.liferay.portal.kernel.util.StringBundler;
29  import com.liferay.portal.kernel.util.StringPool;
30  import com.liferay.portal.kernel.util.StringUtil;
31  import com.liferay.portal.kernel.util.Validator;
32  import com.liferay.portal.model.Company;
33  import com.liferay.portal.model.CompanyConstants;
34  import com.liferay.portal.model.Contact;
35  import com.liferay.portal.model.ContactConstants;
36  import com.liferay.portal.model.User;
37  import com.liferay.portal.model.UserGroup;
38  import com.liferay.portal.model.UserGroupRole;
39  import com.liferay.portal.security.auth.ScreenNameGenerator;
40  import com.liferay.portal.security.auth.ScreenNameGeneratorFactory;
41  import com.liferay.portal.service.CompanyLocalServiceUtil;
42  import com.liferay.portal.service.ServiceContext;
43  import com.liferay.portal.service.UserGroupLocalServiceUtil;
44  import com.liferay.portal.service.UserLocalServiceUtil;
45  import com.liferay.portal.util.PrefsPropsUtil;
46  import com.liferay.portal.util.PropsValues;
47  import com.liferay.util.ldap.LDAPUtil;
48  
49  import java.text.DateFormat;
50  import java.text.ParseException;
51  
52  import java.util.ArrayList;
53  import java.util.Calendar;
54  import java.util.Date;
55  import java.util.List;
56  import java.util.Locale;
57  import java.util.Properties;
58  
59  import javax.naming.Binding;
60  import javax.naming.NameNotFoundException;
61  import javax.naming.directory.Attribute;
62  import javax.naming.directory.Attributes;
63  import javax.naming.directory.SearchResult;
64  import javax.naming.ldap.LdapContext;
65  
66  /**
67   * <a href="PortalLDAPImporter.java.html"><b><i>View Source</i></b></a>
68   *
69   * @author Edward Han
70   * @author Michael C. Han
71   * @author Brian Wing Shun Chan
72   */
73  public class PortalLDAPImporter {
74  
75      public static final String IMPORT_BY_GROUP = "group";
76  
77      public static final String IMPORT_BY_USER = "user";
78  
79      public static void importFromLDAP() throws Exception {
80          List<Company> companies = CompanyLocalServiceUtil.getCompanies(false);
81  
82          for (Company company : companies) {
83              PortalLDAPImporter.importFromLDAP(company.getCompanyId());
84          }
85      }
86  
87      public static void importFromLDAP(long companyId) throws Exception {
88          if (!LDAPSettingsUtil.isImportEnabled(companyId)) {
89              return;
90          }
91  
92          long[] ldapServerIds = StringUtil.split(
93              PrefsPropsUtil.getString(companyId, "ldap.server.ids"), 0L);
94  
95          for (long ldapServerId : ldapServerIds) {
96              importFromLDAP(ldapServerId, companyId);
97          }
98      }
99  
100     public static void importFromLDAP(long ldapServerId, long companyId)
101         throws Exception {
102 
103         if (!LDAPSettingsUtil.isImportEnabled(companyId)) {
104             return;
105         }
106 
107         LdapContext ldapContext = PortalLDAPUtil.getContext(
108             ldapServerId, companyId);
109 
110         if (ldapContext == null) {
111             return;
112         }
113 
114         try {
115             String importMethod = PrefsPropsUtil.getString(
116                 companyId, PropsKeys.LDAP_IMPORT_METHOD);
117 
118             if (importMethod.equals(IMPORT_BY_USER)) {
119                 List<SearchResult> searchResults = PortalLDAPUtil.getUsers(
120                     ldapServerId, companyId, ldapContext, 0);
121 
122                 // Loop through all LDAP users
123 
124                 for (SearchResult searchResult : searchResults) {
125                     Attributes attributes = PortalLDAPUtil.getUserAttributes(
126                         ldapServerId, companyId, ldapContext,
127                         PortalLDAPUtil.getNameInNamespace(
128                             ldapServerId, companyId, searchResult));
129 
130                     try {
131                         importLDAPUser(
132                             ldapServerId, companyId, ldapContext, attributes,
133                             StringPool.BLANK, true);
134                     }
135                     catch (Exception e) {
136                         _log.error("Unable to import user: " + searchResult, e);
137                     }
138                 }
139             }
140             else if (importMethod.equals(IMPORT_BY_GROUP)) {
141                 List<SearchResult> searchResults = PortalLDAPUtil.getGroups(
142                     ldapServerId, companyId, ldapContext, 0);
143 
144                 // Loop through all LDAP groups
145 
146                 for (SearchResult searchResult : searchResults) {
147                     Attributes attributes = PortalLDAPUtil.getGroupAttributes(
148                         ldapServerId, companyId, ldapContext,
149                         PortalLDAPUtil.getNameInNamespace(
150                             ldapServerId, companyId, searchResult),
151                         true);
152 
153                     importLDAPGroup(
154                         ldapServerId, companyId, ldapContext, attributes,
155                         true);
156                 }
157             }
158         }
159         catch (Exception e) {
160             _log.error("Error importing LDAP users and groups", e);
161         }
162         finally {
163             if (ldapContext != null) {
164                 ldapContext.close();
165             }
166         }
167     }
168 
169     public static UserGroup importLDAPGroup(
170             long ldapServerId, long companyId, LdapContext ldapContext,
171             Attributes attributes, boolean importGroupMembership)
172         throws Exception {
173 
174         String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
175 
176         AttributesTransformer attributesTransformer =
177             AttributesTransformerFactory.getInstance();
178 
179         attributes = attributesTransformer.transformGroup(attributes);
180 
181         Properties groupMappings = LDAPSettingsUtil.getGroupMappings(
182             ldapServerId, companyId);
183 
184         LogUtil.debug(_log, groupMappings);
185 
186         String groupName = LDAPUtil.getAttributeValue(
187             attributes, groupMappings.getProperty("groupName")).toLowerCase();
188         String description = LDAPUtil.getAttributeValue(
189             attributes, groupMappings.getProperty("description"));
190 
191         // Get or create user group
192 
193         UserGroup userGroup = null;
194 
195         try {
196             userGroup = UserGroupLocalServiceUtil.getUserGroup(
197                 companyId, groupName);
198 
199             UserGroupLocalServiceUtil.updateUserGroup(
200                 companyId, userGroup.getUserGroupId(), groupName, description);
201         }
202         catch (NoSuchUserGroupException nsuge) {
203             if (_log.isDebugEnabled()) {
204                 _log.debug("Adding user group to portal " + groupName);
205             }
206 
207             long defaultUserId = UserLocalServiceUtil.getDefaultUserId(
208                 companyId);
209 
210             try {
211                 userGroup = UserGroupLocalServiceUtil.addUserGroup(
212                     defaultUserId, companyId, groupName, description);
213             }
214             catch (Exception e) {
215                 if (_log.isWarnEnabled()) {
216                     _log.warn("Could not create user group " + groupName);
217                 }
218 
219                 if (_log.isDebugEnabled()) {
220                     _log.debug(e, e);
221                 }
222             }
223         }
224 
225         // Import users and membership
226 
227         if (!importGroupMembership || (userGroup == null)) {
228             return userGroup;
229         }
230 
231         Attribute attribute = attributes.get(groupMappings.getProperty("user"));
232 
233         if (attribute == null) {
234             return userGroup;
235         }
236 
237         String baseDN = PrefsPropsUtil.getString(
238             companyId, PropsKeys.LDAP_BASE_DN + postfix);
239 
240         StringBundler sb = new StringBundler(7);
241 
242         sb.append("(&");
243         sb.append(
244             PrefsPropsUtil.getString(
245                 companyId,
246                 PropsKeys.LDAP_IMPORT_GROUP_SEARCH_FILTER + postfix));
247         sb.append("(");
248         sb.append(groupMappings.getProperty("groupName"));
249         sb.append("=");
250         sb.append(
251             LDAPUtil.getAttributeValue(
252                 attributes, groupMappings.getProperty("groupName")));
253         sb.append("))");
254 
255         attribute = PortalLDAPUtil.getMultivaluedAttribute(
256             companyId, ldapContext, baseDN, sb.toString(), attribute);
257 
258         _importUsersAndMembershipFromLDAPGroup(
259             ldapServerId, companyId, ldapContext, userGroup.getUserGroupId(),
260             attribute);
261 
262         return userGroup;
263     }
264 
265     public static User importLDAPUser(
266             long ldapServerId, long companyId, LdapContext ldapContext,
267             Attributes attributes, String password,
268             boolean importGroupMembership)
269         throws Exception {
270 
271         LDAPUserTransactionThreadLocal.setOriginatesFromLDAP(true);
272 
273         try {
274             return _importLDAPUser(
275                 ldapServerId, companyId, ldapContext, attributes, password,
276                 importGroupMembership);
277         }
278         finally {
279             LDAPUserTransactionThreadLocal.setOriginatesFromLDAP(false);
280         }
281     }
282 
283     private static void _importGroupsAndMembershipFromLDAPUser(
284             long ldapServerId, long companyId, LdapContext ldapContext,
285             long userId, Attribute attribute)
286         throws Exception {
287 
288         List<Long> newUserGroupIds = new ArrayList<Long>(attribute.size());
289 
290         for (int i = 0; i < attribute.size(); i++) {
291 
292             // Find group in LDAP
293 
294             String fullGroupDN = (String)attribute.get(i);
295 
296             Attributes groupAttributes = null;
297 
298             try {
299                 groupAttributes = PortalLDAPUtil.getGroupAttributes(
300                     ldapServerId, companyId, ldapContext, fullGroupDN);
301             }
302             catch (NameNotFoundException nnfe) {
303                 _log.error(
304                     "LDAP group not found with fullGroupDN " + fullGroupDN,
305                     nnfe);
306 
307                 continue;
308             }
309 
310             UserGroup userGroup = PortalLDAPImporter.importLDAPGroup(
311                 ldapServerId, companyId, ldapContext, groupAttributes, false);
312 
313             // Add user to user group
314 
315             if (userGroup != null) {
316                 if (_log.isDebugEnabled()) {
317                     _log.debug(
318                         "Adding " + userId + " to group " +
319                             userGroup.getUserGroupId());
320                 }
321 
322                 newUserGroupIds.add(userGroup.getUserGroupId());
323             }
324         }
325 
326         UserGroupLocalServiceUtil.setUserUserGroups(
327             userId,
328             ArrayUtil.toArray(
329                 newUserGroupIds.toArray(new Long[newUserGroupIds.size()])));
330     }
331 
332     private static User _importLDAPUser(
333             long ldapServerId, long companyId, LdapContext ldapContext,
334             Attributes attributes, String password,
335             boolean importGroupMembership)
336         throws Exception {
337 
338         AttributesTransformer attributesTransformer =
339             AttributesTransformerFactory.getInstance();
340 
341         attributes = attributesTransformer.transformUser(attributes);
342 
343         Properties userMappings = LDAPSettingsUtil.getUserMappings(
344             ldapServerId, companyId);
345 
346         LogUtil.debug(_log, userMappings);
347 
348         User defaultUser = UserLocalServiceUtil.getDefaultUser(companyId);
349 
350         boolean autoPassword = false;
351         boolean updatePassword = true;
352 
353         if (password.equals(StringPool.BLANK)) {
354             autoPassword = true;
355             updatePassword = false;
356         }
357 
358         long creatorUserId = 0;
359         boolean passwordReset = false;
360         boolean autoScreenName = false;
361         String screenName = LDAPUtil.getAttributeValue(
362             attributes, userMappings.getProperty("screenName")).toLowerCase();
363         String emailAddress = LDAPUtil.getAttributeValue(
364             attributes, userMappings.getProperty("emailAddress"));
365         String openId = StringPool.BLANK;
366         Locale locale = defaultUser.getLocale();
367         String firstName = LDAPUtil.getAttributeValue(
368             attributes, userMappings.getProperty("firstName"));
369         String middleName = LDAPUtil.getAttributeValue(
370             attributes, userMappings.getProperty("middleName"));
371         String lastName = LDAPUtil.getAttributeValue(
372             attributes, userMappings.getProperty("lastName"));
373 
374         if (Validator.isNull(firstName) || Validator.isNull(lastName)) {
375             String fullName = LDAPUtil.getAttributeValue(
376                 attributes, userMappings.getProperty("fullName"));
377 
378             String[] names = LDAPUtil.splitFullName(fullName);
379 
380             firstName = names[0];
381             middleName = names[1];
382             lastName = names[2];
383         }
384 
385         int prefixId = 0;
386         int suffixId = 0;
387         boolean male = true;
388         int birthdayMonth = Calendar.JANUARY;
389         int birthdayDay = 1;
390         int birthdayYear = 1970;
391         String jobTitle = LDAPUtil.getAttributeValue(
392             attributes, userMappings.getProperty("jobTitle"));
393         long[] groupIds = null;
394         long[] organizationIds = null;
395         long[] roleIds = null;
396         List<UserGroupRole> userGroupRoles = null;
397         long[] userGroupIds = null;
398         boolean sendEmail = false;
399         ServiceContext serviceContext = new ServiceContext();
400 
401         if (_log.isDebugEnabled()) {
402             _log.debug(
403                 "Screen name " + screenName + " and email address " +
404                     emailAddress);
405         }
406 
407         if (Validator.isNull(screenName) &&
408             !PrefsPropsUtil.getBoolean(
409                 companyId, PropsKeys.USERS_SCREEN_NAME_ALWAYS_AUTOGENERATE)) {
410 
411             throw new UserScreenNameException(
412                 "Screen name cannot be null for " +
413                     ContactConstants.getFullName(
414                         firstName, middleName, lastName));
415         }
416 
417         if (Validator.isNull(emailAddress) &&
418             PrefsPropsUtil.getBoolean(
419                 companyId, PropsKeys.USERS_EMAIL_ADDRESS_REQUIRED)) {
420 
421             throw new UserEmailAddressException(
422                 "Email address cannot be null for " +
423                     ContactConstants.getFullName(
424                         firstName, middleName, lastName));
425         }
426 
427         User user = null;
428 
429         try {
430 
431             // Find corresponding portal user
432 
433             String authType = PrefsPropsUtil.getString(
434                 companyId, PropsKeys.COMPANY_SECURITY_AUTH_TYPE,
435                 PropsValues.COMPANY_SECURITY_AUTH_TYPE);
436 
437             if (authType.equals(CompanyConstants.AUTH_TYPE_SN)) {
438                 user = UserLocalServiceUtil.getUserByScreenName(
439                     companyId, screenName);
440             }
441             else {
442                 user = UserLocalServiceUtil.getUserByEmailAddress(
443                     companyId, emailAddress);
444             }
445 
446             // Skip if is default user
447 
448             if (user.isDefaultUser()) {
449                 return user;
450             }
451 
452             // User already exists in the Liferay database. Skip import if user
453             // fields have been already synced, if import is part of a scheduled
454             // import, or if the LDAP entry has never been modified.
455 
456             Date ldapUserModifiedDate = null;
457 
458             String modifiedDate = LDAPUtil.getAttributeValue(
459                 attributes, "modifyTimestamp");
460 
461             try {
462                 if (Validator.isNull(modifiedDate)) {
463                     if (_log.isInfoEnabled()) {
464                         _log.info(
465                             "LDAP entry never modified, skipping user " +
466                                 user.getEmailAddress());
467                     }
468 
469                     return user;
470                 }
471                 else {
472                     DateFormat dateFormat =
473                         DateFormatFactoryUtil.getSimpleDateFormat(
474                             "yyyyMMddHHmmss");
475 
476                     ldapUserModifiedDate = dateFormat.parse(modifiedDate);
477                 }
478 
479                 if (ldapUserModifiedDate.equals(user.getModifiedDate()) &&
480                     autoPassword) {
481 
482                     if (_log.isDebugEnabled()) {
483                         _log.debug(
484                             "User is already syncronized, skipping user " +
485                                 user.getEmailAddress());
486                     }
487 
488                     return user;
489                 }
490             }
491             catch (ParseException pe) {
492                 if (_log.isDebugEnabled()) {
493                     _log.debug(
494                         "Unable to parse LDAP modify timestamp " + modifiedDate,
495                         pe);
496                 }
497             }
498 
499             // LPS-443
500 
501             if (Validator.isNull(screenName)) {
502                 autoScreenName = true;
503             }
504 
505             if (autoScreenName) {
506                 ScreenNameGenerator screenNameGenerator =
507                     ScreenNameGeneratorFactory.getInstance();
508 
509                 screenName = screenNameGenerator.generate(
510                     companyId, user.getUserId(), emailAddress);
511             }
512 
513             Contact contact = user.getContact();
514 
515             Calendar birthdayCal = CalendarFactoryUtil.getCalendar();
516 
517             birthdayCal.setTime(contact.getBirthday());
518 
519             birthdayMonth = birthdayCal.get(Calendar.MONTH);
520             birthdayDay = birthdayCal.get(Calendar.DATE);
521             birthdayYear = birthdayCal.get(Calendar.YEAR);
522 
523             // User exists so update user information
524 
525             if (updatePassword) {
526                 user = UserLocalServiceUtil.updatePassword(
527                     user.getUserId(), password, password, passwordReset, true);
528             }
529 
530             user = UserLocalServiceUtil.updateUser(
531                 user.getUserId(), password, StringPool.BLANK, StringPool.BLANK,
532                 user.isPasswordReset(), user.getReminderQueryQuestion(),
533                 user.getReminderQueryAnswer(), screenName, emailAddress, openId,
534                 user.getLanguageId(), user.getTimeZoneId(), user.getGreeting(),
535                 user.getComments(), firstName, middleName, lastName,
536                 contact.getPrefixId(), contact.getSuffixId(), contact.getMale(),
537                 birthdayMonth, birthdayDay, birthdayYear, contact.getSmsSn(),
538                 contact.getAimSn(), contact.getFacebookSn(), contact.getIcqSn(),
539                 contact.getJabberSn(), contact.getMsnSn(),
540                 contact.getMySpaceSn(), contact.getSkypeSn(),
541                 contact.getTwitterSn(), contact.getYmSn(), jobTitle, groupIds,
542                 organizationIds, roleIds, userGroupRoles, userGroupIds,
543                 serviceContext);
544 
545             if (ldapUserModifiedDate != null) {
546                 UserLocalServiceUtil.updateModifiedDate(
547                     user.getUserId(), ldapUserModifiedDate);
548             }
549         }
550         catch (NoSuchUserException nsue) {
551 
552             // User does not exist so create
553 
554         }
555         catch (Exception e) {
556             _log.error(
557                 "Error updating user with screen name " + screenName +
558                 " and email address " + emailAddress,
559                 e);
560 
561             return null;
562         }
563 
564         if (user == null) {
565             try {
566                 if (_log.isDebugEnabled()) {
567                     _log.debug(
568                         "Adding user to portal " + emailAddress);
569                 }
570 
571                 user = UserLocalServiceUtil.addUser(
572                     creatorUserId, companyId, autoPassword, password, password,
573                     autoScreenName, screenName, emailAddress, openId, locale,
574                     firstName, middleName, lastName, prefixId, suffixId, male,
575                     birthdayMonth, birthdayDay, birthdayYear, jobTitle,
576                     groupIds, organizationIds, roleIds, userGroupIds, sendEmail,
577                     serviceContext);
578             }
579             catch (Exception e) {
580                 _log.error(
581                     "Problem adding user with screen name " + screenName +
582                     " and email address " + emailAddress,
583                     e);
584             }
585         }
586 
587         // Import user groups and membership
588 
589         if (!importGroupMembership || (user == null)) {
590             return user;
591         }
592 
593         String userMappingsGroup = userMappings.getProperty("group");
594 
595         if (userMappingsGroup == null) {
596             return user;
597         }
598 
599         Attribute attribute = attributes.get(userMappingsGroup);
600 
601         if (attribute == null) {
602             return user;
603         }
604 
605         attribute.clear();
606 
607         Properties groupMappings = LDAPSettingsUtil.getGroupMappings(
608             ldapServerId, companyId);
609 
610         String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
611 
612         String baseDN = PrefsPropsUtil.getString(
613             companyId, PropsKeys.LDAP_BASE_DN + postfix);
614 
615         Binding binding = PortalLDAPUtil.getUser(
616             ldapServerId, companyId, screenName);
617 
618         String fullUserDN = PortalLDAPUtil.getNameInNamespace(
619             ldapServerId, companyId, binding);
620 
621         StringBundler sb = new StringBundler(9);
622 
623         sb.append(StringPool.OPEN_PARENTHESIS);
624         sb.append(StringPool.AMPERSAND);
625         sb.append(
626             PrefsPropsUtil.getString(
627                 companyId,
628                 PropsKeys.LDAP_IMPORT_GROUP_SEARCH_FILTER + postfix));
629         sb.append(StringPool.OPEN_PARENTHESIS);
630         sb.append(groupMappings.getProperty("user"));
631         sb.append(StringPool.EQUAL);
632         sb.append(fullUserDN);
633         sb.append(StringPool.CLOSE_PARENTHESIS);
634         sb.append(StringPool.CLOSE_PARENTHESIS);
635 
636         List<SearchResult> searchResults = PortalLDAPUtil.searchLDAP(
637             companyId, ldapContext, 0, baseDN, sb.toString(), null);
638 
639         for (SearchResult searchResult : searchResults) {
640             String fullGroupDN = PortalLDAPUtil.getNameInNamespace(
641                 ldapServerId, companyId, searchResult);
642 
643             attribute.add(fullGroupDN);
644         }
645 
646         _importGroupsAndMembershipFromLDAPUser(
647             ldapServerId, companyId, ldapContext, user.getUserId(), attribute);
648 
649         return user;
650     }
651 
652     private static void _importUsersAndMembershipFromLDAPGroup(
653             long ldapServerId, long companyId, LdapContext ldapContext,
654             long userGroupId, Attribute attribute)
655         throws Exception {
656 
657         List<Long> newUserIds = new ArrayList<Long>(attribute.size());
658 
659         for (int i = 0; i < attribute.size(); i++) {
660 
661             // Find user in LDAP
662 
663             String fullUserDN = (String)attribute.get(i);
664 
665             Attributes userAttributes = null;
666 
667             try {
668                 userAttributes = PortalLDAPUtil.getUserAttributes(
669                     ldapServerId, companyId, ldapContext, fullUserDN);
670             }
671             catch (NameNotFoundException nnfe) {
672                 _log.error(
673                     "LDAP user not found with fullUserDN " + fullUserDN, nnfe);
674 
675                 continue;
676             }
677 
678             User user = PortalLDAPImporter.importLDAPUser(
679                 ldapServerId, companyId, ldapContext, userAttributes,
680                 StringPool.BLANK, false);
681 
682             // Add user to user group
683 
684             if (user != null) {
685                 if (_log.isDebugEnabled()) {
686                     _log.debug(
687                         "Adding " + user.getUserId() + " to group " +
688                             userGroupId);
689                 }
690 
691                 newUserIds.add(user.getUserId());
692             }
693         }
694 
695         UserLocalServiceUtil.setUserGroupUsers(
696             userGroupId,
697             ArrayUtil.toArray(newUserIds.toArray(new Long[newUserIds.size()])));
698     }
699 
700     private static Log _log = LogFactoryUtil.getLog(PortalLDAPImporter.class);
701 
702 }