This project has retired. For details please refer to its Attic page.
DefaultLdapController xref
View Javadoc

1   package org.apache.archiva.redback.users.ldap.ctl;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Collection;
23  import java.util.HashMap;
24  import java.util.HashSet;
25  import java.util.LinkedHashSet;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import javax.annotation.PostConstruct;
32  import javax.inject.Inject;
33  import javax.inject.Named;
34  import javax.naming.NamingEnumeration;
35  import javax.naming.NamingException;
36  import javax.naming.directory.Attribute;
37  import javax.naming.directory.Attributes;
38  import javax.naming.directory.BasicAttribute;
39  import javax.naming.directory.BasicAttributes;
40  import javax.naming.directory.DirContext;
41  import javax.naming.directory.SearchControls;
42  import javax.naming.directory.SearchResult;
43  
44  import org.apache.archiva.redback.common.ldap.user.LdapUser;
45  import org.apache.archiva.redback.common.ldap.user.LdapUserMapper;
46  import org.apache.archiva.redback.common.ldap.user.UserMapper;
47  import org.apache.archiva.redback.configuration.UserConfiguration;
48  import org.apache.archiva.redback.configuration.UserConfigurationKeys;
49  import org.apache.archiva.redback.policy.PasswordEncoder;
50  import org.apache.archiva.redback.policy.encoders.SHA1PasswordEncoder;
51  import org.apache.archiva.redback.users.User;
52  import org.apache.archiva.redback.users.UserManager;
53  import org.apache.archiva.redback.common.ldap.MappingException;
54  import org.apache.archiva.redback.users.ldap.LdapUserQuery;
55  import org.apache.commons.lang.StringUtils;
56  import org.slf4j.Logger;
57  import org.slf4j.LoggerFactory;
58  import org.springframework.stereotype.Service;
59  
60  /**
61   * @author <a href="jesse@codehaus.org"> jesse
62   */
63  @Service
64  public class DefaultLdapController
65      implements LdapController
66  {
67  
68      private Logger log = LoggerFactory.getLogger( getClass() );
69  
70      @Inject
71      @Named(value = "userMapper#ldap")
72      private UserMapper mapper;
73  
74      @Inject
75      @Named( value = "userConfiguration#default" )
76      private UserConfiguration userConf;
77  
78      private boolean writableLdap = false;
79  
80      private PasswordEncoder passwordEncoder;
81  
82      private String baseDn;
83  
84      private String groupsDn;
85  
86      private String ldapGroupClass = "groupOfUniqueNames";
87  
88      @PostConstruct
89      public void initialize()
90      {
91          this.writableLdap = userConf.getBoolean( UserConfigurationKeys.LDAP_WRITABLE, this.writableLdap );
92          this.baseDn = userConf.getConcatenatedList( UserConfigurationKeys.LDAP_BASEDN, null );
93          this.passwordEncoder = new SHA1PasswordEncoder();
94          this.groupsDn = userConf.getConcatenatedList( UserConfigurationKeys.LDAP_GROUPS_BASEDN, this.groupsDn );
95          this.ldapGroupClass = userConf.getString( UserConfigurationKeys.LDAP_GROUPS_CLASS, this.ldapGroupClass );
96      }
97  
98      /**
99       * @see org.apache.archiva.redback.users.ldap.ctl.LdapController#removeUser(String, javax.naming.directory.DirContext)
100      */
101     public void removeUser( String principal, DirContext context )
102         throws LdapControllerException
103     {
104         // no op
105     }
106 
107     /**
108      * @see org.apache.archiva.redback.users.ldap.ctl.LdapController#updateUser(org.apache.archiva.redback.users.User, javax.naming.directory.DirContext)
109      */
110     public void updateUser( User user, DirContext context )
111         throws LdapControllerException, MappingException
112     {
113         // no op
114     }
115 
116     /**
117      * @see org.apache.archiva.redback.users.ldap.ctl.LdapController#userExists(String, javax.naming.directory.DirContext)
118      */
119     public boolean userExists( String key, DirContext context )
120         throws LdapControllerException
121     {
122         NamingEnumeration<SearchResult> results = null;
123         try
124         {
125             results = searchUsers( key, context );
126             return results.hasMoreElements();
127         }
128         catch ( NamingException e )
129         {
130             throw new LdapControllerException( "Error searching for the existence of user: " + key, e );
131         }
132         finally
133         {
134             if ( results != null )
135             {
136                 try
137                 {
138                     results.close();
139                 }
140                 catch ( NamingException e )
141                 {
142                     log.warn( "Error closing search results", e );
143                 }
144             }
145         }
146     }
147 
148     protected NamingEnumeration<SearchResult> searchUsers( String key, DirContext context )
149         throws NamingException
150     {
151         LdapUserQuery query = new LdapUserQuery();
152         query.setUsername( key );
153         return searchUsers( context, null, query );
154     }
155 
156     protected NamingEnumeration<SearchResult> searchUsers( DirContext context )
157         throws NamingException
158     {
159         return searchUsers( context, null, null );
160     }
161 
162     protected NamingEnumeration<SearchResult> searchUsers( DirContext context, String[] returnAttributes )
163         throws NamingException
164     {
165         return searchUsers( context, returnAttributes, null );
166     }
167 
168     protected NamingEnumeration<SearchResult> searchUsers( DirContext context, String[] returnAttributes,
169                                                            LdapUserQuery query )
170         throws NamingException
171     {
172         if ( query == null )
173         {
174             query = new LdapUserQuery();
175         }
176         SearchControls ctls = new SearchControls();
177 
178         ctls.setDerefLinkFlag( true );
179         ctls.setSearchScope( SearchControls.SUBTREE_SCOPE );
180         ctls.setReturningAttributes( mapper.getReturningAttributes() );
181         ctls.setCountLimit( ( (LdapUserMapper) mapper ).getMaxResultCount() );
182 
183         String finalFilter = new StringBuilder( "(&(objectClass=" + mapper.getUserObjectClass() + ")" ).append(
184             ( mapper.getUserFilter() != null ? mapper.getUserFilter() : "" ) ).append(
185             query.getLdapFilter( mapper ) + ")" ).toString();
186 
187         log.debug( "Searching for users with filter: '{}' from base dn: {}", finalFilter, mapper.getUserBaseDn() );
188 
189         return context.search( mapper.getUserBaseDn(), finalFilter, ctls );
190     }
191 
192     /**
193      * @see org.apache.archiva.redback.users.ldap.ctl.LdapController#getUsers(javax.naming.directory.DirContext)
194      */
195     public Collection<User> getUsers( DirContext context )
196         throws LdapControllerException, MappingException
197     {
198         NamingEnumeration<SearchResult> results = null;
199         try
200         {
201             results = searchUsers( context, null, null );
202             Set<User> users = new LinkedHashSet<User>();
203 
204             while ( results.hasMoreElements() )
205             {
206                 SearchResult result = results.nextElement();
207 
208                 users.add( mapper.getUser( result.getAttributes() ) );
209             }
210 
211             return users;
212         }
213         catch ( NamingException e )
214         {
215             String message = "Failed to retrieve ldap information for users.";
216 
217             throw new LdapControllerException( message, e );
218         }
219         finally
220         {
221             if ( results != null )
222             {
223                 try
224                 {
225                     results.close();
226                 }
227                 catch ( NamingException e )
228                 {
229                     log.warn( "failed to close search results", e );
230                 }
231             }
232         }
233     }
234 
235     /**
236      * @see org.apache.archiva.redback.users.ldap.ctl.LdapController#getUsersByQuery(org.apache.archiva.redback.users.ldap.LdapUserQuery, javax.naming.directory.DirContext)
237      */
238     public List<User> getUsersByQuery( LdapUserQuery query, DirContext context )
239         throws LdapControllerException, MappingException
240     {
241         NamingEnumeration<SearchResult> results = null;
242         try
243         {
244             results = searchUsers( context, null, query );
245             List<User> users = new LinkedList<User>();
246 
247             while ( results.hasMoreElements() )
248             {
249                 SearchResult result = results.nextElement();
250 
251                 users.add( mapper.getUser( result.getAttributes() ) );
252             }
253 
254             return users;
255         }
256         catch ( NamingException e )
257         {
258             String message = "Failed to retrieve ldap information for users.";
259 
260             throw new LdapControllerException( message, e );
261         }
262         finally
263         {
264             if ( results != null )
265             {
266                 try
267                 {
268                     results.close();
269                 }
270                 catch ( NamingException e )
271                 {
272                     log.warn( "failed to close search results", e );
273                 }
274             }
275         }
276     }
277 
278     /**
279      * @see org.apache.archiva.redback.users.ldap.ctl.LdapController#createUser(org.apache.archiva.redback.users.User, javax.naming.directory.DirContext, boolean)
280      */
281     public void createUser( User user, DirContext context, boolean encodePasswordIfChanged )
282         throws LdapControllerException, MappingException
283     {
284         if ( user == null )
285         {
286             return;
287         }
288         if ( user.getUsername().equals( UserManager.GUEST_USERNAME ) )
289         {
290             log.warn( "skip user '{}' creation" );
291             //We don't store guest
292             return;
293         }
294         boolean userExists = userExists( user.getUsername(), context );
295         if ( userExists )
296         {
297             log.debug( "user '{}' exists skip creation", user.getUsername() );
298             return;
299         }
300         if ( writableLdap )
301         {
302             try
303             {
304                 bindUserObject( context, user );
305                 log.info( "user {} created in ldap", user.getUsername() );
306             }
307             catch ( NamingException e )
308             {
309                 throw new LdapControllerException( e.getMessage(), e );
310             }
311         }
312     }
313 
314 
315     private void bindUserObject( DirContext context, User user )
316         throws NamingException
317     {
318         Attributes attributes = new BasicAttributes( true );
319         BasicAttribute objectClass = new BasicAttribute( "objectClass" );
320         objectClass.add( "top" );
321         objectClass.add( "inetOrgPerson" );
322         objectClass.add( "person" );
323         objectClass.add( "organizationalperson" );
324         attributes.put( objectClass );
325         attributes.put( "cn", user.getUsername() );
326         attributes.put( "sn", "foo" );
327         if ( StringUtils.isNotEmpty( user.getEmail() ) )
328         {
329             attributes.put( "mail", user.getEmail() );
330         }
331 
332         if ( userConf.getBoolean( UserConfigurationKeys.LDAP_BIND_AUTHENTICATOR_ALLOW_EMPTY_PASSWORDS, false )
333             && StringUtils.isNotEmpty( user.getPassword() ) )
334         {
335             attributes.put( "userPassword", passwordEncoder.encodePassword( user.getPassword() ) );
336         }
337         attributes.put( "givenName", "foo" );
338         context.createSubcontext( "cn=" + user.getUsername() + "," + this.getBaseDn(), attributes );
339     }
340 
341     /**
342      * @see org.apache.archiva.redback.users.ldap.ctl.LdapController#getUser(String, javax.naming.directory.DirContext)
343      */
344     public LdapUser getUser( String username, DirContext context )
345         throws LdapControllerException, MappingException
346     {
347 
348         log.debug( "Searching for user: {}", username );
349 
350         LdapUserQuery query = new LdapUserQuery();
351         query.setUsername( username );
352 
353         NamingEnumeration<SearchResult> result = null;
354         try
355         {
356             result = searchUsers( context, null, query );
357 
358             if ( result.hasMoreElements() )
359             {
360                 SearchResult next = result.nextElement();
361 
362                 log.info( "Found user: {}", username );
363 
364                 return mapper.getUser( next.getAttributes() );
365             }
366             else
367             {
368                 return null;
369             }
370         }
371         catch ( NamingException e )
372         {
373             String message = "Failed to retrieve information for user: " + username;
374 
375             throw new LdapControllerException( message, e );
376         }
377         finally
378         {
379             if ( result != null )
380             {
381                 try
382                 {
383                     result.close();
384                 }
385                 catch ( NamingException e )
386                 {
387                     log.warn( "failed to close search results", e );
388                 }
389             }
390         }
391     }
392 
393     public Map<String, Collection<String>> findUsersWithRoles( DirContext dirContext )
394         throws LdapControllerException
395     {
396         Map<String, Collection<String>> usersWithRoles = new HashMap<String, Collection<String>>();
397 
398         NamingEnumeration<SearchResult> namingEnumeration = null;
399         try
400         {
401 
402             SearchControls searchControls = new SearchControls();
403 
404             searchControls.setDerefLinkFlag( true );
405             searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
406 
407             String filter = "objectClass=" + getLdapGroupClass();
408 
409             namingEnumeration = dirContext.search( getGroupsDn(), filter, searchControls );
410 
411             while ( namingEnumeration.hasMore() )
412             {
413                 SearchResult searchResult = namingEnumeration.next();
414 
415                 String groupName = searchResult.getName();
416                 // cn=blabla we only want bla bla
417                 groupName = StringUtils.substringAfter( groupName, "=" );
418 
419                 Attribute uniqueMemberAttr = searchResult.getAttributes().get( "uniquemember" );
420 
421                 if ( uniqueMemberAttr != null )
422                 {
423                     NamingEnumeration<String> allMembersEnum = (NamingEnumeration<String>) uniqueMemberAttr.getAll();
424                     while ( allMembersEnum.hasMore() )
425                     {
426                         String userName = allMembersEnum.next();
427                         // uid=blabla we only want bla bla
428                         userName = StringUtils.substringAfter( userName, "=" );
429                         userName = StringUtils.substringBefore( userName, "," );
430                         Collection<String> roles = usersWithRoles.get( userName );
431                         if ( roles == null )
432                         {
433                             roles = new HashSet<String>();
434                         }
435 
436                         roles.add( groupName );
437 
438                         usersWithRoles.put( userName, roles );
439 
440                     }
441                 }
442 
443                 log.debug( "found groupName: '{}' with users: {}", groupName );
444 
445             }
446 
447             return usersWithRoles;
448         }
449         catch ( NamingException e )
450         {
451             throw new LdapControllerException( e.getMessage(), e );
452         }
453 
454         finally
455         {
456 
457             if ( namingEnumeration != null )
458             {
459                 try
460                 {
461                     namingEnumeration.close();
462                 }
463                 catch ( NamingException e )
464                 {
465                     log.warn( "failed to close search results", e );
466                 }
467             }
468         }
469     }
470 
471     //-----------------------------
472     // setters/getters
473     //-----------------------------
474     public UserMapper getMapper()
475     {
476         return mapper;
477     }
478 
479     public void setMapper( UserMapper mapper )
480     {
481         this.mapper = mapper;
482     }
483 
484     public UserConfiguration getUserConf()
485     {
486         return userConf;
487     }
488 
489     public void setUserConf( UserConfiguration userConf )
490     {
491         this.userConf = userConf;
492     }
493 
494     public boolean isWritableLdap()
495     {
496         return writableLdap;
497     }
498 
499     public void setWritableLdap( boolean writableLdap )
500     {
501         this.writableLdap = writableLdap;
502     }
503 
504     public PasswordEncoder getPasswordEncoder()
505     {
506         return passwordEncoder;
507     }
508 
509     public void setPasswordEncoder( PasswordEncoder passwordEncoder )
510     {
511         this.passwordEncoder = passwordEncoder;
512     }
513 
514     public String getBaseDn()
515     {
516         return baseDn;
517     }
518 
519     public void setBaseDn( String baseDn )
520     {
521         this.baseDn = baseDn;
522     }
523 
524     public String getGroupsDn()
525     {
526         return groupsDn;
527     }
528 
529     public void setGroupsDn( String groupsDn )
530     {
531         this.groupsDn = groupsDn;
532     }
533 
534     public String getLdapGroupClass()
535     {
536         return ldapGroupClass;
537     }
538 
539     public void setLdapGroupClass( String ldapGroupClass )
540     {
541         this.ldapGroupClass = ldapGroupClass;
542     }
543 }