View Javadoc

1   /**
2    * 2007, Digitalis Informatica. All rights reserved. Distribuicao e Gestao de Informatica, Lda. Estrada de Paco de Arcos
3    * num.9 - Piso -1 2780-666 Paco de Arcos Telefone: (351) 21 4408990 Fax: (351) 21 4408999 http://www.digitalis.pt
4    */
5   package pt.digitalis.utils.ldap.impl.openldap;
6   
7   import java.util.ArrayList;
8   import java.util.HashMap;
9   import java.util.List;
10  import java.util.Map;
11  import java.util.Map.Entry;
12  
13  import javax.naming.NamingException;
14  import javax.naming.directory.Attributes;
15  import javax.naming.directory.BasicAttribute;
16  import javax.naming.directory.BasicAttributes;
17  import javax.naming.directory.ModificationItem;
18  import javax.naming.ldap.LdapContext;
19  
20  import pt.digitalis.utils.ldap.LDAPGroup;
21  import pt.digitalis.utils.ldap.LDAPUser;
22  import pt.digitalis.utils.ldap.exception.LDAPOperationException;
23  import pt.digitalis.utils.ldap.exception.LDAPOperationReadOnlyException;
24  import pt.digitalis.utils.ldap.impl.AbstractLDAPUtils;
25  
26  /**
27   * LDAP Utils implementation for OpenLDAP).
28   * 
29   * @author Rodrigo Gonçalves <a href="mailto:rgoncalves@digitalis.pt">rgoncalves@digitalis.pt</a><br/>
30   * @created Jul 02, 2008
31   */
32  public class LDAPUtilsOpenLDAPImpl extends AbstractLDAPUtils {
33  
34      /** the unchangeable attributes */
35      final static List<String> unchangeableAttributes = new ArrayList<String>();
36  
37      /**
38       * For the AD implementation an entity's DN is composed of the common name and the parent group's DN, excluding the
39       * group´s CN.<br>
40       * <br>
41       * Example:<br>
42       * <br>
43       * <br>
44       * - User login name: 'user1' <br>
45       * <br>
46       * - Main group's DN: cn=group1,ou=group1,dc=dc1,dc=dc2 <br>
47       * <br>
48       * - User's DN: cn=user1,ou=group1,dc=dc1,dc=dc2 <br>
49       * 
50       * @see pt.digitalis.utils.ldap.impl.AbstractLDAPUtils#calculateDistinguishedName(java.lang.String,
51       *      java.lang.String)
52       */
53      @Override
54      protected String calculateDistinguishedName(String commonName, String mainGroupDN) throws LDAPOperationException
55      {
56  
57          if (commonName == null)
58              throw new LDAPOperationException(
59                      "The supplied CN was null! Cannot calculate the entity's DN without a valid CN...");
60          if (mainGroupDN == null)
61          {
62              throw new LDAPOperationException(
63                      "The supplied parent group name was null!! Cannot calculate the entity's DN without a valid parent group name...");
64          }
65  
66          // Create user CN part
67          StringBuffer newDistinguishedName = new StringBuffer(AbstractLDAPUtils.CN_TAG + commonName);
68  
69          // Get group DN
70          StringBuffer groupDistinguishedName = new StringBuffer(mainGroupDN);
71  
72          // Discard group's CN
73          groupDistinguishedName.trimToSize();
74          groupDistinguishedName.replace(0, groupDistinguishedName.capacity(),
75                  groupDistinguishedName.substring(groupDistinguishedName.indexOf(",")));
76  
77          // Append the group's DN to user's DN
78          newDistinguishedName.append(groupDistinguishedName);
79  
80          // Return result
81          return newDistinguishedName.toString();
82      }
83  
84      /**
85       * @see pt.digitalis.utils.ldap.ILDAPUtils#changePassword(java.lang.String, java.lang.String)
86       */
87      @Override
88      public void changePassword(String loginName, String newPassword) throws LDAPOperationException
89      {
90          if (this.isReadOnly())
91              throw new LDAPOperationReadOnlyException();
92  
93          LDAPUser user = findUserByLogin(loginName);
94  
95          if (user != null)
96          {
97  
98              ArrayList<ModificationItem> itens = new ArrayList<ModificationItem>();
99              itens.add(new ModificationItem(LdapContext.REPLACE_ATTRIBUTE, new BasicAttribute(
100                     getPasswordAttributeName(), newPassword)));
101 
102             if (itens.size() > 0)
103             {
104                 ModificationItem mods[] = new ModificationItem[itens.size()];
105                 for (int i = 0; i < itens.size(); i++)
106                 {
107                     mods[i] = itens.get(i);
108                 }
109 
110                 LdapContext ctx = getLDAPContext();
111                 try
112                 {
113                     ctx.modifyAttributes(user.getDistinguishedName(), mods);
114                 }
115                 catch (NamingException namingException)
116                 {
117                     throw new LDAPOperationException("Could not change password for user with DN="
118                             + user.getDistinguishedName() + "!", namingException);
119                 }
120                 finally
121                 {
122                     try
123                     {
124                         ctx.close();
125                     }
126                     catch (NamingException e)
127                     {
128                         throw new LDAPOperationException("Error closing NamingEnumeration!", e);
129                     }
130                 }
131             }
132         }
133         else
134             throw new LDAPOperationException("User with login=" + loginName
135                     + " was not found on the LDAP server! Can't change the password on an nonexistent user...");
136     }
137 
138     /**
139      * @see pt.digitalis.utils.ldap.impl.AbstractLDAPUtils#getAttributesForGroupAddition(pt.digitalis.utils.ldap.LDAPGroup)
140      */
141     @Override
142     protected Attributes getAttributesForGroupAddition(LDAPGroup newGroup) throws LDAPOperationException
143     {
144         Attributes attrs = new BasicAttributes(true);
145 
146         attrs.put(getObjectClassName(), getGroupClassName());
147 
148         // Populate 'member' attribute -> must exist but will be empty upon creation
149         attrs.put(getGroupAttributeName(), "");
150 
151         if (newGroup.getDescription() != null)
152             attrs.put(getDescriptionAttributeName(), newGroup.getDescription());
153 
154         if (newGroup.getParentGroupDN() != null)
155         {
156             attrs.put(getGroupParentGroupAttributeName(), newGroup.getParentGroupDN());
157         }
158 
159         return attrs;
160     }
161 
162     /**
163      * @see pt.digitalis.utils.ldap.impl.AbstractLDAPUtils#getAttributesForUserAddition(pt.digitalis.utils.ldap.LDAPUser)
164      */
165     @Override
166     protected Attributes getAttributesForUserAddition(LDAPUser newUser) throws LDAPOperationException
167     {
168 
169         Attributes attributes = super.getAttributesForUserAddition(newUser);
170         attributes.put(getSurnameAttributeName(), newUser.getGivenName());
171         attributes.remove(getNameAttributeName());
172 
173         return attributes;
174     }
175 
176     /**
177      * @see pt.digitalis.utils.ldap.impl.AbstractLDAPUtils#getGroupClassName()
178      */
179     @Override
180     protected String getGroupClassName()
181     {
182         return "groupOfNames";
183     }
184 
185     /**
186      * The LDAP attribute 'secretary' was chosen to store the group's parent group information since there is no
187      * attribute on the so-called LDAP standard for the this information, and such information is needed for this
188      * implementation.
189      * 
190      * @see pt.digitalis.utils.ldap.impl.AbstractLDAPUtils#getGroupParentGroupAttributeName()
191      */
192     @Override
193     public String getGroupParentGroupAttributeName()
194     {
195         return "seeAlso";
196     }
197 
198     /**
199      * @see pt.digitalis.utils.ldap.impl.AbstractLDAPUtils#getNameAttributeName()
200      */
201     @Override
202     public String getNameAttributeName()
203     {
204         return "cn";
205     }
206 
207     /**
208      * @see pt.digitalis.utils.ldap.ILDAPUtils#getUnchangeableLDAPAttributes()
209      */
210     public List<String> getUnchangeableLDAPAttributes()
211     {
212         return unchangeableAttributes;
213     }
214 
215     /**
216      * @see pt.digitalis.utils.ldap.impl.AbstractLDAPUtils#getUserClassName()
217      */
218     @Override
219     protected String getUserClassName()
220     {
221         return "inetOrgPerson";
222     }
223 
224     /**
225      * @see pt.digitalis.utils.ldap.impl.AbstractLDAPUtils#getUserLoginAttributeName()
226      */
227     @Override
228     public String getUserLoginAttributeName()
229     {
230         return "cn";
231     }
232 
233     /**
234      * @see pt.digitalis.utils.ldap.ILDAPUtils#updateUser(pt.digitalis.utils.ldap.LDAPUser, java.lang.String)
235      */
236     @Override
237     public void updateUser(LDAPUser userToUpdate, String userLogin) throws LDAPOperationException
238     {
239 
240         if (this.isReadOnly())
241             throw new LDAPOperationReadOnlyException();
242 
243         // Retrieve old user data to get the old data
244         LDAPUser existingUser = findUserByLogin(userLogin, false);
245 
246         if (existingUser != null)
247         {
248             ArrayList<ModificationItem> items = new ArrayList<ModificationItem>();
249 
250             // 'displayName'
251             if (userToUpdate.getDisplayName() != null
252                     && !userToUpdate.getDisplayName().equalsIgnoreCase(existingUser.getDisplayName()))
253             {
254                 items.add(new ModificationItem(LdapContext.REPLACE_ATTRIBUTE, new BasicAttribute(
255                         getDisplayNameAttributeName(), userToUpdate.getDisplayName())));
256             }
257 
258             // 'e-mail'
259             if (userToUpdate.getEmail() != null && !userToUpdate.getEmail().equals(existingUser.getEmail()))
260                 items.add(new ModificationItem(LdapContext.REPLACE_ATTRIBUTE, new BasicAttribute(
261                         getMailAttributeName(), userToUpdate.getEmail())));
262             // 'givenName'
263             if (userToUpdate.getGivenName() != null && !userToUpdate.getGivenName().equals(existingUser.getGivenName()))
264                 items.add(new ModificationItem(LdapContext.REPLACE_ATTRIBUTE, new BasicAttribute(
265                         getGivenNameAttributeName(), userToUpdate.getGivenName())));
266 
267             /*
268              * Password change method is implementation dependent so it's not done through ModificationItem. Password
269              * does not come from LDAP server, so it's ALWAYS changed!!! Done before login change to assure that the
270              * former login name still works.
271              */
272             if (userToUpdate.getPassword() != null)
273                 changePassword(userLogin, userToUpdate.getPassword());
274 
275             // '"mainGroup"'
276             if (userToUpdate.getParentGroupDN() != null
277                     && !userToUpdate.getParentGroupDN().equals(existingUser.getParentGroupDN()))
278                 items.add(new ModificationItem(LdapContext.REPLACE_ATTRIBUTE, new BasicAttribute(
279                         getUserParentGroupAttributeName(), userToUpdate.getParentGroupDN())));
280 
281             // Parameters
282             Map<String, String> bulkParameters = new HashMap<String, String>();
283             for (String parameterName: userToUpdate.getParameters().keySet())
284             {
285                 if (getConfigurations().getAttributesMapping().containsKey(parameterName))
286                 {
287                     items.add(new ModificationItem(LdapContext.REPLACE_ATTRIBUTE, new BasicAttribute(
288                             getConfigurations().getAttributesMapping().get(parameterName), userToUpdate
289                                     .getParameter(parameterName))));
290                 }
291                 else
292                 {
293                     if (existingUser.getParameter(parameterName) != null)
294                     {
295                         if (!getUnchangeableLDAPAttributes().contains(parameterName.toUpperCase())
296                                 && !userToUpdate.getParameter(parameterName).equalsIgnoreCase(
297                                         existingUser.getParameter(parameterName)))
298                             items.add(new ModificationItem(LdapContext.REPLACE_ATTRIBUTE, new BasicAttribute(
299                                     parameterName, userToUpdate.getParameter(parameterName))));
300                     }
301                     else
302                     {
303                         bulkParameters.put(parameterName, userToUpdate.getParameter(parameterName));
304                     }
305                 }
306             }
307 
308             for (String parameterName: userToUpdate.getParametersToRemove())
309             {
310                 if (getConfigurations().getAttributesMapping().containsKey(parameterName))
311                 {
312                     items.add(new ModificationItem(LdapContext.REMOVE_ATTRIBUTE, new BasicAttribute(getConfigurations()
313                             .getAttributesMapping().get(parameterName))));
314                 }
315                 else
316                 {
317                     bulkParameters.remove(parameterName);
318                 }
319             }
320 
321             // The value of the Bulk parameters to commit
322             StringBuilder bulkParameterAttributeValue = new StringBuilder();
323             for (Entry<String, String> entry: bulkParameters.entrySet())
324             {
325                 bulkParameterAttributeValue.append(entry.getKey() + "=" + entry.getValue() + ";");
326             }
327 
328             if (bulkParameterAttributeValue.length() == 0)
329             {
330                 /* LDAP doesn't like empty strings on REPLACE_ATTRIBUTE */
331                 bulkParameterAttributeValue.append(" ");
332             }
333 
334             items.add(new ModificationItem(LdapContext.REPLACE_ATTRIBUTE, new BasicAttribute(getConfigurations()
335                     .getBulkParametersAttributeName(), bulkParameterAttributeValue.toString())));
336 
337             // Persist changes
338             if (items.size() > 0)
339             {
340                 ModificationItem mods[] = new ModificationItem[items.size()];
341                 for (int i = 0; i < items.size(); i++)
342                 {
343                     mods[i] = items.get(i);
344                 }
345 
346                 modifyAttributes(existingUser.getDistinguishedName(), mods, false);
347             }
348 
349             /* Group && CN/login modification must be made after all other attributes since it can cause DN changes. */
350 
351             if (ldapConfigurations.getAllowDistinguishedNameModifications()
352                     && userToUpdate.getParentGroupDN() != null
353                     && !userToUpdate.getParentGroupDN().equals(existingUser.getParentGroupDN())
354                     || ((userToUpdate.getCommonName() != null && !userToUpdate.getCommonName().equals(
355                             existingUser.getCommonName())) || ((userToUpdate.getLoginName() != null && !userToUpdate
356                             .getLoginName().equals(existingUser.getLoginName())))))
357             {
358 
359                 // Get login name from user to change
360                 String loginName = userToUpdate.getLoginName();
361 
362                 // If only the group changes, get login from existing user data
363                 if (loginName == null || NON_AVAILABLE.equals(loginName))
364                     loginName = existingUser.getLoginName();
365 
366                 // Get parent group DN from user to change
367                 String parentGroupDN = userToUpdate.getParentGroupDN();
368 
369                 // If the group is not defined, get it from existing user
370                 if (parentGroupDN == null || NON_AVAILABLE.equals(parentGroupDN))
371                     parentGroupDN = existingUser.getParentGroupDN();
372 
373                 String newDN = calculateDistinguishedName(loginName, parentGroupDN);
374 
375                 try
376                 {
377                     if (!existingUser.getDistinguishedName().equals(newDN))
378                     {
379                         LdapContext ctx = getLDAPContext();
380                         try
381                         {
382                             ctx.rename(existingUser.getDistinguishedName(), newDN);
383                         }
384                         finally
385                         {
386                             ctx.close();
387                         }
388                     }
389                 }
390                 catch (LDAPOperationException ldapOperationException)
391                 {
392                     throw ldapOperationException;
393                 }
394                 catch (NamingException namingException)
395                 {
396                     throw new LDAPOperationException("Could not update user's main group...", namingException);
397                 }
398             }
399         }
400     }
401 }