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