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