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