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( DirContext context )
103 throws MappingException
106 NamingEnumeration<SearchResult> namingEnumeration = null;
110 SearchControls searchControls = new SearchControls();
112 searchControls.setDerefLinkFlag( true );
113 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
115 String filter = "objectClass=" + getLdapGroupClass();
117 namingEnumeration = context.search( getGroupsDn(), filter, searchControls );
119 List<String> allGroups = new ArrayList<String>();
121 while ( namingEnumeration.hasMore() )
123 SearchResult searchResult = namingEnumeration.next();
125 String groupName = searchResult.getName();
126 // cn=blabla we only want bla bla
127 groupName = StringUtils.substringAfter( groupName, "=" );
129 log.debug( "found groupName: '{}", groupName );
131 allGroups.add( groupName );
137 catch ( LdapException e )
139 throw new MappingException( e.getMessage(), e );
141 catch ( NamingException e )
143 throw new MappingException( e.getMessage(), e );
148 close( namingEnumeration );
152 protected void closeNamingEnumeration( NamingEnumeration namingEnumeration )
154 if ( namingEnumeration != null )
158 namingEnumeration.close();
160 catch ( NamingException e )
162 log.warn( "failed to close NamingEnumeration", e );
167 public List<String> getAllRoles( DirContext context )
168 throws MappingException
170 List<String> groups = getAllGroups( context );
172 if ( groups.isEmpty() )
174 return Collections.emptyList();
177 List<String> roles = new ArrayList<String>( groups.size() );
179 Map<String, String> mapping = getLdapGroupMappings();
181 for ( String group : groups )
183 String role = mapping.get( group );
193 public List<String> getGroupsMember( String group, DirContext context )
194 throws MappingException
197 NamingEnumeration<SearchResult> namingEnumeration = null;
201 SearchControls searchControls = new SearchControls();
203 searchControls.setDerefLinkFlag( true );
204 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
206 String filter = "objectClass=" + getLdapGroupClass();
208 namingEnumeration = context.search( "cn=" + group + "," + getGroupsDn(), filter, searchControls );
210 List<String> allMembers = new ArrayList<String>();
212 while ( namingEnumeration.hasMore() )
214 SearchResult searchResult = namingEnumeration.next();
216 Attribute uniqueMemberAttr = searchResult.getAttributes().get( "uniquemember" );
218 if ( uniqueMemberAttr != null )
220 NamingEnumeration<String> allMembersEnum = (NamingEnumeration<String>) uniqueMemberAttr.getAll();
221 while ( allMembersEnum.hasMore() )
223 String userName = allMembersEnum.next();
224 // uid=blabla we only want bla bla
225 userName = StringUtils.substringAfter( userName, "=" );
226 userName = StringUtils.substringBefore( userName, "," );
227 log.debug( "found userName for group {}: '{}", group, userName );
229 allMembers.add( userName );
231 close( allMembersEnum );
239 catch ( LdapException e )
241 throw new MappingException( e.getMessage(), e );
243 catch ( NamingException e )
245 throw new MappingException( e.getMessage(), e );
250 close( namingEnumeration );
254 public List<String> getGroups( String username, DirContext context )
255 throws MappingException
258 List<String> userGroups = new ArrayList<String>();
260 NamingEnumeration<SearchResult> namingEnumeration = null;
264 SearchControls searchControls = new SearchControls();
266 searchControls.setDerefLinkFlag( true );
267 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
270 new StringBuilder().append( "(&" ).append( "(objectClass=" + getLdapGroupClass() + ")" ).append(
271 "(uniquemember=" ).append( "uid=" + username + "," + this.getBaseDn() ).append( ")" ).append(
274 log.debug( "filter: {}", filter );
276 namingEnumeration = context.search( getGroupsDn(), filter, searchControls );
278 while ( namingEnumeration.hasMore() )
280 SearchResult searchResult = namingEnumeration.next();
282 List<String> allMembers = new ArrayList<String>();
284 Attribute uniqueMemberAttr = searchResult.getAttributes().get( "uniquemember" );
286 if ( uniqueMemberAttr != null )
288 NamingEnumeration<String> allMembersEnum = (NamingEnumeration<String>) uniqueMemberAttr.getAll();
289 while ( allMembersEnum.hasMore() )
291 String userName = allMembersEnum.next();
292 // uid=blabla we only want bla bla
293 userName = StringUtils.substringAfter( userName, "=" );
294 userName = StringUtils.substringBefore( userName, "," );
295 allMembers.add( userName );
297 close( allMembersEnum );
300 if ( allMembers.contains( username ) )
302 String groupName = searchResult.getName();
303 // cn=blabla we only want bla bla
304 groupName = StringUtils.substringAfter( groupName, "=" );
305 userGroups.add( groupName );
314 catch ( LdapException e )
316 throw new MappingException( e.getMessage(), e );
318 catch ( NamingException e )
320 throw new MappingException( e.getMessage(), e );
324 close( namingEnumeration );
328 public List<String> getRoles( String username, DirContext context )
329 throws MappingException
331 List<String> groups = getGroups( username, context );
333 Map<String, String> rolesMapping = getLdapGroupMappings();
335 List<String> roles = new ArrayList<String>( groups.size() );
337 for ( String group : groups )
339 String role = rolesMapping.get( group );
349 private void close( NamingEnumeration namingEnumeration )
351 if ( namingEnumeration != null )
355 namingEnumeration.close();
357 catch ( NamingException e )
359 log.warn( "fail to close namingEnumeration: {}", e.getMessage() );
364 public String getGroupsDn()
366 return this.groupsDn;
369 public String getLdapGroupClass()
371 return this.ldapGroupClass;
374 public void addLdapMapping( String role, String ldapGroup )
376 log.warn( "addLdapMapping not implemented" );
379 public void removeLdapMapping( String role )
381 log.warn( "removeLdapMapping not implemented" );
384 public void setLdapGroupMappings( Map<String, String> mappings )
385 throws MappingException
387 log.warn( "setLdapGroupMappings not implemented" );
390 public Map<String, String> getLdapGroupMappings()
392 Map<String, String> map = new HashMap<String, String>();
394 Collection<String> keys = userConf.getKeys();
396 for ( String key : keys )
398 if ( key.startsWith( UserConfigurationKeys.LDAP_GROUPS_ROLE_START_KEY ) )
400 map.put( StringUtils.substringAfter( key, UserConfigurationKeys.LDAP_GROUPS_ROLE_START_KEY ),
401 userConf.getString( key ) );
408 public boolean saveRole( String roleName, DirContext context )
409 throws MappingException
412 String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
413 if ( groupName == null )
415 log.warn( "skip group creation as no mapping fro roleName:'{}'", roleName );
419 List<String> allGroups = getAllGroups( context );
420 if ( allGroups.contains( groupName ) )
422 log.info( "group {} already exists for role.", groupName, roleName );
426 Attributes attributes = new BasicAttributes( true );
427 BasicAttribute objectClass = new BasicAttribute( "objectClass" );
428 objectClass.add( "top" );
429 objectClass.add( "groupOfUniqueNames" );
430 attributes.put( objectClass );
431 attributes.put( "cn", groupName );
433 // attribute mandatory when created a group so add admin as default member
434 // TODO make this default configurable
435 BasicAttribute basicAttribute = new BasicAttribute( "uniquemember" );
436 basicAttribute.add( "uid=admin," + getBaseDn() );
437 attributes.put( basicAttribute );
441 String dn = "cn=" + groupName + "," + this.groupsDn;
443 context.createSubcontext( dn, attributes );
445 log.info( "created group with dn:'{}", dn );
449 catch ( LdapException e )
451 throw new MappingException( e.getMessage(), e );
454 catch ( NamingException e )
456 throw new MappingException( e.getMessage(), e );
460 public boolean saveUserRole( String roleName, String username, DirContext context )
461 throws MappingException
464 String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
466 if ( groupName == null )
468 log.warn( "no group found for role '{}", roleName );
472 NamingEnumeration<SearchResult> namingEnumeration = null;
475 SearchControls searchControls = new SearchControls();
477 searchControls.setDerefLinkFlag( true );
478 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
480 String filter = "objectClass=" + getLdapGroupClass();
482 namingEnumeration = context.search( "cn=" + groupName + "," + getGroupsDn(), filter, searchControls );
484 while ( namingEnumeration.hasMore() )
486 SearchResult searchResult = namingEnumeration.next();
487 Attribute attribute = searchResult.getAttributes().get( "uniquemember" );
488 if ( attribute == null )
490 BasicAttribute basicAttribute = new BasicAttribute( "uniquemember" );
491 basicAttribute.add( "uid=" + username + "," + getGroupsDn() );
492 context.modifyAttributes( "cn=" + groupName + "," + getGroupsDn(), new ModificationItem[]{
493 new ModificationItem( DirContext.ADD_ATTRIBUTE, basicAttribute ) } );
497 attribute.add( "uid=" + username + "," + getGroupsDn() );
498 context.modifyAttributes( "cn=" + groupName + "," + getGroupsDn(), new ModificationItem[]{
499 new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attribute ) } );
506 catch ( LdapException e )
508 throw new MappingException( e.getMessage(), e );
510 catch ( NamingException e )
512 throw new MappingException( e.getMessage(), e );
517 if ( namingEnumeration != null )
521 namingEnumeration.close();
523 catch ( NamingException e )
525 log.warn( "failed to close search results", e );
531 public boolean removeUserRole( String roleName, String username, DirContext context )
532 throws MappingException
534 String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
536 if ( groupName == null )
538 log.warn( "no group found for role '{}", roleName );
542 NamingEnumeration<SearchResult> namingEnumeration = null;
546 SearchControls searchControls = new SearchControls();
548 searchControls.setDerefLinkFlag( true );
549 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
551 String filter = "objectClass=" + getLdapGroupClass();
553 namingEnumeration = context.search( "cn=" + groupName + "," + getGroupsDn(), filter, searchControls );
555 while ( namingEnumeration.hasMore() )
557 SearchResult searchResult = namingEnumeration.next();
558 Attribute attribute = searchResult.getAttributes().get( "uniquemember" );
559 if ( attribute != null )
561 BasicAttribute basicAttribute = new BasicAttribute( "uniquemember" );
562 basicAttribute.add( "uid=" + username + "," + getGroupsDn() );
563 context.modifyAttributes( "cn=" + groupName + "," + getGroupsDn(), new ModificationItem[]{
564 new ModificationItem( DirContext.REMOVE_ATTRIBUTE, basicAttribute ) } );
571 catch ( LdapException e )
573 throw new MappingException( e.getMessage(), e );
575 catch ( NamingException e )
577 throw new MappingException( e.getMessage(), e );
582 if ( namingEnumeration != null )
586 namingEnumeration.close();
588 catch ( NamingException e )
590 log.warn( "failed to close search results", e );
596 public void removeAllRoles( DirContext context )
597 throws MappingException
600 Collection<String> groups = getLdapGroupMappings().keySet();
604 for ( String groupName : groups )
607 String dn = "cn=" + groupName + "," + this.groupsDn;
609 context.unbind( dn );
611 log.debug( "deleted group with dn:'{}", dn );
615 catch ( LdapException e )
617 throw new MappingException( e.getMessage(), e );
620 catch ( NamingException e )
622 throw new MappingException( e.getMessage(), e );
626 public void removeRole( String roleName, DirContext context )
627 throws MappingException
630 String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
635 String dn = "cn=" + groupName + "," + this.groupsDn;
637 context.unbind( dn );
639 log.info( "deleted group with dn:'{}", dn );
642 catch ( LdapException e )
644 throw new MappingException( e.getMessage(), e );
647 catch ( NamingException e )
649 throw new MappingException( e.getMessage(), e );
653 //---------------------------------
654 // setters for unit tests
655 //---------------------------------
658 public void setGroupsDn( String groupsDn )
660 this.groupsDn = groupsDn;
663 public void setLdapGroupClass( String ldapGroupClass )
665 this.ldapGroupClass = ldapGroupClass;
668 public void setUserConf( UserConfiguration userConf )
670 this.userConf = userConf;
673 public void setLdapConnectionFactory( LdapConnectionFactory ldapConnectionFactory )
675 this.ldapConnectionFactory = ldapConnectionFactory;
678 public String getBaseDn()
683 public void setBaseDn( String baseDn )
685 this.baseDn = baseDn;