1 package org.apache.archiva.redback.common.ldap.role;
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
21 import com.google.common.collect.HashBiMap;
22 import org.apache.archiva.redback.common.ldap.MappingException;
23 import org.apache.archiva.redback.common.ldap.connection.LdapConnection;
24 import org.apache.archiva.redback.common.ldap.connection.LdapConnectionFactory;
25 import org.apache.archiva.redback.common.ldap.connection.LdapException;
26 import org.apache.archiva.redback.configuration.UserConfiguration;
27 import org.apache.archiva.redback.configuration.UserConfigurationKeys;
28 import org.apache.commons.lang.StringUtils;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31 import org.springframework.stereotype.Service;
33 import javax.annotation.PostConstruct;
34 import javax.inject.Inject;
35 import javax.inject.Named;
36 import javax.naming.NamingEnumeration;
37 import javax.naming.NamingException;
38 import javax.naming.directory.Attribute;
39 import javax.naming.directory.Attributes;
40 import javax.naming.directory.BasicAttribute;
41 import javax.naming.directory.BasicAttributes;
42 import javax.naming.directory.DirContext;
43 import javax.naming.directory.ModificationItem;
44 import javax.naming.directory.SearchControls;
45 import javax.naming.directory.SearchResult;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.Collection;
49 import java.util.Collections;
50 import java.util.HashMap;
51 import java.util.List;
55 * @author Olivier Lamy
58 @Service( "ldapRoleMapper#default" )
59 public class DefaultLdapRoleMapper
60 implements LdapRoleMapper
63 private Logger log = LoggerFactory.getLogger( getClass() );
66 private LdapConnectionFactory ldapConnectionFactory;
69 @Named( value = "userConfiguration#default" )
70 private UserConfiguration userConf;
72 //---------------------------
74 //---------------------------
76 private String ldapGroupClass = "groupOfUniqueNames";
78 private String groupsDn;
80 private String baseDn;
83 public void initialize()
85 this.ldapGroupClass = userConf.getString( UserConfigurationKeys.LDAP_GROUPS_CLASS, this.ldapGroupClass );
87 this.baseDn = userConf.getConcatenatedList( UserConfigurationKeys.LDAP_BASEDN, this.baseDn );
89 this.groupsDn = userConf.getConcatenatedList( UserConfigurationKeys.LDAP_GROUPS_BASEDN, this.groupsDn );
91 if ( StringUtils.isEmpty( this.groupsDn ) )
93 this.groupsDn = this.baseDn;
97 public String getLdapGroup( String role )
99 return userConf.getString( UserConfigurationKeys.LDAP_GROUPS_ROLE_START_KEY + role );
102 public List<String> getAllGroups()
103 throws MappingException
105 LdapConnection ldapConnection = null;
107 NamingEnumeration<SearchResult> namingEnumeration = null;
110 ldapConnection = ldapConnectionFactory.getConnection();
112 DirContext context = ldapConnection.getDirContext();
114 SearchControls searchControls = new SearchControls();
116 searchControls.setDerefLinkFlag( true );
117 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
119 String filter = "objectClass=" + getLdapGroupClass();
121 namingEnumeration = context.search( getGroupsDn(), filter, searchControls );
123 List<String> allGroups = new ArrayList<String>();
125 while ( namingEnumeration.hasMore() )
127 SearchResult searchResult = namingEnumeration.next();
129 String groupName = searchResult.getName();
130 // cn=blabla we only want bla bla
131 groupName = StringUtils.substringAfter( groupName, "=" );
133 log.debug( "found groupName: '{}", groupName );
135 allGroups.add( groupName );
141 catch ( LdapException e )
143 throw new MappingException( e.getMessage(), e );
145 catch ( NamingException e )
147 throw new MappingException( e.getMessage(), e );
152 if ( ldapConnection != null )
154 ldapConnection.close();
156 if ( namingEnumeration != null )
160 namingEnumeration.close();
162 catch ( NamingException e )
164 log.warn( "failed to close search results", e );
170 public List<String> getAllRoles()
171 throws MappingException
173 // TODO read from ldap ?
174 List<String> groups = getAllGroups();
176 if ( groups.isEmpty() )
178 return Collections.emptyList();
181 List<String> roles = new ArrayList<String>( groups.size() );
183 Map<String, String> mapping = getLdapGroupMappings();
185 for ( String group : groups )
187 String role = mapping.get( group );
197 public List<String> getGroupsMember( String group )
198 throws MappingException
200 LdapConnection ldapConnection = null;
202 NamingEnumeration<SearchResult> namingEnumeration = null;
205 ldapConnection = ldapConnectionFactory.getConnection();
207 DirContext context = ldapConnection.getDirContext();
209 SearchControls searchControls = new SearchControls();
211 searchControls.setDerefLinkFlag( true );
212 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
214 String filter = "objectClass=" + getLdapGroupClass();
216 namingEnumeration = context.search( "cn=" + group + "," + getGroupsDn(), filter, searchControls );
218 List<String> allMembers = new ArrayList<String>();
220 while ( namingEnumeration.hasMore() )
222 SearchResult searchResult = namingEnumeration.next();
224 Attribute uniqueMemberAttr = searchResult.getAttributes().get( "uniquemember" );
226 if ( uniqueMemberAttr != null )
228 NamingEnumeration<String> allMembersEnum = (NamingEnumeration<String>) uniqueMemberAttr.getAll();
229 while ( allMembersEnum.hasMore() )
231 String userName = allMembersEnum.next();
232 // uid=blabla we only want bla bla
233 userName = StringUtils.substringAfter( userName, "=" );
234 userName = StringUtils.substringBefore( userName, "," );
235 log.debug( "found userName for group {}: '{}", group, userName );
237 allMembers.add( userName );
239 close( allMembersEnum );
247 catch ( LdapException e )
249 throw new MappingException( e.getMessage(), e );
251 catch ( NamingException e )
253 throw new MappingException( e.getMessage(), e );
258 if ( ldapConnection != null )
260 ldapConnection.close();
262 close( namingEnumeration );
266 public List<String> getGroups( String username )
267 throws MappingException
270 List<String> userGroups = new ArrayList<String>();
272 LdapConnection ldapConnection = null;
274 NamingEnumeration<SearchResult> namingEnumeration = null;
277 ldapConnection = ldapConnectionFactory.getConnection();
279 DirContext context = ldapConnection.getDirContext();
281 SearchControls searchControls = new SearchControls();
283 searchControls.setDerefLinkFlag( true );
284 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
287 new StringBuilder().append( "(&" ).append( "(objectClass=" + getLdapGroupClass() + ")" ).append(
288 "(uniquemember=" ).append( "uid=" + username + "," + this.getBaseDn() ).append( ")" ).append(
291 log.debug( "filter: {}", filter );
293 namingEnumeration = context.search( getGroupsDn(), filter, searchControls );
295 while ( namingEnumeration.hasMore() )
297 SearchResult searchResult = namingEnumeration.next();
299 List<String> allMembers = new ArrayList<String>();
301 Attribute uniqueMemberAttr = searchResult.getAttributes().get( "uniquemember" );
303 if ( uniqueMemberAttr != null )
305 NamingEnumeration<String> allMembersEnum = (NamingEnumeration<String>) uniqueMemberAttr.getAll();
306 while ( allMembersEnum.hasMore() )
308 String userName = allMembersEnum.next();
309 // uid=blabla we only want bla bla
310 userName = StringUtils.substringAfter( userName, "=" );
311 userName = StringUtils.substringBefore( userName, "," );
312 allMembers.add( userName );
314 close( allMembersEnum );
317 if ( allMembers.contains( username ) )
319 String groupName = searchResult.getName();
320 // cn=blabla we only want bla bla
321 groupName = StringUtils.substringAfter( groupName, "=" );
322 userGroups.add( groupName );
331 catch ( LdapException e )
333 throw new MappingException( e.getMessage(), e );
335 catch ( NamingException e )
337 throw new MappingException( e.getMessage(), e );
342 if ( ldapConnection != null )
344 ldapConnection.close();
346 close( namingEnumeration );
350 public List<String> getRoles( String username )
351 throws MappingException
353 List<String> groups = getGroups( username );
355 Map<String, String> rolesMapping = getLdapGroupMappings();
357 List<String> roles = new ArrayList<String>( groups.size() );
359 for ( String group : groups )
361 String role = rolesMapping.get( group );
371 private void close( NamingEnumeration namingEnumeration )
373 if ( namingEnumeration != null )
377 namingEnumeration.close();
379 catch ( NamingException e )
381 log.warn( "fail to close namingEnumeration: {}", e.getMessage() );
386 public String getGroupsDn()
388 return this.groupsDn;
391 public String getLdapGroupClass()
393 return this.ldapGroupClass;
396 public void addLdapMapping( String role, String ldapGroup )
398 log.warn( "addLdapMapping not implemented" );
401 public void removeLdapMapping( String role )
403 log.warn( "removeLdapMapping not implemented" );
406 public void setLdapGroupMappings( Map<String, String> mappings )
407 throws MappingException
409 log.warn( "setLdapGroupMappings not implemented" );
412 public Map<String, String> getLdapGroupMappings()
414 Map<String, String> map = new HashMap<String, String>();
416 Collection<String> keys = userConf.getKeys();
418 for ( String key : keys )
420 if ( key.startsWith( UserConfigurationKeys.LDAP_GROUPS_ROLE_START_KEY ) )
422 map.put( StringUtils.substringAfter( key, UserConfigurationKeys.LDAP_GROUPS_ROLE_START_KEY ),
423 userConf.getString( key ) );
430 public boolean saveRole( String roleName )
431 throws MappingException
434 String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
435 if ( groupName == null )
437 log.warn( "skip group creation as no mapping fro roleName:'{}'", roleName );
441 List<String> allGroups = getAllGroups();
442 if ( allGroups.contains( groupName ) )
444 log.info( "group {} already exists for role.", groupName, roleName );
448 Attributes attributes = new BasicAttributes( true );
449 BasicAttribute objectClass = new BasicAttribute( "objectClass" );
450 objectClass.add( "top" );
451 objectClass.add( "groupOfUniqueNames" );
452 attributes.put( objectClass );
453 attributes.put( "cn", groupName );
455 // attribute mandatory when created a group so add admin as default member
456 // TODO make this default configurable
457 BasicAttribute basicAttribute = new BasicAttribute( "uniquemember" );
458 basicAttribute.add( "uid=admin," + getBaseDn() );
459 attributes.put( basicAttribute );
461 LdapConnection ldapConnection = null;
465 ldapConnection = ldapConnectionFactory.getConnection();
467 DirContext context = ldapConnection.getDirContext();
469 String dn = "cn=" + groupName + "," + this.groupsDn;
471 context.createSubcontext( dn, attributes );
473 log.info( "created group with dn:'{}", dn );
477 catch ( LdapException e )
479 throw new MappingException( e.getMessage(), e );
482 catch ( NamingException e )
484 throw new MappingException( e.getMessage(), e );
488 if ( ldapConnection != null )
490 ldapConnection.close();
495 public boolean saveUserRole( String roleName, String username )
496 throws MappingException
499 String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
501 if ( groupName == null )
503 log.warn( "no group found for role '{}", roleName );
507 LdapConnection ldapConnection = null;
509 NamingEnumeration<SearchResult> namingEnumeration = null;
512 ldapConnection = ldapConnectionFactory.getConnection();
514 DirContext context = ldapConnection.getDirContext();
516 SearchControls searchControls = new SearchControls();
518 searchControls.setDerefLinkFlag( true );
519 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
521 String filter = "objectClass=" + getLdapGroupClass();
523 namingEnumeration = context.search( "cn=" + groupName + "," + getGroupsDn(), filter, searchControls );
525 while ( namingEnumeration.hasMore() )
527 SearchResult searchResult = namingEnumeration.next();
528 Attribute attribute = searchResult.getAttributes().get( "uniquemember" );
529 if ( attribute == null )
531 BasicAttribute basicAttribute = new BasicAttribute( "uniquemember" );
532 basicAttribute.add( "uid=" + username + "," + getGroupsDn() );
533 context.modifyAttributes( "cn=" + groupName + "," + getGroupsDn(), new ModificationItem[]{
534 new ModificationItem( DirContext.ADD_ATTRIBUTE, basicAttribute ) } );
538 attribute.add( "uid=" + username + "," + getGroupsDn() );
539 context.modifyAttributes( "cn=" + groupName + "," + getGroupsDn(), new ModificationItem[]{
540 new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attribute ) } );
547 catch ( LdapException e )
549 throw new MappingException( e.getMessage(), e );
551 catch ( NamingException e )
553 throw new MappingException( e.getMessage(), e );
558 if ( ldapConnection != null )
560 ldapConnection.close();
562 if ( namingEnumeration != null )
566 namingEnumeration.close();
568 catch ( NamingException e )
570 log.warn( "failed to close search results", e );
576 public boolean removeUserRole( String roleName, String username )
577 throws MappingException
579 String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
581 if ( groupName == null )
583 log.warn( "no group found for role '{}", roleName );
587 LdapConnection ldapConnection = null;
589 NamingEnumeration<SearchResult> namingEnumeration = null;
592 ldapConnection = ldapConnectionFactory.getConnection();
594 DirContext context = ldapConnection.getDirContext();
596 SearchControls searchControls = new SearchControls();
598 searchControls.setDerefLinkFlag( true );
599 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
601 String filter = "objectClass=" + getLdapGroupClass();
603 namingEnumeration = context.search( "cn=" + groupName + "," + getGroupsDn(), filter, searchControls );
605 while ( namingEnumeration.hasMore() )
607 SearchResult searchResult = namingEnumeration.next();
608 Attribute attribute = searchResult.getAttributes().get( "uniquemember" );
609 if ( attribute != null )
611 BasicAttribute basicAttribute = new BasicAttribute( "uniquemember" );
612 basicAttribute.add( "uid=" + username + "," + getGroupsDn() );
613 context.modifyAttributes( "cn=" + groupName + "," + getGroupsDn(), new ModificationItem[]{
614 new ModificationItem( DirContext.REMOVE_ATTRIBUTE, basicAttribute ) } );
621 catch ( LdapException e )
623 throw new MappingException( e.getMessage(), e );
625 catch ( NamingException e )
627 throw new MappingException( e.getMessage(), e );
632 if ( ldapConnection != null )
634 ldapConnection.close();
636 if ( namingEnumeration != null )
640 namingEnumeration.close();
642 catch ( NamingException e )
644 log.warn( "failed to close search results", e );
650 public void removeAllRoles()
651 throws MappingException
654 Collection<String> groups = getLdapGroupMappings().keySet();
656 LdapConnection ldapConnection = null;
659 ldapConnection = ldapConnectionFactory.getConnection();
661 DirContext context = ldapConnection.getDirContext();
663 for ( String groupName : groups )
666 String dn = "cn=" + groupName + "," + this.groupsDn;
668 context.unbind( dn );
670 log.debug( "deleted group with dn:'{}", dn );
674 catch ( LdapException e )
676 throw new MappingException( e.getMessage(), e );
679 catch ( NamingException e )
681 throw new MappingException( e.getMessage(), e );
685 if ( ldapConnection != null )
687 ldapConnection.close();
692 public void removeRole( String roleName )
693 throws MappingException
696 String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
698 LdapConnection ldapConnection = null;
701 ldapConnection = ldapConnectionFactory.getConnection();
703 DirContext context = ldapConnection.getDirContext();
705 String dn = "cn=" + groupName + "," + this.groupsDn;
707 context.unbind( dn );
709 log.info( "deleted group with dn:'{}", dn );
712 catch ( LdapException e )
714 throw new MappingException( e.getMessage(), e );
717 catch ( NamingException e )
719 throw new MappingException( e.getMessage(), e );
723 if ( ldapConnection != null )
725 ldapConnection.close();
730 //---------------------------------
731 // setters for unit tests
732 //---------------------------------
735 public void setGroupsDn( String groupsDn )
737 this.groupsDn = groupsDn;
740 public void setLdapGroupClass( String ldapGroupClass )
742 this.ldapGroupClass = ldapGroupClass;
745 public void setUserConf( UserConfiguration userConf )
747 this.userConf = userConf;
750 public void setLdapConnectionFactory( LdapConnectionFactory ldapConnectionFactory )
752 this.ldapConnectionFactory = ldapConnectionFactory;
755 public String getBaseDn()
760 public void setBaseDn( String baseDn )
762 this.baseDn = baseDn;