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