]> source.dussan.org Git - archiva.git/blob
e4af79a46cb430e8f24f93e56eb69eaaac5eee04
[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()
103         throws MappingException
104     {
105         LdapConnection ldapConnection = null;
106
107         NamingEnumeration<SearchResult> namingEnumeration = null;
108         try
109         {
110             ldapConnection = ldapConnectionFactory.getConnection();
111
112             DirContext context = ldapConnection.getDirContext();
113
114             SearchControls searchControls = new SearchControls();
115
116             searchControls.setDerefLinkFlag( true );
117             searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
118
119             String filter = "objectClass=" + getLdapGroupClass();
120
121             namingEnumeration = context.search( getGroupsDn(), filter, searchControls );
122
123             List<String> allGroups = new ArrayList<String>();
124
125             while ( namingEnumeration.hasMore() )
126             {
127                 SearchResult searchResult = namingEnumeration.next();
128
129                 String groupName = searchResult.getName();
130                 // cn=blabla we only want bla bla
131                 groupName = StringUtils.substringAfter( groupName, "=" );
132
133                 log.debug( "found groupName: '{}", groupName );
134
135                 allGroups.add( groupName );
136
137             }
138
139             return allGroups;
140         }
141         catch ( LdapException e )
142         {
143             throw new MappingException( e.getMessage(), e );
144         }
145         catch ( NamingException e )
146         {
147             throw new MappingException( e.getMessage(), e );
148         }
149
150         finally
151         {
152             if ( ldapConnection != null )
153             {
154                 ldapConnection.close();
155             }
156             if ( namingEnumeration != null )
157             {
158                 try
159                 {
160                     namingEnumeration.close();
161                 }
162                 catch ( NamingException e )
163                 {
164                     log.warn( "failed to close search results", e );
165                 }
166             }
167         }
168     }
169
170     public List<String> getAllRoles()
171         throws MappingException
172     {
173         // TODO read from ldap ?
174         List<String> groups = getAllGroups();
175
176         if ( groups.isEmpty() )
177         {
178             return Collections.emptyList();
179         }
180
181         List<String> roles = new ArrayList<String>( groups.size() );
182
183         Map<String, String> mapping = getLdapGroupMappings();
184
185         for ( String group : groups )
186         {
187             String role = mapping.get( group );
188             if ( role != null )
189             {
190                 roles.add( role );
191             }
192         }
193
194         return roles;
195     }
196
197     public List<String> getGroupsMember( String group )
198         throws MappingException
199     {
200         LdapConnection ldapConnection = null;
201
202         NamingEnumeration<SearchResult> namingEnumeration = null;
203         try
204         {
205             ldapConnection = ldapConnectionFactory.getConnection();
206
207             DirContext context = ldapConnection.getDirContext();
208
209             SearchControls searchControls = new SearchControls();
210
211             searchControls.setDerefLinkFlag( true );
212             searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
213
214             String filter = "objectClass=" + getLdapGroupClass();
215
216             namingEnumeration = context.search( "cn=" + group + "," + getGroupsDn(), filter, searchControls );
217
218             List<String> allMembers = new ArrayList<String>();
219
220             while ( namingEnumeration.hasMore() )
221             {
222                 SearchResult searchResult = namingEnumeration.next();
223
224                 Attribute uniqueMemberAttr = searchResult.getAttributes().get( "uniquemember" );
225
226                 if ( uniqueMemberAttr != null )
227                 {
228                     NamingEnumeration<String> allMembersEnum = (NamingEnumeration<String>) uniqueMemberAttr.getAll();
229                     while ( allMembersEnum.hasMore() )
230                     {
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 );
236
237                         allMembers.add( userName );
238                     }
239                     close( allMembersEnum );
240                 }
241
242
243             }
244
245             return allMembers;
246         }
247         catch ( LdapException e )
248         {
249             throw new MappingException( e.getMessage(), e );
250         }
251         catch ( NamingException e )
252         {
253             throw new MappingException( e.getMessage(), e );
254         }
255
256         finally
257         {
258             if ( ldapConnection != null )
259             {
260                 ldapConnection.close();
261             }
262             close( namingEnumeration );
263         }
264     }
265
266     public List<String> getGroups( String username )
267         throws MappingException
268     {
269
270         List<String> userGroups = new ArrayList<String>();
271
272         LdapConnection ldapConnection = null;
273
274         NamingEnumeration<SearchResult> namingEnumeration = null;
275         try
276         {
277             ldapConnection = ldapConnectionFactory.getConnection();
278
279             DirContext context = ldapConnection.getDirContext();
280
281             SearchControls searchControls = new SearchControls();
282
283             searchControls.setDerefLinkFlag( true );
284             searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
285
286             String filter =
287                 new StringBuilder().append( "(&" ).append( "(objectClass=" + getLdapGroupClass() + ")" ).append(
288                     "(uniquemember=" ).append( "uid=" + username + "," + this.getBaseDn() ).append( ")" ).append(
289                     ")" ).toString();
290
291             log.debug( "filter: {}", filter );
292
293             namingEnumeration = context.search( getGroupsDn(), filter, searchControls );
294
295             while ( namingEnumeration.hasMore() )
296             {
297                 SearchResult searchResult = namingEnumeration.next();
298
299                 List<String> allMembers = new ArrayList<String>();
300
301                 Attribute uniqueMemberAttr = searchResult.getAttributes().get( "uniquemember" );
302
303                 if ( uniqueMemberAttr != null )
304                 {
305                     NamingEnumeration<String> allMembersEnum = (NamingEnumeration<String>) uniqueMemberAttr.getAll();
306                     while ( allMembersEnum.hasMore() )
307                     {
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 );
313                     }
314                     close( allMembersEnum );
315                 }
316
317                 if ( allMembers.contains( username ) )
318                 {
319                     String groupName = searchResult.getName();
320                     // cn=blabla we only want bla bla
321                     groupName = StringUtils.substringAfter( groupName, "=" );
322                     userGroups.add( groupName );
323
324                 }
325
326
327             }
328
329             return userGroups;
330         }
331         catch ( LdapException e )
332         {
333             throw new MappingException( e.getMessage(), e );
334         }
335         catch ( NamingException e )
336         {
337             throw new MappingException( e.getMessage(), e );
338         }
339
340         finally
341         {
342             if ( ldapConnection != null )
343             {
344                 ldapConnection.close();
345             }
346             close( namingEnumeration );
347         }
348     }
349
350     public List<String> getRoles( String username )
351         throws MappingException
352     {
353         List<String> groups = getGroups( username );
354
355         Map<String, String> rolesMapping = getLdapGroupMappings();
356
357         List<String> roles = new ArrayList<String>( groups.size() );
358
359         for ( String group : groups )
360         {
361             String role = rolesMapping.get( group );
362             if ( role != null )
363             {
364                 roles.add( role );
365             }
366         }
367
368         return roles;
369     }
370
371     private void close( NamingEnumeration namingEnumeration )
372     {
373         if ( namingEnumeration != null )
374         {
375             try
376             {
377                 namingEnumeration.close();
378             }
379             catch ( NamingException e )
380             {
381                 log.warn( "fail to close namingEnumeration: {}", e.getMessage() );
382             }
383         }
384     }
385
386     public String getGroupsDn()
387     {
388         return this.groupsDn;
389     }
390
391     public String getLdapGroupClass()
392     {
393         return this.ldapGroupClass;
394     }
395
396     public void addLdapMapping( String role, String ldapGroup )
397     {
398         log.warn( "addLdapMapping not implemented" );
399     }
400
401     public void removeLdapMapping( String role )
402     {
403         log.warn( "removeLdapMapping not implemented" );
404     }
405
406     public void setLdapGroupMappings( Map<String, String> mappings )
407         throws MappingException
408     {
409         log.warn( "setLdapGroupMappings not implemented" );
410     }
411
412     public Map<String, String> getLdapGroupMappings()
413     {
414         Map<String, String> map = new HashMap<String, String>();
415
416         Collection<String> keys = userConf.getKeys();
417
418         for ( String key : keys )
419         {
420             if ( key.startsWith( UserConfigurationKeys.LDAP_GROUPS_ROLE_START_KEY ) )
421             {
422                 map.put( StringUtils.substringAfter( key, UserConfigurationKeys.LDAP_GROUPS_ROLE_START_KEY ),
423                          userConf.getString( key ) );
424             }
425         }
426
427         return map;
428     }
429
430     public boolean saveRole( String roleName )
431         throws MappingException
432     {
433
434         String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
435         if ( groupName == null )
436         {
437             log.warn( "skip group creation as no mapping fro roleName:'{}'", roleName );
438             return false;
439         }
440
441         List<String> allGroups = getAllGroups();
442         if ( allGroups.contains( groupName ) )
443         {
444             log.info( "group {} already exists for role.", groupName, roleName );
445             return false;
446         }
447
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 );
454
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 );
460
461         LdapConnection ldapConnection = null;
462
463         try
464         {
465             ldapConnection = ldapConnectionFactory.getConnection();
466
467             DirContext context = ldapConnection.getDirContext();
468
469             String dn = "cn=" + groupName + "," + this.groupsDn;
470
471             context.createSubcontext( dn, attributes );
472
473             log.info( "created group with dn:'{}", dn );
474
475             return true;
476         }
477         catch ( LdapException e )
478         {
479             throw new MappingException( e.getMessage(), e );
480
481         }
482         catch ( NamingException e )
483         {
484             throw new MappingException( e.getMessage(), e );
485         }
486         finally
487         {
488             if ( ldapConnection != null )
489             {
490                 ldapConnection.close();
491             }
492         }
493     }
494
495     public boolean saveUserRole( String roleName, String username )
496         throws MappingException
497     {
498
499         String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
500
501         if ( groupName == null )
502         {
503             log.warn( "no group found for role '{}", roleName );
504             return false;
505         }
506
507         LdapConnection ldapConnection = null;
508
509         NamingEnumeration<SearchResult> namingEnumeration = null;
510         try
511         {
512             ldapConnection = ldapConnectionFactory.getConnection();
513
514             DirContext context = ldapConnection.getDirContext();
515
516             SearchControls searchControls = new SearchControls();
517
518             searchControls.setDerefLinkFlag( true );
519             searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
520
521             String filter = "objectClass=" + getLdapGroupClass();
522
523             namingEnumeration = context.search( "cn=" + groupName + "," + getGroupsDn(), filter, searchControls );
524
525             while ( namingEnumeration.hasMore() )
526             {
527                 SearchResult searchResult = namingEnumeration.next();
528                 Attribute attribute = searchResult.getAttributes().get( "uniquemember" );
529                 if ( attribute == null )
530                 {
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 ) } );
535                 }
536                 else
537                 {
538                     attribute.add( "uid=" + username + "," + getGroupsDn() );
539                     context.modifyAttributes( "cn=" + groupName + "," + getGroupsDn(), new ModificationItem[]{
540                         new ModificationItem( DirContext.REPLACE_ATTRIBUTE, attribute ) } );
541                 }
542                 return true;
543             }
544
545             return false;
546         }
547         catch ( LdapException e )
548         {
549             throw new MappingException( e.getMessage(), e );
550         }
551         catch ( NamingException e )
552         {
553             throw new MappingException( e.getMessage(), e );
554         }
555
556         finally
557         {
558             if ( ldapConnection != null )
559             {
560                 ldapConnection.close();
561             }
562             if ( namingEnumeration != null )
563             {
564                 try
565                 {
566                     namingEnumeration.close();
567                 }
568                 catch ( NamingException e )
569                 {
570                     log.warn( "failed to close search results", e );
571                 }
572             }
573         }
574     }
575
576     public boolean removeUserRole( String roleName, String username )
577         throws MappingException
578     {
579         String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
580
581         if ( groupName == null )
582         {
583             log.warn( "no group found for role '{}", roleName );
584             return false;
585         }
586
587         LdapConnection ldapConnection = null;
588
589         NamingEnumeration<SearchResult> namingEnumeration = null;
590         try
591         {
592             ldapConnection = ldapConnectionFactory.getConnection();
593
594             DirContext context = ldapConnection.getDirContext();
595
596             SearchControls searchControls = new SearchControls();
597
598             searchControls.setDerefLinkFlag( true );
599             searchControls.setSearchScope( SearchControls.SUBTREE_SCOPE );
600
601             String filter = "objectClass=" + getLdapGroupClass();
602
603             namingEnumeration = context.search( "cn=" + groupName + "," + getGroupsDn(), filter, searchControls );
604
605             while ( namingEnumeration.hasMore() )
606             {
607                 SearchResult searchResult = namingEnumeration.next();
608                 Attribute attribute = searchResult.getAttributes().get( "uniquemember" );
609                 if ( attribute != null )
610                 {
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 ) } );
615                 }
616                 return true;
617             }
618
619             return false;
620         }
621         catch ( LdapException e )
622         {
623             throw new MappingException( e.getMessage(), e );
624         }
625         catch ( NamingException e )
626         {
627             throw new MappingException( e.getMessage(), e );
628         }
629
630         finally
631         {
632             if ( ldapConnection != null )
633             {
634                 ldapConnection.close();
635             }
636             if ( namingEnumeration != null )
637             {
638                 try
639                 {
640                     namingEnumeration.close();
641                 }
642                 catch ( NamingException e )
643                 {
644                     log.warn( "failed to close search results", e );
645                 }
646             }
647         }
648     }
649
650     public void removeAllRoles()
651         throws MappingException
652     {
653         //all mapped roles
654         Collection<String> groups = getLdapGroupMappings().keySet();
655
656         LdapConnection ldapConnection = null;
657         try
658         {
659             ldapConnection = ldapConnectionFactory.getConnection();
660
661             DirContext context = ldapConnection.getDirContext();
662
663             for ( String groupName : groups )
664             {
665
666                 String dn = "cn=" + groupName + "," + this.groupsDn;
667
668                 context.unbind( dn );
669
670                 log.debug( "deleted group with dn:'{}", dn );
671             }
672
673         }
674         catch ( LdapException e )
675         {
676             throw new MappingException( e.getMessage(), e );
677
678         }
679         catch ( NamingException e )
680         {
681             throw new MappingException( e.getMessage(), e );
682         }
683         finally
684         {
685             if ( ldapConnection != null )
686             {
687                 ldapConnection.close();
688             }
689         }
690     }
691
692     public void removeRole( String roleName )
693         throws MappingException
694     {
695
696         String groupName = HashBiMap.create( getLdapGroupMappings() ).inverse().get( roleName );
697
698         LdapConnection ldapConnection = null;
699         try
700         {
701             ldapConnection = ldapConnectionFactory.getConnection();
702
703             DirContext context = ldapConnection.getDirContext();
704
705             String dn = "cn=" + groupName + "," + this.groupsDn;
706
707             context.unbind( dn );
708
709             log.info( "deleted group with dn:'{}", dn );
710
711         }
712         catch ( LdapException e )
713         {
714             throw new MappingException( e.getMessage(), e );
715
716         }
717         catch ( NamingException e )
718         {
719             throw new MappingException( e.getMessage(), e );
720         }
721         finally
722         {
723             if ( ldapConnection != null )
724             {
725                 ldapConnection.close();
726             }
727         }
728     }
729
730     //---------------------------------
731     // setters for unit tests
732     //---------------------------------
733
734
735     public void setGroupsDn( String groupsDn )
736     {
737         this.groupsDn = groupsDn;
738     }
739
740     public void setLdapGroupClass( String ldapGroupClass )
741     {
742         this.ldapGroupClass = ldapGroupClass;
743     }
744
745     public void setUserConf( UserConfiguration userConf )
746     {
747         this.userConf = userConf;
748     }
749
750     public void setLdapConnectionFactory( LdapConnectionFactory ldapConnectionFactory )
751     {
752         this.ldapConnectionFactory = ldapConnectionFactory;
753     }
754
755     public String getBaseDn()
756     {
757         return baseDn;
758     }
759
760     public void setBaseDn( String baseDn )
761     {
762         this.baseDn = baseDn;
763     }
764 }