1 package org.apache.archiva.redback.users.ldap.ctl;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
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
100
101 public void removeUser( String principal, DirContext context )
102 throws LdapControllerException
103 {
104
105 }
106
107
108
109
110 public void updateUser( User user, DirContext context )
111 throws LdapControllerException, MappingException
112 {
113
114 }
115
116
117
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
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
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
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
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
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
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
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
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 }