]> source.dussan.org Git - archiva.git/blob
bfa828ef0e43d9cd9d6ab0c0a407fd6276a3926b
[archiva.git] /
1 package org.apache.archiva.redback.common.ldap.role;
2 /*
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
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
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
18  * under the License.
19  */
20
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;
32
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;
52 import java.util.Map;
53
54 /**
55  * @author Olivier Lamy
56  * @since 2.1
57  */
58 @Service( "ldapRoleMapper#default" )
59 public class DefaultLdapRoleMapper
60     implements LdapRoleMapper
61 {
62
63     private Logger log = LoggerFactory.getLogger( getClass() );
64
65     @Inject
66     private LdapConnectionFactory ldapConnectionFactory;
67
68     @Inject
69     @Named( value = "userConfiguration#default" )
70     private UserConfiguration userConf;
71
72     //---------------------------
73     // fields
74     //---------------------------
75
76     private String ldapGroupClass = "groupOfUniqueNames";
77
78     private String groupsDn;
79
80     private String baseDn;
81
82     @PostConstruct
83     public void initialize()
84     {
85         this.ldapGroupClass = userConf.getString( UserConfigurationKeys.LDAP_GROUPS_CLASS, this.ldapGroupClass );
86
87         this.baseDn = userConf.getConcatenatedList( UserConfigurationKeys.LDAP_BASEDN, this.baseDn );
88
89         this.groupsDn = userConf.getConcatenatedList( UserConfigurationKeys.LDAP_GROUPS_BASEDN, this.groupsDn );
90
91         if ( StringUtils.isEmpty( this.groupsDn ) )
92         {
93             this.groupsDn = this.baseDn;
94         }
95     }
96
97     public String getLdapGroup( String role )
98     {
99         return userConf.getString( UserConfigurationKeys.LDAP_GROUPS_ROLE_START_KEY + role );
100     }
101
102     public List<String> getAllGroups( DirContext context )
103         throws MappingException
104     {
105
106         NamingEnumeration<SearchResult> namingEnumeration = null;
107         try
108         {
109
110             SearchControls searchControls = new SearchControls();
111
112             searchControls.setDerefLinkFlag( true );
113             searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
114
115             String filter = "objectClass=" + getLdapGroupClass();
116
117             namingEnumeration = context.search( getGroupsDn(), filter, searchControls );
118
119             List<String> allGroups = new ArrayList<String>();
120
121             while ( namingEnumeration.hasMore() )
122             {
123                 SearchResult searchResult = namingEnumeration.next();
124
125                 String groupName = searchResult.getName();
126                 // cn=blabla we only want bla bla
127                 groupName = StringUtils.substringAfter( groupName, "=" );
128
129                 log.debug( "found groupName: '{}", groupName );
130
131                 allGroups.add( groupName );
132
133             }
134
135             return allGroups;
136         }
137         catch ( LdapException e )
138         {
139             throw new MappingException( e.getMessage(), e );
140         }
141         catch ( NamingException e )
142         {
143             throw new MappingException( e.getMessage(), e );
144         }
145
146         finally
147         {
148             close( namingEnumeration );
149         }
150     }
151
152     protected void closeNamingEnumeration( NamingEnumeration namingEnumeration )
153     {
154         if ( namingEnumeration != null )
155         {
156             try
157             {
158                 namingEnumeration.close();
159             }
160             catch ( NamingException e )
161             {
162                 log.warn( "failed to close NamingEnumeration", e );
163             }
164         }
165     }
166
167     public List<String> getAllRoles( DirContext context )
168         throws MappingException
169     {
170         List<String> groups = getAllGroups( context );
171
172         if ( groups.isEmpty() )
173         {
174             return Collections.emptyList();
175         }
176
177         List<String> roles = new ArrayList<String>( groups.size() );
178
179         Map<String, String> mapping = getLdapGroupMappings();
180
181         for ( String group : groups )
182         {
183             String role = mapping.get( group );
184             if ( role != null )
185             {
186                 roles.add( role );
187             }
188         }
189
190         return roles;
191     }
192
193     public List<String> getGroupsMember( String group, DirContext context )
194         throws MappingException
195     {
196
197         NamingEnumeration<SearchResult> namingEnumeration = null;
198         try
199         {
200
201             SearchControls searchControls = new SearchControls();
202
203             searchControls.setDerefLinkFlag( true );
204             searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
205
206             String filter = "objectClass=" + getLdapGroupClass();
207
208             namingEnumeration = context.search( "cn=" + group + "," + getGroupsDn(), filter, searchControls );
209
210             List<String> allMembers = new ArrayList<String>();
211
212             while ( namingEnumeration.hasMore() )
213             {
214                 SearchResult searchResult = namingEnumeration.next();
215
216                 Attribute uniqueMemberAttr = searchResult.getAttributes().get( "uniquemember" );
217
218                 if ( uniqueMemberAttr != null )
219                 {
220                     NamingEnumeration<String> allMembersEnum = (NamingEnumeration<String>) uniqueMemberAttr.getAll();
221                     while ( allMembersEnum.hasMore() )
222                     {
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 );
228
229                         allMembers.add( userName );
230                     }
231                     close( allMembersEnum );
232                 }
233
234
235             }
236
237             return allMembers;
238         }
239         catch ( LdapException e )
240         {
241             throw new MappingException( e.getMessage(), e );
242         }
243         catch ( NamingException e )
244         {
245             throw new MappingException( e.getMessage(), e );
246         }
247
248         finally
249         {
250             close( namingEnumeration );
251         }
252     }
253
254     public List<String> getGroups( String username, DirContext context )
255         throws MappingException
256     {
257
258         List<String> userGroups = new ArrayList<String>();
259
260         NamingEnumeration<SearchResult> namingEnumeration = null;
261         try
262         {
263
264             SearchControls searchControls = new SearchControls();
265
266             searchControls.setDerefLinkFlag( true );
267             searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
268
269             String filter =
270                 new StringBuilder().append( "(&" ).append( "(objectClass=" + getLdapGroupClass() + ")" ).append(
271                     "(uniquemember=" ).append( "uid=" + username + "," + this.getBaseDn() ).append( ")" ).append(
272                     ")" ).toString();
273
274             log.debug( "filter: {}", filter );
275
276             namingEnumeration = context.search( getGroupsDn(), filter, searchControls );
277
278             while ( namingEnumeration.hasMore() )
279             {
280                 SearchResult searchResult = namingEnumeration.next();
281
282                 List<String> allMembers = new ArrayList<String>();
283
284                 Attribute uniqueMemberAttr = searchResult.getAttributes().get( "uniquemember" );
285
286                 if ( uniqueMemberAttr != null )
287                 {
288                     NamingEnumeration<String> allMembersEnum = (NamingEnumeration<String>) uniqueMemberAttr.getAll();
289                     while ( allMembersEnum.hasMore() )
290                     {
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 );
296                     }
297                     close( allMembersEnum );
298                 }
299
300                 if ( allMembers.contains( username ) )
301                 {
302                     String groupName = searchResult.getName();
303                     // cn=blabla we only want bla bla
304                     groupName = StringUtils.substringAfter( groupName, "=" );
305                     userGroups.add( groupName );
306
307                 }
308
309
310             }
311
312             return userGroups;
313         }
314         catch ( LdapException e )
315         {
316             throw new MappingException( e.getMessage(), e );
317         }
318         catch ( NamingException e )
319         {
320             throw new MappingException( e.getMessage(), e );
321         }
322         finally
323         {
324             close( namingEnumeration );
325         }
326     }
327
328     public List<String> getRoles( String username, DirContext context )
329         throws MappingException
330     {
331         List<String> groups = getGroups( username, context );
332
333         Map<String, String> rolesMapping = getLdapGroupMappings();
334
335         List<String> roles = new ArrayList<String>( groups.size() );
336
337         for ( String group : groups )
338         {
339             String role = rolesMapping.get( group );
340             if ( role != null )
341             {
342                 roles.add( role );
343             }
344         }
345
346         return roles;
347     }
348
349     private void close( NamingEnumeration namingEnumeration )
350     {
351         if ( namingEnumeration != null )
352         {
353             try
354             {
355                 namingEnumeration.close();
356             }
357             catch ( NamingException e )
358             {
359                 log.warn( "fail to close namingEnumeration: {}", e.getMessage() );
360             }
361         }
362     }
363
364     public String getGroupsDn()
365     {
366         return this.groupsDn;
367     }
368
369     public String getLdapGroupClass()
370     {
371         return this.ldapGroupClass;
372     }
373
374     public void addLdapMapping( String role, String ldapGroup )
375     {
376         log.warn( "addLdapMapping not implemented" );
377     }
378
379     public void removeLdapMapping( String role )
380     {
381         log.warn( "removeLdapMapping not implemented" );
382     }
383
384     public void setLdapGroupMappings( Map<String, String> mappings )
385         throws MappingException
386     {
387         log.warn( "setLdapGroupMappings not implemented" );
388     }
389
390     public Map<String, String> getLdapGroupMappings()
391     {
392         Map<String, String> map = new HashMap<String, String>();
393
394         Collection<String> keys = userConf.getKeys();
395
396         for ( String key : keys )
397         {
398             if ( key.startsWith( UserConfigurationKeys.LDAP_GROUPS_ROLE_START_KEY ) )
399             {
400                 map.put( StringUtils.substringAfter( key, UserConfigurationKeys.LDAP_GROUPS_ROLE_START_KEY ),
401                          userConf.getString( key ) );
402             }
403         }
404
405         return map;
406     }
407
408     public boolean saveRole( String roleName, DirContext context )
409         throws MappingException
410     {
411
412         String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
413         if ( groupName == null )
414         {
415             log.warn( "skip group creation as no mapping fro roleName:'{}'", roleName );
416             return false;
417         }
418
419         List<String> allGroups = getAllGroups( context );
420         if ( allGroups.contains( groupName ) )
421         {
422             log.info( "group {} already exists for role.", groupName, roleName );
423             return false;
424         }
425
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 );
432
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 );
438
439         try
440         {
441             String dn = "cn=" + groupName + "," + this.groupsDn;
442
443             context.createSubcontext( dn, attributes );
444
445             log.info( "created group with dn:'{}", dn );
446
447             return true;
448         }
449         catch ( LdapException e )
450         {
451             throw new MappingException( e.getMessage(), e );
452
453         }
454         catch ( NamingException e )
455         {
456             throw new MappingException( e.getMessage(), e );
457         }
458     }
459
460     public boolean saveUserRole( String roleName, String username, DirContext context )
461         throws MappingException
462     {
463
464         String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
465
466         if ( groupName == null )
467         {
468             log.warn( "no group found for role '{}", roleName );
469             return false;
470         }
471
472         NamingEnumeration<SearchResult> namingEnumeration = null;
473         try
474         {
475             SearchControls searchControls = new SearchControls();
476
477             searchControls.setDerefLinkFlag( true );
478             searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
479
480             String filter = "objectClass=" + getLdapGroupClass();
481
482             namingEnumeration = context.search( "cn=" + groupName + "," + getGroupsDn(), filter, searchControls );
483
484             while ( namingEnumeration.hasMore() )
485             {
486                 SearchResult searchResult = namingEnumeration.next();
487                 Attribute attribute = searchResult.getAttributes().get( "uniquemember" );
488                 if ( attribute == null )
489                 {
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 ) } );
494                 }
495                 else
496                 {
497                     attribute.add( "uid=" + username + "," + getGroupsDn() );
498                     context.modifyAttributes( "cn=" + groupName + "," + getGroupsDn(), new ModificationItem[]{
499                         new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attribute ) } );
500                 }
501                 return true;
502             }
503
504             return false;
505         }
506         catch ( LdapException e )
507         {
508             throw new MappingException( e.getMessage(), e );
509         }
510         catch ( NamingException e )
511         {
512             throw new MappingException( e.getMessage(), e );
513         }
514
515         finally
516         {
517             if ( namingEnumeration != null )
518             {
519                 try
520                 {
521                     namingEnumeration.close();
522                 }
523                 catch ( NamingException e )
524                 {
525                     log.warn( "failed to close search results", e );
526                 }
527             }
528         }
529     }
530
531     public boolean removeUserRole( String roleName, String username, DirContext context )
532         throws MappingException
533     {
534         String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
535
536         if ( groupName == null )
537         {
538             log.warn( "no group found for role '{}", roleName );
539             return false;
540         }
541
542         NamingEnumeration<SearchResult> namingEnumeration = null;
543         try
544         {
545
546             SearchControls searchControls = new SearchControls();
547
548             searchControls.setDerefLinkFlag( true );
549             searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
550
551             String filter = "objectClass=" + getLdapGroupClass();
552
553             namingEnumeration = context.search( "cn=" + groupName + "," + getGroupsDn(), filter, searchControls );
554
555             while ( namingEnumeration.hasMore() )
556             {
557                 SearchResult searchResult = namingEnumeration.next();
558                 Attribute attribute = searchResult.getAttributes().get( "uniquemember" );
559                 if ( attribute != null )
560                 {
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 ) } );
565                 }
566                 return true;
567             }
568
569             return false;
570         }
571         catch ( LdapException e )
572         {
573             throw new MappingException( e.getMessage(), e );
574         }
575         catch ( NamingException e )
576         {
577             throw new MappingException( e.getMessage(), e );
578         }
579
580         finally
581         {
582             if ( namingEnumeration != null )
583             {
584                 try
585                 {
586                     namingEnumeration.close();
587                 }
588                 catch ( NamingException e )
589                 {
590                     log.warn( "failed to close search results", e );
591                 }
592             }
593         }
594     }
595
596     public void removeAllRoles( DirContext context )
597         throws MappingException
598     {
599         //all mapped roles
600         Collection<String> groups = getLdapGroupMappings().keySet();
601
602         try
603         {
604             for ( String groupName : groups )
605             {
606
607                 String dn = "cn=" + groupName + "," + this.groupsDn;
608
609                 context.unbind( dn );
610
611                 log.debug( "deleted group with dn:'{}", dn );
612             }
613
614         }
615         catch ( LdapException e )
616         {
617             throw new MappingException( e.getMessage(), e );
618
619         }
620         catch ( NamingException e )
621         {
622             throw new MappingException( e.getMessage(), e );
623         }
624     }
625
626     public void removeRole( String roleName, DirContext context )
627         throws MappingException
628     {
629
630         String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
631
632         try
633         {
634
635             String dn = "cn=" + groupName + "," + this.groupsDn;
636
637             context.unbind( dn );
638
639             log.info( "deleted group with dn:'{}", dn );
640
641         }
642         catch ( LdapException e )
643         {
644             throw new MappingException( e.getMessage(), e );
645
646         }
647         catch ( NamingException e )
648         {
649             throw new MappingException( e.getMessage(), e );
650         }
651     }
652
653     //---------------------------------
654     // setters for unit tests
655     //---------------------------------
656
657
658     public void setGroupsDn( String groupsDn )
659     {
660         this.groupsDn = groupsDn;
661     }
662
663     public void setLdapGroupClass( String ldapGroupClass )
664     {
665         this.ldapGroupClass = ldapGroupClass;
666     }
667
668     public void setUserConf( UserConfiguration userConf )
669     {
670         this.userConf = userConf;
671     }
672
673     public void setLdapConnectionFactory( LdapConnectionFactory ldapConnectionFactory )
674     {
675         this.ldapConnectionFactory = ldapConnectionFactory;
676     }
677
678     public String getBaseDn()
679     {
680         return baseDn;
681     }
682
683     public void setBaseDn( String baseDn )
684     {
685         this.baseDn = baseDn;
686     }
687 }