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