001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.security.ldap;
016    
017    import com.liferay.portal.PwdEncryptorException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.util.GetterUtil;
022    import com.liferay.portal.kernel.util.PropsKeys;
023    import com.liferay.portal.kernel.util.StringBundler;
024    import com.liferay.portal.kernel.util.StringPool;
025    import com.liferay.portal.kernel.util.Validator;
026    import com.liferay.portal.model.Contact;
027    import com.liferay.portal.model.Image;
028    import com.liferay.portal.model.User;
029    import com.liferay.portal.model.UserGroup;
030    import com.liferay.portal.security.pwd.PwdEncryptor;
031    import com.liferay.portal.service.ImageLocalServiceUtil;
032    import com.liferay.portal.util.PrefsPropsUtil;
033    import com.liferay.portlet.expando.model.ExpandoBridge;
034    import com.liferay.portlet.expando.util.ExpandoConverterUtil;
035    
036    import java.io.Serializable;
037    
038    import java.util.HashMap;
039    import java.util.List;
040    import java.util.Map;
041    import java.util.Properties;
042    
043    import javax.naming.Binding;
044    import javax.naming.directory.Attribute;
045    import javax.naming.directory.Attributes;
046    import javax.naming.directory.BasicAttribute;
047    import javax.naming.directory.BasicAttributes;
048    import javax.naming.directory.DirContext;
049    
050    import org.apache.commons.beanutils.PropertyUtils;
051    
052    /**
053     * @author Michael C. Han
054     * @author Brian Wing Shun Chan
055     * @author Marcellus Tavares
056     * @author Wesley Gong
057     */
058    public class DefaultPortalToLDAPConverter implements PortalToLDAPConverter {
059    
060            public DefaultPortalToLDAPConverter() {
061                    _reservedUserFieldNames.put(
062                            UserConverterKeys.GROUP, UserConverterKeys.GROUP);
063                    _reservedUserFieldNames.put(
064                            UserConverterKeys.PASSWORD, UserConverterKeys.PASSWORD);
065                    _reservedUserFieldNames.put(
066                            UserConverterKeys.PORTRAIT, UserConverterKeys.PORTRAIT);
067                    _reservedUserFieldNames.put(
068                            UserConverterKeys.SCREEN_NAME, UserConverterKeys.SCREEN_NAME);
069            }
070    
071            @Override
072            public String getGroupDNName(
073                            long ldapServerId, UserGroup userGroup, Properties groupMappings)
074                    throws Exception {
075    
076                    Binding groupBinding = PortalLDAPUtil.getGroup(
077                            ldapServerId, userGroup.getCompanyId(), userGroup.getName());
078    
079                    if (groupBinding != null) {
080                            return PortalLDAPUtil.getNameInNamespace(
081                                    ldapServerId, userGroup.getCompanyId(), groupBinding);
082                    }
083    
084                    StringBundler sb = new StringBundler(5);
085    
086                    sb.append(
087                            GetterUtil.getString(
088                                    groupMappings.getProperty(_groupDNFieldName), _DEFAULT_DN));
089                    sb.append(StringPool.EQUAL);
090                    sb.append(userGroup.getName());
091                    sb.append(StringPool.COMMA);
092                    sb.append(
093                            PortalLDAPUtil.getGroupsDN(ldapServerId, userGroup.getCompanyId()));
094    
095                    return sb.toString();
096            }
097    
098            @Override
099            public Modifications getLDAPContactModifications(
100                            Contact contact, Map<String, Serializable> contactExpandoAttributes,
101                            Properties contactMappings, Properties contactExpandoMappings)
102                    throws Exception {
103    
104                    if (contactMappings.isEmpty() && contactExpandoMappings.isEmpty()) {
105                            return null;
106                    }
107    
108                    Modifications modifications = getModifications(
109                            contact, contactMappings, _reservedContactFieldNames);
110    
111                    populateCustomAttributeModifications(
112                            contact, contact.getExpandoBridge(), contactExpandoAttributes,
113                            contactExpandoMappings, modifications);
114    
115                    return modifications;
116            }
117    
118            @Override
119            public Attributes getLDAPGroupAttributes(
120                            long ldapServerId, UserGroup userGroup, User user,
121                            Properties groupMappings, Properties userMappings)
122                    throws Exception {
123    
124                    Attributes attributes = new BasicAttributes(true);
125    
126                    Attribute objectClass = new BasicAttribute(_OBJECT_CLASS);
127    
128                    String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
129    
130                    String[] defaultObjectClasses = PrefsPropsUtil.getStringArray(
131                            userGroup.getCompanyId(),
132                            PropsKeys.LDAP_GROUP_DEFAULT_OBJECT_CLASSES + postfix,
133                            StringPool.COMMA);
134    
135                    for (int i = 0; i < defaultObjectClasses.length; i++) {
136                            objectClass.add(defaultObjectClasses[i]);
137                    }
138    
139                    attributes.put(objectClass);
140    
141                    addAttributeMapping(
142                            groupMappings.getProperty(GroupConverterKeys.GROUP_NAME),
143                            userGroup.getName(), attributes);
144                    addAttributeMapping(
145                            groupMappings.getProperty(GroupConverterKeys.DESCRIPTION),
146                            userGroup.getDescription(), attributes);
147                    addAttributeMapping(
148                            groupMappings.getProperty(GroupConverterKeys.USER),
149                            getUserDNName(ldapServerId, user, userMappings), attributes);
150    
151                    return attributes;
152            }
153    
154            /**
155             * @deprecated
156             */
157            @Override
158            public Modifications getLDAPGroupModifications(
159                            long ldapServerId, UserGroup userGroup, User user,
160                            Properties groupMappings, Properties userMappings)
161                    throws Exception {
162    
163                    return getLDAPGroupModifications(
164                            ldapServerId, userGroup, user, groupMappings, userMappings,
165                            LDAPOperation.ADD);
166            }
167    
168            @Override
169            public Modifications getLDAPGroupModifications(
170                            long ldapServerId, UserGroup userGroup, User user,
171                            Properties groupMappings, Properties userMappings,
172                            LDAPOperation ldapOperation)
173                    throws Exception {
174    
175                    Modifications modifications = Modifications.getInstance();
176    
177                    String groupDN = getGroupDNName(ldapServerId, userGroup, groupMappings);
178                    String userDN = getUserDNName(ldapServerId, user, userMappings);
179    
180                    if (PortalLDAPUtil.isGroupMember(
181                                    ldapServerId, user.getCompanyId(), groupDN, userDN)) {
182    
183                            if (ldapOperation == LDAPOperation.REMOVE) {
184                                    modifications.addItem(
185                                            DirContext.REMOVE_ATTRIBUTE,
186                                            groupMappings.getProperty(GroupConverterKeys.USER), userDN);
187                            }
188                    }
189                    else {
190                            if (ldapOperation == LDAPOperation.ADD) {
191                                    modifications.addItem(
192                                            DirContext.ADD_ATTRIBUTE,
193                                            groupMappings.getProperty(GroupConverterKeys.USER), userDN);
194                            }
195                    }
196    
197                    return modifications;
198            }
199    
200            @Override
201            public Attributes getLDAPUserAttributes(
202                            long ldapServerId, User user, Properties userMappings)
203                    throws SystemException {
204    
205                    Attributes attributes = new BasicAttributes(true);
206    
207                    Attribute objectClass = new BasicAttribute(_OBJECT_CLASS);
208    
209                    String postfix = LDAPSettingsUtil.getPropertyPostfix(ldapServerId);
210    
211                    String[] defaultObjectClasses = PrefsPropsUtil.getStringArray(
212                            user.getCompanyId(),
213                            PropsKeys.LDAP_USER_DEFAULT_OBJECT_CLASSES + postfix,
214                            StringPool.COMMA);
215    
216                    for (int i = 0; i < defaultObjectClasses.length; i++) {
217                            objectClass.add(defaultObjectClasses[i]);
218                    }
219    
220                    attributes.put(objectClass);
221    
222                    addAttributeMapping(
223                            userMappings.getProperty(UserConverterKeys.UUID), user.getUuid(),
224                            attributes);
225                    addAttributeMapping(
226                            userMappings.getProperty(UserConverterKeys.SCREEN_NAME),
227                            user.getScreenName(), attributes);
228                    addAttributeMapping(
229                            userMappings.getProperty(UserConverterKeys.PASSWORD),
230                            getEncryptedPasswordForLDAP(user), attributes);
231                    addAttributeMapping(
232                            userMappings.getProperty(UserConverterKeys.EMAIL_ADDRESS),
233                            user.getEmailAddress(), attributes);
234                    addAttributeMapping(
235                            userMappings.getProperty(UserConverterKeys.FULL_NAME),
236                            user.getFullName(), attributes);
237                    addAttributeMapping(
238                            userMappings.getProperty(UserConverterKeys.FIRST_NAME),
239                            user.getFirstName(), attributes);
240                    addAttributeMapping(
241                            userMappings.getProperty(UserConverterKeys.MIDDLE_NAME),
242                            user.getMiddleName(), attributes);
243                    addAttributeMapping(
244                            userMappings.getProperty(UserConverterKeys.LAST_NAME),
245                            user.getLastName(), attributes);
246                    addAttributeMapping(
247                            userMappings.getProperty(UserConverterKeys.JOB_TITLE),
248                            user.getJobTitle(), attributes);
249                    addAttributeMapping(
250                            userMappings.getProperty(UserConverterKeys.PORTRAIT),
251                            getUserPortrait(user), attributes);
252                    addAttributeMapping(
253                            userMappings.getProperty(UserConverterKeys.STATUS),
254                            String.valueOf(user.getStatus()), attributes);
255    
256                    return attributes;
257            }
258    
259            @Override
260            public Modifications getLDAPUserGroupModifications(
261                            long ldapServerId, List<UserGroup> userGroups, User user,
262                            Properties userMappings)
263                    throws Exception {
264    
265                    Modifications modifications = Modifications.getInstance();
266    
267                    String groupMappingAttributeName = userMappings.getProperty(
268                            UserConverterKeys.GROUP);
269    
270                    if (Validator.isNull(groupMappingAttributeName)) {
271                            return modifications;
272                    }
273    
274                    Properties groupMappings = LDAPSettingsUtil.getGroupMappings(
275                            ldapServerId, user.getCompanyId());
276    
277                    String userDN = getUserDNName(ldapServerId, user, userMappings);
278    
279                    for (UserGroup userGroup : userGroups) {
280                            String groupDN = getGroupDNName(
281                                    ldapServerId, userGroup, groupMappings);
282    
283                            if (PortalLDAPUtil.isUserGroupMember(
284                                            ldapServerId, user.getCompanyId(), groupDN, userDN)) {
285    
286                                    continue;
287                            }
288    
289                            modifications.addItem(
290                                    DirContext.ADD_ATTRIBUTE, groupMappingAttributeName, groupDN);
291                    }
292    
293                    return modifications;
294            }
295    
296            @Override
297            public Modifications getLDAPUserModifications(
298                            User user, Map<String, Serializable> userExpandoAttributes,
299                            Properties userMappings, Properties userExpandoMappings)
300                    throws Exception {
301    
302                    Modifications modifications = getModifications(
303                            user, userMappings, _reservedUserFieldNames);
304    
305                    if (user.isPasswordModified() &&
306                            Validator.isNotNull(user.getPasswordUnencrypted())) {
307    
308                            String newPassword = getEncryptedPasswordForLDAP(user);
309    
310                            String passwordKey = userMappings.getProperty(
311                                    UserConverterKeys.PASSWORD);
312    
313                            if (passwordKey.equals("unicodePwd")) {
314                                    String newQuotedPassword = StringPool.QUOTE.concat(
315                                            newPassword).concat(StringPool.QUOTE);
316    
317                                    byte[] newUnicodePassword = newQuotedPassword.getBytes(
318                                            "UTF-16LE");
319    
320                                    addModificationItem(
321                                            new BasicAttribute(passwordKey, newUnicodePassword),
322                                            modifications);
323                            }
324                            else {
325                                    addModificationItem(passwordKey, newPassword, modifications);
326                            }
327                    }
328    
329                    String portraitKey = userMappings.getProperty(
330                            UserConverterKeys.PORTRAIT);
331    
332                    if (Validator.isNotNull(portraitKey)) {
333                            addModificationItem(
334                                    new BasicAttribute(portraitKey, getUserPortrait(user)),
335                                    modifications);
336                    }
337    
338                    populateCustomAttributeModifications(
339                            user, user.getExpandoBridge(), userExpandoAttributes,
340                            userExpandoMappings, modifications);
341    
342                    return modifications;
343            }
344    
345            @Override
346            public String getUserDNName(
347                            long ldapServerId, User user, Properties userMappings)
348                    throws Exception {
349    
350                    Binding userBinding = PortalLDAPUtil.getUser(
351                            ldapServerId, user.getCompanyId(), user.getScreenName(),
352                            user.getEmailAddress());
353    
354                    if (userBinding != null) {
355                            return PortalLDAPUtil.getNameInNamespace(
356                                    ldapServerId, user.getCompanyId(), userBinding);
357                    }
358    
359                    StringBundler sb = new StringBundler(5);
360    
361                    sb.append(
362                            GetterUtil.getString(
363                                    userMappings.getProperty(_userDNFieldName), _DEFAULT_DN));
364                    sb.append(StringPool.EQUAL);
365                    sb.append(PropertyUtils.getProperty(user, _userDNFieldName));
366                    sb.append(StringPool.COMMA);
367                    sb.append(PortalLDAPUtil.getUsersDN(ldapServerId, user.getCompanyId()));
368    
369                    return sb.toString();
370            }
371    
372            public void setContactReservedFieldNames(
373                    List<String> reservedContactFieldNames) {
374    
375                    for (String reservedContactFieldName : reservedContactFieldNames) {
376                            _reservedContactFieldNames.put(
377                                    reservedContactFieldName, reservedContactFieldName);
378                    }
379            }
380    
381            public void setUserDNFieldName(String userDNFieldName) {
382                    _userDNFieldName = userDNFieldName;
383            }
384    
385            public void setUserReservedFieldNames(List<String> reservedUserFieldNames) {
386                    for (String reservedUserFieldName : reservedUserFieldNames) {
387                            _reservedUserFieldNames.put(
388                                    reservedUserFieldName, reservedUserFieldName);
389                    }
390            }
391    
392            protected void addAttributeMapping(
393                    String attributeName, Object attributeValue, Attributes attributes) {
394    
395                    if (Validator.isNotNull(attributeName) && (attributeValue != null)) {
396                            attributes.put(attributeName, attributeValue);
397                    }
398            }
399    
400            protected void addAttributeMapping(
401                    String attributeName, String attributeValue, Attributes attributes) {
402    
403                    if (Validator.isNotNull(attributeName) &&
404                            Validator.isNotNull(attributeValue)) {
405    
406                            attributes.put(attributeName, attributeValue);
407                    }
408            }
409    
410            protected void addModificationItem(
411                    BasicAttribute basicAttribute, Modifications modifications) {
412    
413                    if (Validator.isNotNull(basicAttribute)) {
414                            modifications.addItem(basicAttribute);
415                    }
416            }
417    
418            protected void addModificationItem(
419                    String attributeName, String attributeValue,
420                    Modifications modifications) {
421    
422                    if (Validator.isNotNull(attributeName) &&
423                            Validator.isNotNull(attributeValue)) {
424    
425                            modifications.addItem(attributeName, attributeValue);
426                    }
427            }
428    
429            protected String getEncryptedPasswordForLDAP(User user)
430                    throws SystemException {
431    
432                    String password = user.getPasswordUnencrypted();
433    
434                    if (Validator.isNull(password)) {
435                            return password;
436                    }
437    
438                    String algorithm = PrefsPropsUtil.getString(
439                            user.getCompanyId(),
440                            PropsKeys.LDAP_AUTH_PASSWORD_ENCRYPTION_ALGORITHM);
441    
442                    if (Validator.isNull(algorithm)) {
443                            return password;
444                    }
445    
446                    try {
447                            StringBundler sb = new StringBundler(4);
448    
449                            sb.append(StringPool.OPEN_CURLY_BRACE);
450                            sb.append(algorithm);
451                            sb.append(StringPool.CLOSE_CURLY_BRACE);
452                            sb.append(PwdEncryptor.encrypt(algorithm, password, null));
453    
454                            return sb.toString();
455                    }
456                    catch (PwdEncryptorException pee) {
457                            throw new SystemException(pee);
458                    }
459            }
460    
461            protected Modifications getModifications(
462                    Object object, Properties objectMappings,
463                    Map<String, String> reservedFieldNames) {
464    
465                    Modifications modifications = Modifications.getInstance();
466    
467                    for (Map.Entry<Object, Object> entry : objectMappings.entrySet()) {
468                            String fieldName = (String)entry.getKey();
469    
470                            if (reservedFieldNames.containsKey(fieldName)) {
471                                    continue;
472                            }
473    
474                            String ldapAttributeName = (String)entry.getValue();
475    
476                            try {
477                                    Object attributeValue = PropertyUtils.getProperty(
478                                            object, fieldName);
479    
480                                    if (attributeValue != null) {
481                                            addModificationItem(
482                                                    ldapAttributeName, attributeValue.toString(),
483                                                    modifications);
484                                    }
485                            }
486                            catch (Exception e) {
487                                    if (_log.isWarnEnabled()) {
488                                            _log.warn(
489                                                    "Unable to map field " + fieldName + " to class " +
490                                                            object.getClass(),
491                                                    e);
492                                    }
493                            }
494                    }
495    
496                    return modifications;
497            }
498    
499            protected byte[] getUserPortrait(User user) {
500                    byte[] bytes = null;
501    
502                    if (user.getPortraitId() == 0) {
503                            return bytes;
504                    }
505    
506                    Image image = null;
507    
508                    try {
509                            image = ImageLocalServiceUtil.getImage(user.getPortraitId());
510    
511                            if (image != null) {
512                                    bytes = image.getTextObj();
513                            }
514                    }
515                    catch (Exception e) {
516                            if (_log.isWarnEnabled()) {
517                                    _log.warn(
518                                            "Unable to get the portrait for user " + user.getUserId(),
519                                            e);
520                            }
521                    }
522    
523                    return bytes;
524            }
525    
526            protected void populateCustomAttributeModifications(
527                    Object object, ExpandoBridge expandoBridge,
528                    Map<String, Serializable> expandoAttributes, Properties expandoMappings,
529                    Modifications modifications) {
530    
531                    if ((expandoAttributes == null) || expandoAttributes.isEmpty()) {
532                            return;
533                    }
534    
535                    for (Map.Entry<Object, Object> entry : expandoMappings.entrySet()) {
536                            String fieldName = (String)entry.getKey();
537                            String ldapAttributeName = (String)entry.getValue();
538    
539                            Serializable fieldValue = expandoAttributes.get(fieldName);
540    
541                            if (fieldValue == null) {
542                                    continue;
543                            }
544    
545                            try {
546                                    int type = expandoBridge.getAttributeType(fieldName);
547    
548                                    String value = ExpandoConverterUtil.getStringFromAttribute(
549                                            type, fieldValue);
550    
551                                    addModificationItem(ldapAttributeName, value, modifications);
552                            }
553                            catch (Exception e) {
554                                    if (_log.isWarnEnabled()) {
555                                            _log.warn(
556                                                    "Unable to map field " + fieldName + " to class " +
557                                                            object.getClass(),
558                                                    e);
559                                    }
560                            }
561                    }
562            }
563    
564            private static final String _DEFAULT_DN = "cn";
565    
566            private static final String _OBJECT_CLASS = "objectclass";
567    
568            private static Log _log = LogFactoryUtil.getLog(
569                    DefaultPortalToLDAPConverter.class);
570    
571            private String _groupDNFieldName = GroupConverterKeys.GROUP_NAME;
572            private Map<String, String> _reservedContactFieldNames =
573                    new HashMap<String, String>();
574            private Map<String, String> _reservedUserFieldNames =
575                    new HashMap<String, String>();
576            private String _userDNFieldName = UserConverterKeys.SCREEN_NAME;
577    
578    }