001    /**
002     * Copyright (c) 2000-present 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.PasswordEncryptorUtil;
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            @Override
155            public Modifications getLDAPGroupModifications(
156                            long ldapServerId, UserGroup userGroup, User user,
157                            Properties groupMappings, Properties userMappings,
158                            LDAPOperation ldapOperation)
159                    throws Exception {
160    
161                    Modifications modifications = Modifications.getInstance();
162    
163                    String groupDN = getGroupDNName(ldapServerId, userGroup, groupMappings);
164                    String userDN = getUserDNName(ldapServerId, user, userMappings);
165    
166                    if (PortalLDAPUtil.isGroupMember(
167                                    ldapServerId, user.getCompanyId(), groupDN, userDN)) {
168    
169                            if (ldapOperation == LDAPOperation.REMOVE) {
170                                    modifications.addItem(
171                                            DirContext.REMOVE_ATTRIBUTE,
172                                            groupMappings.getProperty(GroupConverterKeys.USER), userDN);
173                            }
174                    }
175                    else {
176                            if (ldapOperation == LDAPOperation.ADD) {
177                                    modifications.addItem(
178                                            DirContext.ADD_ATTRIBUTE,
179                                            groupMappings.getProperty(GroupConverterKeys.USER), userDN);
180                            }
181                    }
182    
183                    return modifications;
184            }
185    
186            @Override
187            public Attributes getLDAPUserAttributes(
188                    long ldapServerId, User user, Properties userMappings) {
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), 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(user);
294    
295                            String passwordKey = userMappings.getProperty(
296                                    UserConverterKeys.PASSWORD);
297    
298                            if (passwordKey.equals("unicodePwd")) {
299                                    String newQuotedPassword = StringPool.QUOTE.concat(
300                                            newPassword).concat(StringPool.QUOTE);
301    
302                                    byte[] newUnicodePassword = newQuotedPassword.getBytes(
303                                            "UTF-16LE");
304    
305                                    addModificationItem(
306                                            new BasicAttribute(passwordKey, newUnicodePassword),
307                                            modifications);
308                            }
309                            else {
310                                    addModificationItem(passwordKey, newPassword, modifications);
311                            }
312                    }
313    
314                    String portraitKey = userMappings.getProperty(
315                            UserConverterKeys.PORTRAIT);
316    
317                    if (Validator.isNotNull(portraitKey)) {
318                            addModificationItem(
319                                    new BasicAttribute(portraitKey, getUserPortrait(user)),
320                                    modifications);
321                    }
322    
323                    populateCustomAttributeModifications(
324                            user, user.getExpandoBridge(), userExpandoAttributes,
325                            userExpandoMappings, modifications);
326    
327                    return modifications;
328            }
329    
330            @Override
331            public String getUserDNName(
332                            long ldapServerId, User user, Properties userMappings)
333                    throws Exception {
334    
335                    Binding userBinding = PortalLDAPUtil.getUser(
336                            ldapServerId, user.getCompanyId(), user.getScreenName(),
337                            user.getEmailAddress());
338    
339                    if (userBinding != null) {
340                            return PortalLDAPUtil.getNameInNamespace(
341                                    ldapServerId, user.getCompanyId(), userBinding);
342                    }
343    
344                    StringBundler sb = new StringBundler(5);
345    
346                    sb.append(
347                            GetterUtil.getString(
348                                    userMappings.getProperty(_userDNFieldName), _DEFAULT_DN));
349                    sb.append(StringPool.EQUAL);
350                    sb.append(PropertyUtils.getProperty(user, _userDNFieldName));
351                    sb.append(StringPool.COMMA);
352                    sb.append(PortalLDAPUtil.getUsersDN(ldapServerId, user.getCompanyId()));
353    
354                    return sb.toString();
355            }
356    
357            public void setContactReservedFieldNames(
358                    List<String> reservedContactFieldNames) {
359    
360                    for (String reservedContactFieldName : reservedContactFieldNames) {
361                            _reservedContactFieldNames.put(
362                                    reservedContactFieldName, reservedContactFieldName);
363                    }
364            }
365    
366            public void setUserDNFieldName(String userDNFieldName) {
367                    _userDNFieldName = userDNFieldName;
368            }
369    
370            public void setUserReservedFieldNames(List<String> reservedUserFieldNames) {
371                    for (String reservedUserFieldName : reservedUserFieldNames) {
372                            _reservedUserFieldNames.put(
373                                    reservedUserFieldName, reservedUserFieldName);
374                    }
375            }
376    
377            protected void addAttributeMapping(
378                    String attributeName, Object attributeValue, Attributes attributes) {
379    
380                    if (Validator.isNotNull(attributeName) && (attributeValue != null)) {
381                            attributes.put(attributeName, attributeValue);
382                    }
383            }
384    
385            protected void addAttributeMapping(
386                    String attributeName, String attributeValue, Attributes attributes) {
387    
388                    if (Validator.isNotNull(attributeName) &&
389                            Validator.isNotNull(attributeValue)) {
390    
391                            attributes.put(attributeName, attributeValue);
392                    }
393            }
394    
395            protected void addModificationItem(
396                    BasicAttribute basicAttribute, Modifications modifications) {
397    
398                    if (basicAttribute != null) {
399                            modifications.addItem(basicAttribute);
400                    }
401            }
402    
403            protected void addModificationItem(
404                    String attributeName, String attributeValue,
405                    Modifications modifications) {
406    
407                    if (Validator.isNotNull(attributeName)) {
408                            modifications.addItem(attributeName, attributeValue);
409                    }
410            }
411    
412            protected String getEncryptedPasswordForLDAP(User user) {
413                    String password = user.getPasswordUnencrypted();
414    
415                    if (Validator.isNull(password)) {
416                            return password;
417                    }
418    
419                    String algorithm = PrefsPropsUtil.getString(
420                            user.getCompanyId(),
421                            PropsKeys.LDAP_AUTH_PASSWORD_ENCRYPTION_ALGORITHM);
422    
423                    if (Validator.isNull(algorithm)) {
424                            return password;
425                    }
426    
427                    try {
428                            StringBundler sb = new StringBundler(4);
429    
430                            if (!algorithm.equals(PasswordEncryptorUtil.TYPE_NONE)) {
431                                    sb.append(StringPool.OPEN_CURLY_BRACE);
432                                    sb.append(algorithm);
433                                    sb.append(StringPool.CLOSE_CURLY_BRACE);
434                            }
435    
436                            sb.append(PasswordEncryptorUtil.encrypt(algorithm, password, null));
437    
438                            return sb.toString();
439                    }
440                    catch (PwdEncryptorException pee) {
441                            throw new SystemException(pee);
442                    }
443            }
444    
445            protected Modifications getModifications(
446                    Object object, Properties objectMappings,
447                    Map<String, String> reservedFieldNames) {
448    
449                    Modifications modifications = Modifications.getInstance();
450    
451                    for (Map.Entry<Object, Object> entry : objectMappings.entrySet()) {
452                            String fieldName = (String)entry.getKey();
453    
454                            if (reservedFieldNames.containsKey(fieldName)) {
455                                    continue;
456                            }
457    
458                            String ldapAttributeName = (String)entry.getValue();
459    
460                            try {
461                                    Object attributeValue = PropertyUtils.getProperty(
462                                            object, fieldName);
463    
464                                    if (attributeValue != null) {
465                                            addModificationItem(
466                                                    ldapAttributeName, attributeValue.toString(),
467                                                    modifications);
468                                    }
469                            }
470                            catch (Exception e) {
471                                    if (_log.isWarnEnabled()) {
472                                            _log.warn(
473                                                    "Unable to map field " + fieldName + " to class " +
474                                                            object.getClass(),
475                                                    e);
476                                    }
477                            }
478                    }
479    
480                    return modifications;
481            }
482    
483            protected byte[] getUserPortrait(User user) {
484                    byte[] bytes = null;
485    
486                    if (user.getPortraitId() == 0) {
487                            return bytes;
488                    }
489    
490                    Image image = null;
491    
492                    try {
493                            image = ImageLocalServiceUtil.getImage(user.getPortraitId());
494    
495                            if (image != null) {
496                                    bytes = image.getTextObj();
497                            }
498                    }
499                    catch (Exception e) {
500                            if (_log.isWarnEnabled()) {
501                                    _log.warn(
502                                            "Unable to get the portrait for user " + user.getUserId(),
503                                            e);
504                            }
505                    }
506    
507                    return bytes;
508            }
509    
510            protected void populateCustomAttributeModifications(
511                    Object object, ExpandoBridge expandoBridge,
512                    Map<String, Serializable> expandoAttributes, Properties expandoMappings,
513                    Modifications modifications) {
514    
515                    if ((expandoAttributes == null) || expandoAttributes.isEmpty()) {
516                            return;
517                    }
518    
519                    for (Map.Entry<Object, Object> entry : expandoMappings.entrySet()) {
520                            String fieldName = (String)entry.getKey();
521                            String ldapAttributeName = (String)entry.getValue();
522    
523                            Serializable fieldValue = expandoAttributes.get(fieldName);
524    
525                            if (fieldValue == null) {
526                                    continue;
527                            }
528    
529                            try {
530                                    int type = expandoBridge.getAttributeType(fieldName);
531    
532                                    String value = ExpandoConverterUtil.getStringFromAttribute(
533                                            type, fieldValue);
534    
535                                    addModificationItem(ldapAttributeName, value, modifications);
536                            }
537                            catch (Exception e) {
538                                    if (_log.isWarnEnabled()) {
539                                            _log.warn(
540                                                    "Unable to map field " + fieldName + " to class " +
541                                                            object.getClass(),
542                                                    e);
543                                    }
544                            }
545                    }
546            }
547    
548            private static final String _DEFAULT_DN = "cn";
549    
550            private static final String _OBJECT_CLASS = "objectclass";
551    
552            private static Log _log = LogFactoryUtil.getLog(
553                    DefaultPortalToLDAPConverter.class);
554    
555            private String _groupDNFieldName = GroupConverterKeys.GROUP_NAME;
556            private Map<String, String> _reservedContactFieldNames =
557                    new HashMap<String, String>();
558            private Map<String, String> _reservedUserFieldNames =
559                    new HashMap<String, String>();
560            private String _userDNFieldName = UserConverterKeys.SCREEN_NAME;
561    
562    }