001package org.apache.archiva.redback.common.ldap.role; 002/* 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 */ 020 021import org.apache.archiva.redback.common.ldap.MappingException; 022import org.apache.archiva.redback.common.ldap.ObjectNotFoundException; 023import org.apache.archiva.redback.common.ldap.connection.LdapConnectionFactory; 024import org.apache.archiva.redback.common.ldap.connection.LdapException; 025import org.apache.archiva.redback.common.ldap.user.LdapUser; 026import org.apache.archiva.redback.configuration.UserConfiguration; 027import org.apache.archiva.redback.configuration.UserConfigurationKeys; 028import org.apache.archiva.redback.users.User; 029import org.apache.archiva.redback.users.UserManager; 030import org.apache.archiva.redback.users.UserManagerException; 031import org.apache.archiva.redback.users.UserNotFoundException; 032import org.apache.commons.lang3.StringUtils; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035import org.springframework.stereotype.Service; 036 037import javax.annotation.PostConstruct; 038import javax.inject.Inject; 039import javax.inject.Named; 040import javax.naming.NameAlreadyBoundException; 041import javax.naming.NameNotFoundException; 042import javax.naming.NamingEnumeration; 043import javax.naming.NamingException; 044import javax.naming.directory.Attribute; 045import javax.naming.directory.Attributes; 046import javax.naming.directory.BasicAttribute; 047import javax.naming.directory.BasicAttributes; 048import javax.naming.directory.DirContext; 049import javax.naming.directory.ModificationItem; 050import javax.naming.directory.SearchControls; 051import javax.naming.directory.SearchResult; 052import javax.naming.ldap.LdapName; 053import javax.naming.ldap.Rdn; 054import java.util.ArrayList; 055import java.util.Collection; 056import java.util.Collections; 057import java.util.HashSet; 058import java.util.List; 059import java.util.Map; 060import java.util.Set; 061 062/** 063 * @author Olivier Lamy 064 * @since 2.1 065 */ 066@Service( "ldapRoleMapper#default" ) 067public class DefaultLdapRoleMapper 068 implements LdapRoleMapper 069{ 070 071 private Logger log = LoggerFactory.getLogger( getClass( ) ); 072 073 @Inject 074 @Named( value = "ldapConnectionFactory#configurable" ) 075 private LdapConnectionFactory ldapConnectionFactory; 076 077 @Inject 078 @Named( value = "userConfiguration#default" ) 079 private UserConfiguration userConf; 080 081 @Inject 082 @Named( value = "ldapRoleMapperConfiguration#default" ) 083 private LdapRoleMapperConfiguration ldapRoleMapperConfiguration; 084 085 @Inject 086 @Named( value = "userManager#default" ) 087 private UserManager userManager; 088 089 //--------------------------- 090 // fields 091 //--------------------------- 092 093 private String ldapGroupClass = "groupOfUniqueNames"; 094 095 private String groupsDn; 096 097 private String groupFilter; 098 099 private String baseDn; 100 101 private String ldapGroupMemberAttribute = "uniqueMember"; 102 103 private boolean useDefaultRoleName = false; 104 105 private String dnAttr = "dn"; 106 107 /** 108 * possible to user cn=beer or uid=beer or sn=beer etc 109 * so make it configurable 110 */ 111 public static String DEFAULT_USER_ID_ATTRIBUTE = "uid"; 112 private String userIdAttribute = DEFAULT_USER_ID_ATTRIBUTE; 113 114 public static String DEFAULT_GROUP_NAME_ATTRIBUTE = "cn"; 115 private String groupNameAttribute = DEFAULT_GROUP_NAME_ATTRIBUTE; 116 117 public static String DEFAULT_DESCRIPTION_ATTRIBUTE = "description"; 118 private String descriptionAttribute = DEFAULT_DESCRIPTION_ATTRIBUTE; 119 120 // True, if the member attribute stores the DN, otherwise the userkey is used as entry value 121 private boolean useDnAsMemberValue = true; 122 123 private static final String POSIX_GROUP = "posixGroup"; 124 125 @PostConstruct 126 public void initialize( ) 127 { 128 this.ldapGroupClass = userConf.getString( UserConfigurationKeys.LDAP_GROUPS_CLASS, this.ldapGroupClass ); 129 130 if (StringUtils.equalsIgnoreCase( POSIX_GROUP, this.ldapGroupClass )) { 131 this.useDnAsMemberValue = false; 132 } 133 134 this.useDnAsMemberValue = userConf.getBoolean( UserConfigurationKeys.LDAP_GROUPS_USE_DN_AS_MEMBER_VALUE, this.useDnAsMemberValue ); 135 136 this.baseDn = userConf.getConcatenatedList( UserConfigurationKeys.LDAP_BASEDN, this.baseDn ); 137 138 this.groupsDn = userConf.getConcatenatedList( UserConfigurationKeys.LDAP_GROUPS_BASEDN, this.groupsDn ); 139 140 if ( StringUtils.isEmpty( this.groupsDn ) ) 141 { 142 this.groupsDn = this.baseDn; 143 } 144 145 this.groupFilter = userConf.getString( UserConfigurationKeys.LDAP_GROUPS_FILTER, this.groupFilter ); 146 147 this.useDefaultRoleName = 148 userConf.getBoolean( UserConfigurationKeys.LDAP_GROUPS_USE_ROLENAME, this.useDefaultRoleName ); 149 150 this.userIdAttribute = userConf.getString( UserConfigurationKeys.LDAP_USER_ID_ATTRIBUTE, DEFAULT_USER_ID_ATTRIBUTE ); 151 152 this.ldapGroupMemberAttribute = userConf.getString( UserConfigurationKeys.LDAP_GROUPS_MEMBER, this.ldapGroupMemberAttribute ); 153 154 this.dnAttr = userConf.getString( UserConfigurationKeys.LDAP_DN_ATTRIBUTE, this.dnAttr ); 155 156 this.groupNameAttribute = userConf.getString( UserConfigurationKeys.LDAP_GROUP_NAME_ATTRIBUTE, DEFAULT_GROUP_NAME_ATTRIBUTE ); 157 158 this.descriptionAttribute = userConf.getString( UserConfigurationKeys.LDAP_GROUP_DESCRIPTION_ATTRIBUTE, DEFAULT_DESCRIPTION_ATTRIBUTE ); 159 } 160 161 162 private String getGroupNameFromResult( SearchResult searchResult ) throws NamingException 163 { 164 Attribute gNameAtt = searchResult.getAttributes( ).get( groupNameAttribute ); 165 if ( gNameAtt != null ) 166 { 167 return gNameAtt.get( ).toString( ); 168 } 169 else 170 { 171 log.error( "Could not get group name from attribute {}. Group DN: {}", groupNameAttribute, searchResult.getNameInNamespace( ) ); 172 return ""; 173 } 174 175 176 } 177 178 public List<String> getAllGroups( DirContext context ) 179 throws MappingException 180 { 181 182 NamingEnumeration<SearchResult> namingEnumeration = null; 183 try 184 { 185 186 SearchControls searchControls = new SearchControls( ); 187 188 searchControls.setDerefLinkFlag( true ); 189 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 190 searchControls.setReturningAttributes( new String[]{ this.getLdapDnAttribute(), "objectClass", groupNameAttribute} ); 191 192 String filter = "objectClass=" + getLdapGroupClass( ); 193 194 if ( !StringUtils.isEmpty( this.groupFilter ) ) 195 { 196 filter = "(&(" + filter + ")(" + this.groupFilter + "))"; 197 } 198 199 namingEnumeration = context.search( getGroupsDn( ), filter, searchControls ); 200 201 List<String> allGroups = new ArrayList<String>( ); 202 203 while ( namingEnumeration.hasMore( ) ) 204 { 205 SearchResult searchResult = namingEnumeration.next( ); 206 String groupName = getGroupNameFromResult( searchResult ); 207 if ( StringUtils.isNotEmpty( groupName ) ) 208 { 209 log.debug( "Found groupName: '{}", groupName ); 210 allGroups.add( groupName ); 211 } 212 } 213 214 return allGroups; 215 } 216 catch ( LdapException e ) 217 { 218 throw new MappingException( e.getMessage( ), e ); 219 } 220 catch ( NamingException e ) 221 { 222 throw new MappingException( e.getMessage( ), e ); 223 } 224 225 finally 226 { 227 close( namingEnumeration ); 228 } 229 } 230 231 @Override 232 public List<LdapGroup> getAllGroupObjects( DirContext context ) throws MappingException 233 { 234 235 NamingEnumeration<SearchResult> namingEnumeration = null; 236 try 237 { 238 239 SearchControls searchControls = new SearchControls( ); 240 241 searchControls.setDerefLinkFlag( true ); 242 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 243 searchControls.setReturningAttributes( new String[]{ this.getLdapDnAttribute(), "objectClass", groupNameAttribute, 244 ldapGroupMemberAttribute} ); 245 246 String filter = "objectClass=" + getLdapGroupClass( ); 247 248 if ( !StringUtils.isEmpty( this.groupFilter ) ) 249 { 250 filter = "(&(" + filter + ")(" + this.groupFilter + "))"; 251 } 252 253 namingEnumeration = context.search( getGroupsDn( ), filter, searchControls ); 254 255 List<LdapGroup> allGroups = new ArrayList<>( ); 256 257 while ( namingEnumeration.hasMore( ) ) 258 { 259 SearchResult searchResult = namingEnumeration.next( ); 260 allGroups.add( getGroupFromResult( searchResult ) ); 261 } 262 263 return allGroups; 264 } 265 catch ( LdapException e ) 266 { 267 throw new MappingException( e.getMessage( ), e ); 268 } 269 catch ( NamingException e ) 270 { 271 throw new MappingException( e.getMessage( ), e ); 272 } 273 finally 274 { 275 close( namingEnumeration ); 276 } 277 } 278 279 @Override 280 public LdapGroup getGroupForName( DirContext context, String groupName ) throws MappingException 281 { 282 NamingEnumeration<SearchResult> namingEnumeration = null; 283 try 284 { 285 286 SearchControls searchControls = new SearchControls( ); 287 288 searchControls.setDerefLinkFlag( true ); 289 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 290 searchControls.setReturningAttributes( new String[]{this.getLdapDnAttribute( ), "objectClass", groupNameAttribute, 291 ldapGroupMemberAttribute} ); 292 293 StringBuilder fiBuilder = new StringBuilder("(&(objectClass=" ).append( getLdapGroupClass( ) ).append(")"); 294 295 296 if ( !StringUtils.isEmpty( this.groupFilter ) ) 297 { 298 fiBuilder.append("(").append(this.groupFilter).append(")"); 299 } 300 fiBuilder.append("(").append(this.groupNameAttribute) 301 .append("=").append(groupName).append("))"); 302 namingEnumeration = context.search( getGroupsDn( ), fiBuilder.toString(), searchControls ); 303 if (namingEnumeration.hasMore()) { 304 SearchResult result = namingEnumeration.next( ); 305 return getGroupFromResult( result ); 306 } else { 307 throw new ObjectNotFoundException( "Group not found " + groupName ); 308 } 309 } 310 catch ( NamingException e ) 311 { 312 log.error( "Naming error while searching for group {}: {}", groupName, e.getMessage( ) ); 313 throw new MappingException( "Group search failed " + e.getMessage( ), e ); 314 } finally 315 { 316 closeNamingEnumeration( namingEnumeration ); 317 } 318 } 319 320 private LdapGroup getGroupFromResult(SearchResult searchResult) throws NamingException 321 { 322 LdapGroup group = new LdapGroup( searchResult.getNameInNamespace() ); 323 Attribute attValue = searchResult.getAttributes( ).get( groupNameAttribute ); 324 if ( attValue != null ) 325 { 326 group.setName( attValue.get( ).toString( ) ); 327 } 328 else 329 { 330 log.error( "Could not get group name from attribute {}. Group DN: {}", groupNameAttribute, searchResult.getNameInNamespace( ) ); 331 } 332 attValue = searchResult.getAttributes( ).get( descriptionAttribute ); 333 if (attValue!=null) { 334 group.setDescription( attValue.get( ).toString( ) ); 335 } 336 Attribute memberValues = searchResult.getAttributes( ).get( ldapGroupMemberAttribute ); 337 if (memberValues!=null) 338 { 339 NamingEnumeration<?> allMembersEnum = memberValues.getAll( ); 340 try 341 { 342 while ( allMembersEnum.hasMore( ) ) 343 { 344 String memberValue = allMembersEnum.next( ).toString( ); 345 if ( !StringUtils.isEmpty( memberValue ) ) 346 { 347 group.addMember( memberValue ); 348 } 349 } 350 } finally 351 { 352 if (allMembersEnum!=null) { 353 closeNamingEnumeration( allMembersEnum ); 354 } 355 } 356 } 357 return group; 358 } 359 360 protected void closeNamingEnumeration( NamingEnumeration namingEnumeration ) 361 { 362 if ( namingEnumeration != null ) 363 { 364 try 365 { 366 namingEnumeration.close( ); 367 } 368 catch ( NamingException e ) 369 { 370 log.warn( "failed to close NamingEnumeration", e ); 371 } 372 } 373 } 374 375 public boolean hasRole( DirContext context, String roleName ) 376 throws MappingException 377 { 378 String groupName = findGroupName( roleName ); 379 380 if ( groupName == null ) 381 { 382 if ( this.useDefaultRoleName ) 383 { 384 groupName = roleName; 385 } 386 else 387 { 388 log.warn( "skip group creation as no mapping for roleName:'{}'", roleName ); 389 return false; 390 } 391 } 392 NamingEnumeration<SearchResult> namingEnumeration = null; 393 try 394 { 395 396 SearchControls searchControls = new SearchControls( ); 397 398 searchControls.setDerefLinkFlag( true ); 399 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 400 401 String filter = "objectClass=" + getLdapGroupClass( ); 402 403 namingEnumeration = context.search( groupNameAttribute + "=" + groupName + "," + getGroupsDn( ), filter, searchControls ); 404 405 return namingEnumeration.hasMore( ); 406 } 407 catch ( NameNotFoundException e ) 408 { 409 log.debug( "group {} for role {} not found", groupName, roleName ); 410 return false; 411 } 412 catch ( LdapException e ) 413 { 414 throw new MappingException( e.getMessage( ), e ); 415 } 416 catch ( NamingException e ) 417 { 418 throw new MappingException( e.getMessage( ), e ); 419 } 420 421 finally 422 { 423 close( namingEnumeration ); 424 } 425 } 426 427 public List<String> getAllRoles( DirContext context ) 428 throws MappingException 429 { 430 List<String> groups = getAllGroups( context ); 431 432 if ( groups.isEmpty( ) ) 433 { 434 return Collections.emptyList( ); 435 } 436 437 Set<String> roles = new HashSet<String>( groups.size( ) ); 438 439 Map<String, Collection<String>> mapping = ldapRoleMapperConfiguration.getLdapGroupMappings( ); 440 441 for ( String group : groups ) 442 { 443 Collection<String> rolesPerGroup = mapping.get( group ); 444 if ( rolesPerGroup != null ) 445 { 446 for ( String role : rolesPerGroup ) 447 { 448 roles.add( role ); 449 } 450 } 451 } 452 453 return new ArrayList<String>( roles ); 454 } 455 456 public List<String> getGroupsMember( String group, DirContext context ) 457 throws MappingException 458 { 459 460 NamingEnumeration<SearchResult> namingEnumeration = null; 461 try 462 { 463 464 SearchControls searchControls = new SearchControls( ); 465 466 searchControls.setDerefLinkFlag( true ); 467 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 468 469 String filter = "objectClass=" + getLdapGroupClass( ); 470 471 namingEnumeration = context.search( groupNameAttribute + "=" + group + "," + getGroupsDn( ), filter, searchControls ); 472 473 List<String> allMembers = new ArrayList<String>( ); 474 475 while ( namingEnumeration.hasMore( ) ) 476 { 477 SearchResult searchResult = namingEnumeration.next( ); 478 479 Attribute uniqueMemberAttr = searchResult.getAttributes( ).get( getLdapGroupMemberAttribute( ) ); 480 481 if ( uniqueMemberAttr != null ) 482 { 483 NamingEnumeration<?> allMembersEnum = uniqueMemberAttr.getAll( ); 484 while ( allMembersEnum.hasMore( ) ) 485 { 486 String userName = allMembersEnum.next( ).toString( ); 487 // uid=blabla we only want bla bla 488 userName = StringUtils.substringAfter( userName, "=" ); 489 userName = StringUtils.substringBefore( userName, "," ); 490 log.debug( "found userName for group {}: '{}", group, userName ); 491 492 allMembers.add( userName ); 493 } 494 close( allMembersEnum ); 495 } 496 497 498 } 499 500 return allMembers; 501 } 502 catch ( LdapException e ) 503 { 504 throw new MappingException( e.getMessage( ), e ); 505 } 506 catch ( NamingException e ) 507 { 508 throw new MappingException( e.getMessage( ), e ); 509 } 510 511 finally 512 { 513 close( namingEnumeration ); 514 } 515 } 516 517 /* 518 * TODO: Should use LDAP search, as this may not work for users in subtrees 519 */ 520 private String getUserDnFromId(String userKey) { 521 return new StringBuilder().append( this.userIdAttribute ).append( "=" ).append( userKey ).append( "," ).append( 522 getBaseDn( ) ).toString(); 523 } 524 525 public List<String> getGroups( String username, DirContext context ) 526 throws MappingException 527 { 528 529 Set<String> userGroups = new HashSet<String>( ); 530 531 NamingEnumeration<SearchResult> namingEnumeration = null; 532 try 533 { 534 535 SearchControls searchControls = new SearchControls( ); 536 537 searchControls.setDerefLinkFlag( true ); 538 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 539 540 541 String groupEntry = null; 542 try 543 { 544 //try to look the user up 545 User user = userManager.findUser( username ); 546 if ( user != null && user instanceof LdapUser ) 547 { 548 // TODO: This is some kind of memberOf retrieval, but will not work. Need to setup a memberOf Attribute 549 LdapUser ldapUser = (LdapUser) user ; 550 Attribute dnAttribute = ldapUser.getOriginalAttributes( ).get( getLdapDnAttribute( ) ); 551 if ( dnAttribute != null ) 552 { 553 groupEntry = dnAttribute.get( ).toString(); 554 } 555 556 } 557 } 558 catch ( UserNotFoundException e ) 559 { 560 log.warn( "Failed to look up user {}. Computing distinguished name manually", username, e ); 561 } 562 catch ( UserManagerException e ) 563 { 564 log.warn( "Failed to look up user {}. Computing distinguished name manually", username, e ); 565 } 566 if ( groupEntry == null ) 567 { 568 //failed to look up the user's groupEntry directly 569 570 if ( this.useDnAsMemberValue ) 571 { 572 groupEntry = getUserDnFromId( username ); 573 } 574 else 575 { 576 groupEntry = username; 577 } 578 } 579 580 String filter = 581 new StringBuilder( ).append( "(&" ).append( "(objectClass=" + getLdapGroupClass( ) + ")" ).append( 582 "(" ).append( getLdapGroupMemberAttribute( ) ).append( "=" ).append( Rdn.escapeValue( groupEntry ) ).append( ")" ).append( 583 ")" ).toString( ); 584 585 log.debug( "filter: {}", filter ); 586 587 namingEnumeration = context.search( getGroupsDn( ), filter, searchControls ); 588 589 while ( namingEnumeration.hasMore( ) ) 590 { 591 SearchResult groupSearchResult = namingEnumeration.next( ); 592 593 String groupName = getGroupNameFromResult( groupSearchResult ); 594 595 if (StringUtils.isNotEmpty( groupName )) { 596 userGroups.add( groupName ); 597 } 598 599 600 } 601 602 return new ArrayList( userGroups ); 603 } 604 catch ( LdapException e ) 605 { 606 throw new MappingException( e.getMessage( ), e ); 607 } 608 catch ( NamingException e ) 609 { 610 throw new MappingException( e.getMessage( ), e ); 611 } 612 finally 613 { 614 close( namingEnumeration ); 615 } 616 } 617 618 /* 619 * TODO: We should implement recursive group retrieval 620 * Need a configuration flag, to activate recursion 621 */ 622 @Override 623 public List<LdapGroup> getGroupObjects( String username, DirContext context ) throws MappingException 624 { 625 Set<LdapGroup> userGroups = new HashSet<>( ); 626 627 NamingEnumeration<SearchResult> namingEnumeration = null; 628 try 629 { 630 631 SearchControls searchControls = new SearchControls( ); 632 633 searchControls.setDerefLinkFlag( true ); 634 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 635 636 637 String userIdentifier = null; 638 String userDn = null; 639 try 640 { 641 //try to look the user up 642 User user = userManager.findUser( username ); 643 if ( user != null && user instanceof LdapUser ) 644 { 645 // TODO: This is some kind of memberOf retrieval, but will not work with DN. 646 // We need a configuration entry for the memberOf attribute and a flag, if this should be used 647 LdapUser ldapUser = (LdapUser) user ; 648 Attribute dnAttribute = ldapUser.getOriginalAttributes( ).get( getLdapDnAttribute( ) ); 649 if ( dnAttribute != null ) 650 { 651 userIdentifier = dnAttribute.get( ).toString(); 652 } 653 userDn = ldapUser.getDn( ); 654 655 } 656 } 657 catch ( UserNotFoundException e ) 658 { 659 log.warn( "Failed to look up user {}. Computing distinguished name manually", username, e ); 660 } 661 catch ( UserManagerException e ) 662 { 663 log.warn( "Failed to look up user {}. Computing distinguished name manually", username, e ); 664 } 665 if ( userIdentifier == null ) 666 { 667 //failed to look up the user's groupEntry directly 668 669 if ( this.useDnAsMemberValue ) 670 { 671 userIdentifier = userDn; 672 } 673 else 674 { 675 userIdentifier = username; 676 } 677 } 678 679 String filter = 680 new StringBuilder( ).append( "(&" ).append( "(objectClass=" + getLdapGroupClass( ) + ")" ).append( 681 "(" ).append( getLdapGroupMemberAttribute( ) ).append( "=" ).append( Rdn.escapeValue( userIdentifier ) ).append( ")" ).append( 682 ")" ).toString( ); 683 684 log.debug( "filter: {}", filter ); 685 686 namingEnumeration = context.search( getGroupsDn( ), filter, searchControls ); 687 688 while ( namingEnumeration.hasMore( ) ) 689 { 690 SearchResult groupSearchResult = namingEnumeration.next( ); 691 LdapGroup groupName = getGroupFromResult( groupSearchResult ); 692 userGroups.add( groupName ); 693 } 694 } 695 catch ( LdapException e ) 696 { 697 throw new MappingException( e.getMessage( ), e ); 698 } 699 catch ( NamingException e ) 700 { 701 throw new MappingException( e.getMessage( ), e ); 702 } 703 finally 704 { 705 close( namingEnumeration ); 706 } 707 return new ArrayList( userGroups ); 708 } 709 710 public List<String> getRoles( String username, DirContext context, Collection<String> realRoles ) 711 throws MappingException 712 { 713 List<String> groups = getGroups( username, context ); 714 715 Map<String, Collection<String>> rolesMapping = ldapRoleMapperConfiguration.getLdapGroupMappings( ); 716 717 Set<String> roles = new HashSet<String>( groups.size( ) ); 718 719 for ( String group : groups ) 720 { 721 Collection<String> rolesPerGroup = rolesMapping.get( group ); 722 if ( rolesPerGroup != null ) 723 { 724 roles.addAll( rolesPerGroup ); 725 } 726 else 727 { 728 if ( this.useDefaultRoleName && realRoles != null && realRoles.contains( group ) ) 729 { 730 roles.add( group ); 731 } 732 } 733 } 734 735 return new ArrayList<String>( roles ); 736 } 737 738 private void close( NamingEnumeration namingEnumeration ) 739 { 740 if ( namingEnumeration != null ) 741 { 742 try 743 { 744 namingEnumeration.close( ); 745 } 746 catch ( NamingException e ) 747 { 748 log.warn( "fail to close namingEnumeration: {}", e.getMessage( ) ); 749 } 750 } 751 } 752 753 public String getGroupsDn( ) 754 { 755 return this.groupsDn; 756 } 757 758 public String getLdapGroupClass( ) 759 { 760 return this.ldapGroupClass; 761 } 762 763 public String getLdapDnAttribute( ) 764 { 765 return this.dnAttr; 766 } 767 768 public boolean saveRole( String roleName, DirContext context ) 769 throws MappingException 770 { 771 772 if ( hasRole( context, roleName ) ) 773 { 774 return true; 775 } 776 777 String groupName = findGroupName( roleName ); 778 779 if ( groupName == null ) 780 { 781 if ( this.useDefaultRoleName ) 782 { 783 groupName = roleName; 784 } 785 else 786 { 787 log.warn( "skip group creation as no mapping for roleName:'{}'", roleName ); 788 return false; 789 } 790 } 791 792 List<String> allGroups = getAllGroups( context ); 793 if ( allGroups.contains( groupName ) ) 794 { 795 log.info( "group {} already exists for role.", groupName, roleName ); 796 return false; 797 } 798 799 Attributes attributes = new BasicAttributes( true ); 800 BasicAttribute objectClass = new BasicAttribute( "objectClass" ); 801 objectClass.add( "top" ); 802 objectClass.add( "groupOfUniqueNames" ); 803 attributes.put( objectClass ); 804 attributes.put( this.groupNameAttribute, groupName ); 805 806 // attribute mandatory when created a group so add admin as default member 807 BasicAttribute basicAttribute = new BasicAttribute( getLdapGroupMemberAttribute( ) ); 808 basicAttribute.add( this.userIdAttribute + "=admin," + getBaseDn( ) ); 809 attributes.put( basicAttribute ); 810 811 try 812 { 813 String dn = this.groupNameAttribute + "=" + groupName + "," + this.groupsDn; 814 815 context.createSubcontext( dn, attributes ); 816 817 log.info( "created group with dn:'{}", dn ); 818 819 return true; 820 } 821 catch ( NameAlreadyBoundException e ) 822 { 823 log.info( "skip group '{}' creation as already exists", groupName ); 824 return true; 825 } 826 catch ( LdapException e ) 827 { 828 throw new MappingException( e.getMessage( ), e ); 829 830 } 831 catch ( NamingException e ) 832 { 833 throw new MappingException( e.getMessage( ), e ); 834 } 835 } 836 837 public boolean saveUserRole( String roleName, String username, DirContext context ) 838 throws MappingException 839 { 840 841 String groupName = findGroupName( roleName ); 842 843 if ( groupName == null ) 844 { 845 log.warn( "no group found for role '{}", roleName ); 846 groupName = roleName; 847 } 848 849 NamingEnumeration<SearchResult> namingEnumeration = null; 850 try 851 { 852 SearchControls searchControls = new SearchControls( ); 853 854 searchControls.setDerefLinkFlag( true ); 855 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 856 857 String filter = "objectClass=" + getLdapGroupClass( ); 858 859 namingEnumeration = context.search( this.groupNameAttribute + "=" + groupName + "," + getGroupsDn( ), filter, searchControls ); 860 861 if ( namingEnumeration.hasMore( ) ) 862 { 863 SearchResult searchResult = namingEnumeration.next( ); 864 Attribute attribute = searchResult.getAttributes( ).get( getLdapGroupMemberAttribute( ) ); 865 if ( attribute == null ) 866 { 867 BasicAttribute basicAttribute = new BasicAttribute( getLdapGroupMemberAttribute( ) ); 868 basicAttribute.add( this.userIdAttribute + "=" + username + "," + getBaseDn( ) ); 869 context.modifyAttributes( this.groupNameAttribute + "=" + groupName + "," + getGroupsDn( ), new ModificationItem[]{ 870 new ModificationItem( DirContext.ADD_ATTRIBUTE, basicAttribute )} ); 871 } 872 else 873 { 874 attribute.add( this.userIdAttribute + "=" + username + "," + getBaseDn( ) ); 875 context.modifyAttributes( this.groupNameAttribute + "=" + groupName + "," + getGroupsDn( ), new ModificationItem[]{ 876 new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attribute )} ); 877 } 878 return true; 879 } 880 881 return false; 882 } 883 catch ( LdapException e ) 884 { 885 throw new MappingException( e.getMessage( ), e ); 886 } 887 catch ( NamingException e ) 888 { 889 throw new MappingException( e.getMessage( ), e ); 890 } 891 892 finally 893 { 894 if ( namingEnumeration != null ) 895 { 896 try 897 { 898 namingEnumeration.close( ); 899 } 900 catch ( NamingException e ) 901 { 902 log.warn( "failed to close search results", e ); 903 } 904 } 905 } 906 } 907 908 public boolean removeUserRole( String roleName, String username, DirContext context ) 909 throws MappingException 910 { 911 String groupName = findGroupName( roleName ); 912 913 if ( groupName == null ) 914 { 915 log.warn( "no group found for role '{}", roleName ); 916 return false; 917 } 918 919 NamingEnumeration<SearchResult> namingEnumeration = null; 920 try 921 { 922 923 SearchControls searchControls = new SearchControls( ); 924 925 searchControls.setDerefLinkFlag( true ); 926 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 927 928 String filter = "objectClass=" + getLdapGroupClass( ); 929 930 namingEnumeration = context.search( groupNameAttribute + "=" + groupName + "," + getGroupsDn( ), filter, searchControls ); 931 932 if ( namingEnumeration.hasMore( ) ) 933 { 934 SearchResult searchResult = namingEnumeration.next( ); 935 Attribute attribute = searchResult.getAttributes( ).get( getLdapGroupMemberAttribute( ) ); 936 if ( attribute != null ) 937 { 938 BasicAttribute basicAttribute = new BasicAttribute( getLdapGroupMemberAttribute( ) ); 939 basicAttribute.add( this.userIdAttribute + "=" + username + "," + getGroupsDn( ) ); 940 context.modifyAttributes( groupNameAttribute + "=" + groupName + "," + getGroupsDn( ), new ModificationItem[]{ 941 new ModificationItem( DirContext.REMOVE_ATTRIBUTE, basicAttribute )} ); 942 } 943 return true; 944 } 945 946 return false; 947 } 948 catch ( LdapException e ) 949 { 950 throw new MappingException( e.getMessage( ), e ); 951 } 952 catch ( NamingException e ) 953 { 954 throw new MappingException( e.getMessage( ), e ); 955 } 956 957 finally 958 { 959 if ( namingEnumeration != null ) 960 { 961 try 962 { 963 namingEnumeration.close( ); 964 } 965 catch ( NamingException e ) 966 { 967 log.warn( "failed to close search results", e ); 968 } 969 } 970 } 971 } 972 973 974 975 public void removeAllRoles( DirContext context ) 976 throws MappingException 977 { 978 //all mapped roles 979 Collection<String> groups = ldapRoleMapperConfiguration.getLdapGroupMappings( ).keySet( ); 980 981 try 982 { 983 for ( String groupName : groups ) 984 { 985 removeGroupByName( context, groupName ); 986 } 987 988 } 989 catch ( LdapException e ) 990 { 991 throw new MappingException( e.getMessage( ), e ); 992 993 } 994 catch ( NamingException e ) 995 { 996 throw new MappingException( e.getMessage( ), e ); 997 } 998 } 999 1000 private void removeGroupByName( DirContext context, String groupName ) throws NamingException 1001 { 1002 NamingEnumeration<SearchResult> namingEnumeration = null; 1003 try 1004 { 1005 SearchControls searchControls = new SearchControls( ); 1006 1007 searchControls.setDerefLinkFlag( true ); 1008 searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE ); 1009 String filter = "(&(objectClass=" + getLdapGroupClass( ) + ")(" + groupNameAttribute + "=" + Rdn.escapeValue( groupName ) + "))"; 1010 // String filter = "(&(objectClass=" + getLdapGroupClass( ) + "))"; 1011 namingEnumeration = context.search( getGroupsDn( ), filter, searchControls ); 1012 1013 // We delete only the first found group 1014 if ( namingEnumeration != null && namingEnumeration.hasMore( ) ) 1015 { 1016 SearchResult result = namingEnumeration.next( ); 1017 String dn = result.getNameInNamespace( ); 1018 context.unbind( new LdapName( dn ) ); 1019 log.debug( "Deleted group with dn:'{}", dn ); 1020 } 1021 } 1022 finally 1023 { 1024 closeNamingEnumeration( namingEnumeration ); 1025 } 1026 } 1027 1028 public void removeRole( String roleName, DirContext context ) 1029 throws MappingException 1030 { 1031 1032 String groupName = findGroupName( roleName ); 1033 if (StringUtils.isEmpty( groupName )) { 1034 log.warn( "No group for the given role found: role={}", roleName ); 1035 return; 1036 } 1037 try 1038 { 1039 1040 removeGroupByName( context, groupName ); 1041 1042 } 1043 catch ( LdapException e ) 1044 { 1045 throw new MappingException( e.getMessage( ), e ); 1046 1047 } 1048 catch ( NamingException e ) 1049 { 1050 throw new MappingException( e.getMessage( ), e ); 1051 } 1052 } 1053 1054 //------------------------------------ 1055 // Mapping part 1056 //------------------------------------ 1057 1058 //--------------------------------- 1059 // setters for unit tests 1060 //--------------------------------- 1061 1062 1063 public void setGroupsDn( String groupsDn ) 1064 { 1065 this.groupsDn = groupsDn; 1066 } 1067 1068 public void setLdapGroupClass( String ldapGroupClass ) 1069 { 1070 this.ldapGroupClass = ldapGroupClass; 1071 } 1072 1073 public void setUserConf( UserConfiguration userConf ) 1074 { 1075 this.userConf = userConf; 1076 } 1077 1078 public void setLdapConnectionFactory( LdapConnectionFactory ldapConnectionFactory ) 1079 { 1080 this.ldapConnectionFactory = ldapConnectionFactory; 1081 } 1082 1083 public String getBaseDn( ) 1084 { 1085 return baseDn; 1086 } 1087 1088 public void setBaseDn( String baseDn ) 1089 { 1090 this.baseDn = baseDn; 1091 } 1092 1093 public String getLdapGroupMemberAttribute( ) 1094 { 1095 return ldapGroupMemberAttribute; 1096 } 1097 1098 public void setLdapGroupMemberAttribute( String ldapGroupMemberAttribute ) 1099 { 1100 this.ldapGroupMemberAttribute = ldapGroupMemberAttribute; 1101 } 1102 1103 //------------------- 1104 // utils methods 1105 //------------------- 1106 1107 protected String findGroupName( String role ) 1108 throws MappingException 1109 { 1110 Map<String, Collection<String>> mapping = ldapRoleMapperConfiguration.getLdapGroupMappings( ); 1111 1112 for ( Map.Entry<String, Collection<String>> entry : mapping.entrySet( ) ) 1113 { 1114 if ( entry.getValue( ).contains( role ) ) 1115 { 1116 return entry.getKey( ); 1117 } 1118 } 1119 return null; 1120 } 1121 1122 1123 public String getUserIdAttribute( ) 1124 { 1125 return userIdAttribute; 1126 } 1127 1128 public void setUserIdAttribute( String userIdAttribute ) 1129 { 1130 this.userIdAttribute = userIdAttribute; 1131 } 1132 1133 public boolean isUseDefaultRoleName( ) 1134 { 1135 return useDefaultRoleName; 1136 } 1137 1138 public void setUseDefaultRoleName( boolean useDefaultRoleName ) 1139 { 1140 this.useDefaultRoleName = useDefaultRoleName; 1141 } 1142}