001package org.apache.archiva.redback.rbac.ldap;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.archiva.redback.common.ldap.MappingException;
023import org.apache.archiva.redback.common.ldap.connection.LdapConnection;
024import org.apache.archiva.redback.common.ldap.connection.LdapConnectionFactory;
025import org.apache.archiva.redback.common.ldap.connection.LdapException;
026import org.apache.archiva.redback.common.ldap.role.LdapRoleMapper;
027import org.apache.archiva.redback.common.ldap.role.LdapRoleMapperConfiguration;
028import org.apache.archiva.components.cache.Cache;
029import org.apache.archiva.redback.configuration.UserConfiguration;
030import org.apache.archiva.redback.configuration.UserConfigurationKeys;
031import org.apache.archiva.redback.rbac.AbstractRBACManager;
032import org.apache.archiva.redback.rbac.AbstractRole;
033import org.apache.archiva.redback.rbac.Operation;
034import org.apache.archiva.redback.rbac.Permission;
035import org.apache.archiva.redback.rbac.RBACManager;
036import org.apache.archiva.redback.rbac.RBACManagerListener;
037import org.apache.archiva.redback.rbac.RBACObjectAssertions;
038import org.apache.archiva.redback.rbac.RbacManagerException;
039import org.apache.archiva.redback.rbac.RbacObjectInvalidException;
040import org.apache.archiva.redback.rbac.RbacObjectNotFoundException;
041import org.apache.archiva.redback.rbac.RbacPermanentException;
042import org.apache.archiva.redback.rbac.Resource;
043import org.apache.archiva.redback.rbac.Role;
044import org.apache.archiva.redback.rbac.UserAssignment;
045import org.apache.archiva.redback.users.User;
046import org.apache.archiva.redback.users.UserManager;
047import org.apache.archiva.redback.users.UserManagerException;
048import org.apache.archiva.redback.users.ldap.ctl.LdapController;
049import org.apache.archiva.redback.users.ldap.ctl.LdapControllerException;
050import org.apache.commons.lang3.StringUtils;
051import org.slf4j.Logger;
052import org.slf4j.LoggerFactory;
053import org.springframework.stereotype.Service;
054
055import javax.annotation.PostConstruct;
056import javax.inject.Inject;
057import javax.inject.Named;
058import javax.naming.NamingException;
059import javax.naming.directory.DirContext;
060import java.util.ArrayList;
061import java.util.Collection;
062import java.util.Collections;
063import java.util.List;
064import java.util.Map;
065import java.util.Optional;
066import java.util.Set;
067import java.util.stream.Collectors;
068import java.util.stream.Stream;
069
070/**
071 * LdapRbacManager will read datas from ldap for mapping groups to role.
072 * Write operations will delegate to cached implementation.
073 *
074 * @author Olivier Lamy
075 */
076@Service("rbacManager#ldap")
077public class LdapRbacManager
078    extends AbstractRBACManager
079    implements RBACManager, RBACManagerListener
080{
081
082    private Logger log = LoggerFactory.getLogger( getClass() );
083
084    @Inject
085    @Named(value = "rbacManager#cached")
086    private RBACManager rbacImpl;
087
088    @Inject
089    @Named(value = "ldapRoleMapper#default")
090    private LdapRoleMapper ldapRoleMapper;
091
092    @Inject
093    @Named(value = "userConfiguration#default")
094    private UserConfiguration userConf;
095
096    @Inject
097    @Named(value = "userManager#ldap")
098    private UserManager userManager;
099
100    @Inject
101    private LdapConnectionFactory ldapConnectionFactory;
102
103    @Inject
104    private LdapController ldapController;
105
106    @Inject
107    @Named(value = "ldapRoleMapperConfiguration#default")
108    private LdapRoleMapperConfiguration ldapRoleMapperConfiguration;
109
110    @Inject
111    @Named(value = "cache#ldapRoles")
112    private Cache<String, Role> rolesCache;
113
114    @Inject
115    @Named(value = "cache#userAssignments")
116    private Cache<String, UserAssignment> userAssignmentsCache;
117
118    private boolean writableLdap = false;
119
120    @Override
121    @PostConstruct
122    public void initialize()
123    {
124        this.writableLdap = userConf.getBoolean( UserConfigurationKeys.LDAP_WRITABLE, this.writableLdap );
125    }
126
127
128    @Override
129    public void addChildRole( Role role, Role childRole )
130        throws RbacObjectInvalidException, RbacManagerException
131    {
132        this.rbacImpl.addChildRole( role, childRole );
133    }
134
135    @Override
136    public void addListener( RBACManagerListener listener )
137    {
138        super.addListener( listener );
139        this.rbacImpl.addListener( listener );
140    }
141
142    @Override
143    public Operation createOperation( String name )
144        throws RbacManagerException
145    {
146        return this.rbacImpl.createOperation( name );
147    }
148
149    @Override
150    public Permission createPermission( String name )
151        throws RbacManagerException
152    {
153        return this.rbacImpl.createPermission( name );
154    }
155
156    @Override
157    public Permission createPermission( String name, String operationName, String resourceIdentifier )
158        throws RbacManagerException
159    {
160        return this.rbacImpl.createPermission( name, operationName, resourceIdentifier );
161    }
162
163    @Override
164    public Resource createResource( String identifier )
165        throws RbacManagerException
166    {
167        return this.rbacImpl.createResource( identifier );
168    }
169
170    @Override
171    public Role createRole( String id, String name )
172    {
173        return this.rbacImpl.createRole( id, name );
174    }
175
176    @Override
177    public UserAssignment createUserAssignment( String username )
178        throws RbacManagerException
179    {
180        // TODO ldap cannot or isWritable ldap ?
181        return this.rbacImpl.createUserAssignment( username );
182    }
183
184    @Override
185    public void eraseDatabase()
186    {
187        if ( writableLdap )
188        {
189            LdapConnection ldapConnection = null;
190            DirContext context = null;
191            try
192            {
193                ldapConnection = ldapConnectionFactory.getConnection();
194                context = ldapConnection.getDirContext();
195                ldapRoleMapper.removeAllRoles( context );
196            }
197            catch ( MappingException e )
198            {
199                log.warn( "skip error removing all roles {}", e.getMessage() );
200            }
201            catch ( LdapException e )
202            {
203                log.warn( "skip error removing all roles {}", e.getMessage() );
204            }
205            finally
206            {
207                closeContext( context );
208                closeLdapConnection( ldapConnection );
209            }
210        }
211        this.rolesCache.clear();
212        this.userAssignmentsCache.clear();
213        this.rbacImpl.eraseDatabase();
214    }
215
216    /**
217     * @see org.apache.archiva.redback.rbac.RBACManager#getAllAssignableRoles()
218     */
219    @Override
220    public List<Role> getAllAssignableRoles()
221        throws RbacManagerException
222    {
223        try
224        {
225            return ldapRoleMapperConfiguration.getLdapGroupMappings( ).entrySet( ).stream( ).flatMap( entry ->{
226                if (entry.getValue()==null) {
227                    return Stream.empty( );
228                } else
229                {
230                    return entry.getValue( ).stream( ).map( role -> new RoleImpl( entry.getKey( ) + role, role ) );
231                }
232            } ).collect( Collectors.toList( ) );
233        }
234        catch ( MappingException e )
235        {
236            throw new RbacManagerException( e.getMessage(), e );
237        }
238    }
239
240    @Override
241    public List<? extends Operation> getAllOperations()
242        throws RbacManagerException
243    {
244        return this.rbacImpl.getAllOperations();
245    }
246
247    @Override
248    public List<? extends Permission> getAllPermissions()
249        throws RbacManagerException
250    {
251        return this.rbacImpl.getAllPermissions();
252    }
253
254    @Override
255    public List<? extends Resource> getAllResources()
256        throws RbacManagerException
257    {
258        return this.rbacImpl.getAllResources();
259    }
260
261    @Override
262    public List<Role> getAllRoles()
263        throws RbacManagerException
264    {
265        LdapConnection ldapConnection = null;
266        DirContext context = null;
267        try
268        {
269            ldapConnection = ldapConnectionFactory.getConnection();
270            context = ldapConnection.getDirContext();
271
272            List<String> groups = ldapRoleMapper.getAllGroups( context );
273            return mapToRoles( groups );
274        }
275        catch ( MappingException e )
276        {
277            throw new RbacManagerException( e.getMessage(), e );
278        }
279        catch ( LdapException e )
280        {
281            throw new RbacManagerException( e.getMessage(), e );
282        }
283        finally
284        {
285            closeContext( context );
286            closeLdapConnection( ldapConnection );
287        }
288        //return this.rbacImpl.getAllRoles();
289    }
290
291
292    @Override
293    public List<UserAssignment> getAllUserAssignments()
294        throws RbacManagerException
295    {
296        LdapConnection ldapConnection = null;
297        DirContext context = null;
298        try
299        {
300            ldapConnection = ldapConnectionFactory.getConnection();
301            context = ldapConnection.getDirContext();
302            Map<String, Collection<String>> usersWithRoles = ldapController.findUsersWithRoles( context );
303            List<UserAssignment> userAssignments = new ArrayList<UserAssignment>( usersWithRoles.size() );
304
305            for ( Map.Entry<String, Collection<String>> entry : usersWithRoles.entrySet() )
306            {
307                UserAssignment userAssignment = new UserAssignmentImpl( entry.getKey(), entry.getValue() );
308                userAssignments.add( userAssignment );
309                userAssignmentsCache.put( userAssignment.getPrincipal(), userAssignment );
310            }
311
312            return userAssignments;
313        }
314        catch ( LdapControllerException e )
315        {
316            throw new RbacManagerException( e.getMessage(), e );
317        }
318        catch ( LdapException e )
319        {
320            throw new RbacManagerException( e.getMessage(), e );
321        }
322        finally
323        {
324            closeContext( context );
325            closeLdapConnection( ldapConnection );
326        }
327    }
328
329    protected void closeLdapConnection( LdapConnection ldapConnection )
330    {
331        if ( ldapConnection != null )
332        {
333            try
334            {
335                ldapConnection.close();
336            }
337            catch ( NamingException e )
338            {
339                log.error( "Could not close connection: {}", e.getMessage( ), e );
340            }
341        }
342    }
343
344    protected void closeContext( DirContext context )
345    {
346        if ( context != null )
347        {
348            try
349            {
350                context.close();
351            }
352            catch ( NamingException e )
353            {
354                log.warn( "skip issue closing context: {}", e.getMessage() );
355            }
356        }
357    }
358
359    /**
360     * public Map<String, List<Permission>> getAssignedPermissionMap( String username )
361     * throws RbacManagerException
362     * {
363     * return this.rbacImpl.getAssignedPermissionMap( username );
364     * }*
365     */
366
367    /*public Set<Permission> getAssignedPermissions( String username )
368        throws RbacObjectNotFoundException, RbacManagerException
369    {
370        // TODO here !!
371        return this.rbacImpl.getAssignedPermissions( username );
372    }*/
373    private List<Role> mapToRoles( List<String> groups )
374        throws MappingException, RbacManagerException
375    {
376        if ( groups == null || groups.isEmpty() )
377        {
378            return Collections.emptyList();
379        }
380        final Map<String, Collection<String>> mappedGroups = ldapRoleMapperConfiguration.getLdapGroupMappings();
381        try
382        {
383            return groups.stream( ).flatMap( group -> mappedGroups.get( group ) == null ?
384                ( this.ldapRoleMapper.isUseDefaultRoleName( ) ? Stream.of( this.buildRole( group, group ) ) : Stream.empty( ) )
385                : mappedGroups.get( group ).stream( ).map( roleName -> this.buildRole( group + roleName, roleName ) ) ).collect( Collectors.toList( ) );
386        } catch (RuntimeException e) {
387            if (e.getCause() instanceof RbacManagerException)
388            {
389                throw ( (RbacManagerException) e.getCause( ) );
390            } else {
391                throw new MappingException( e.getMessage(), e );
392            }
393        }
394    }
395
396    private Role buildRole( String groupId, String roleName )
397    {
398        Role role = null;
399        try
400        {
401            role = this.rbacImpl.getRole( roleName );
402        }
403        catch ( RbacObjectNotFoundException e )
404        {
405            // if it's mapped role to a group it doesn't exist in jpa
406        }
407        catch ( RbacManagerException e )
408        {
409            throw new RuntimeException( e );
410        }
411        role = ( role == null ) ? new RoleImpl( groupId, roleName ) : role;
412        if ( role != null )
413        {
414            rolesCache.put( role.getName(), role );
415
416        }
417        return role;
418    }
419
420    protected List<String> getRealRoles()
421        throws RbacManagerException
422    {
423        List<? extends Role> roles = this.rbacImpl.getAllRoles();
424        List<String> roleNames = new ArrayList<String>( roles.size() );
425        for ( Role role : roles )
426        {
427            roleNames.add( role.getName() );
428        }
429        return roleNames;
430    }
431
432    @Override
433    public Collection<Role> getAssignedRoles( String username )
434        throws RbacManagerException
435    {
436
437        LdapConnection ldapConnection = null;
438        DirContext context = null;
439
440        try
441        {
442
443            ldapConnection = ldapConnectionFactory.getConnection();
444            context = ldapConnection.getDirContext();
445            List<String> roleNames = ldapRoleMapper.getRoles( username, context, getRealRoles() );
446
447            if ( roleNames.isEmpty() )
448            {
449                return Collections.emptyList();
450            }
451
452            List<Role> roles = new ArrayList<Role>( roleNames.size() );
453
454            for ( String name : roleNames )
455            {
456                roles.add( this.rbacImpl.getRole( name ) );// new RoleImpl( name ) );
457            }
458
459            return roles;
460        }
461        catch ( MappingException e )
462        {
463            throw new RbacManagerException( e.getMessage(), e );
464        }
465        catch ( LdapException e )
466        {
467            throw new RbacManagerException( e.getMessage(), e );
468        } finally
469        {
470            closeContext( context );
471            closeLdapConnection( ldapConnection );
472        }
473    }
474
475    @Override
476    public Collection<Role> getAssignedRoles( UserAssignment userAssignment )
477        throws RbacManagerException
478    {
479        return getAssignedRoles( userAssignment.getPrincipal() );
480    }
481
482    @Override
483    public Map<String, ? extends Role> getChildRoleNames( Role role )
484        throws RbacManagerException
485    {
486        return this.rbacImpl.getChildRoleNames( role );
487    }
488
489    @Override
490    public Map<String, ? extends Role> getChildRoleIds( Role role ) throws RbacManagerException
491    {
492        return this.rbacImpl.getChildRoleIds( role );
493    }
494
495    @Override
496    public Map<String, ? extends Role> getParentRoleNames( Role role )
497        throws RbacManagerException
498    {
499        return this.rbacImpl.getParentRoleNames( role );
500    }
501
502    @Override
503    public Map<String, ? extends Role> getParentRoleIds( Role role ) throws RbacManagerException
504    {
505        return this.rbacImpl.getParentRoleIds( role );
506    }
507
508    //
509    // public Collection<Role> getEffectivelyAssignedRoles( String username )
510    // throws RbacManagerException
511    // {
512    // TODO here !!
513    // return this.rbacImpl.getEffectivelyAssignedRoles( username );
514    // }
515
516    //public Collection<Role> getEffectivelyUnassignedRoles( String username )
517    //throws RbacManagerException
518    //{
519    // TODO here !!
520    // return this.rbacImpl.getEffectivelyUnassignedRoles( username );
521    // }
522
523
524    @Override
525    public Set<? extends Role> getEffectiveRoles( Role role )
526        throws RbacManagerException
527    {
528        return this.rbacImpl.getEffectiveRoles( role );
529    }
530
531    @Override
532    public Resource getGlobalResource()
533        throws RbacManagerException
534    {
535        return this.rbacImpl.getGlobalResource();
536    }
537
538    @Override
539    public Operation getOperation( String operationName )
540        throws RbacManagerException
541    {
542        return this.rbacImpl.getOperation( operationName );
543    }
544
545    @Override
546    public Permission getPermission( String permissionName )
547        throws RbacManagerException
548    {
549        return this.rbacImpl.getPermission( permissionName );
550    }
551
552    @Override
553    public Resource getResource( String resourceIdentifier )
554        throws RbacManagerException
555    {
556        return this.rbacImpl.getResource( resourceIdentifier );
557    }
558
559    @Override
560    public Role getRole( String roleName )
561        throws RbacManagerException
562    {
563
564        Role role = rolesCache.get( roleName );
565        if ( role != null )
566        {
567            return role;
568        }
569        if ( !checkIfLdapRole( roleName ) ) return null;
570        role = this.rbacImpl.getRole( roleName );
571        if (role==null)
572        {
573            try
574            {
575                String groupName = ldapRoleMapperConfiguration.getLdapGroupMappings( ).entrySet( ).stream( )
576                    .filter( entry -> entry.getValue( ).contains( roleName ) )
577                    .map( entry -> entry.getKey( ) ).findFirst( ).orElseGet( String::new );
578                role = new RoleImpl( groupName + roleName, roleName );
579            }
580            catch ( MappingException e )
581            {
582                role = new RoleImpl( roleName );
583            }
584        };
585        rolesCache.put( roleName, role );
586        return role;
587    }
588
589    protected boolean checkIfLdapRole( String roleName ) throws RbacManagerException
590    {
591        LdapConnection ldapConnection = null;
592        DirContext context = null;
593        //verify it's a ldap group
594        try
595        {
596            ldapConnection = ldapConnectionFactory.getConnection();
597            context = ldapConnection.getDirContext();
598            if ( !ldapRoleMapper.hasRole( context, roleName ) )
599            {
600                return false;
601            }
602        }
603        catch ( MappingException e )
604        {
605            throw new RbacManagerException( e.getMessage(), e );
606        }
607        catch ( LdapException e )
608        {
609            throw new RbacManagerException( e.getMessage(), e );
610        } finally
611        {
612            closeContext( context );
613            closeLdapConnection( ldapConnection );
614        }
615        return true;
616    }
617
618    @Override
619    public Role getRoleById( String id ) throws RbacObjectNotFoundException, RbacManagerException
620    {
621        Role role = rbacImpl.getRoleById( id );
622        if (role==null) {
623            throw new RbacObjectNotFoundException( "Role with id " + id + " not found" );
624        } else {
625            if (checkIfLdapRole( role.getName() )) {
626                return role;
627            } else {
628                return null;
629            }
630        }
631    }
632
633    @Override
634    public Map<String, ? extends Role> getRoles( Collection<String> roleNames )
635        throws RbacManagerException
636    {
637        return this.rbacImpl.getRoles( roleNames );
638    }
639
640    @Override
641    public Collection<Role> getUnassignedRoles( String username )
642        throws RbacManagerException
643    {
644        LdapConnection ldapConnection = null;
645
646        DirContext context = null;
647
648        try
649        {
650
651            ldapConnection = ldapConnectionFactory.getConnection();
652
653            context = ldapConnection.getDirContext();
654
655            List<String> allRoles = ldapRoleMapper.getAllRoles( context );
656            final List<String> userRoles = ldapRoleMapper.getRoles( username, context, getRealRoles() );
657
658            List<Role> unassignedRoles = new ArrayList<Role>();
659
660            for ( String roleName : allRoles )
661            {
662                if ( !userRoles.contains( roleName ) )
663                {
664                    unassignedRoles.add( rbacImpl.getRole( roleName ) );
665                }
666            }
667            return unassignedRoles;
668        }
669        catch ( MappingException e )
670        {
671            throw new RbacManagerException( e.getMessage(), e );
672        }
673        catch ( LdapException e )
674        {
675            throw new RbacManagerException( e.getMessage(), e );
676        }
677        finally
678        {
679            closeContext( context );
680            closeLdapConnection( ldapConnection );
681        }
682    }
683
684    @Override
685    public UserAssignment getUserAssignment( String username )
686        throws RbacManagerException
687    {
688        UserAssignment ua = userAssignmentsCache.get( username );
689        if ( ua != null )
690        {
691            return ua;
692        }
693        LdapConnection ldapConnection = null;
694        DirContext context = null;
695        try
696        {
697            ldapConnection = ldapConnectionFactory.getConnection();
698            context = ldapConnection.getDirContext();
699            List<String> roles = ldapRoleMapper.getRoles( username, context, getRealRoles() )
700                .stream( ).map( roleName -> {
701                    try
702                    {
703                        return Optional.of( rbacImpl.getRole( roleName ).getId() );
704                    }
705                    catch ( RbacManagerException e )
706                    {
707                        return Optional.<String>empty( );
708                    }
709                } ).filter( Optional::isPresent ).map( Optional::get ).collect( Collectors.toList() );
710
711            ua = new UserAssignmentImpl( username, roles );
712
713            userAssignmentsCache.put( username, ua );
714
715            return ua;
716        }
717        catch ( MappingException e )
718        {
719            throw new RbacManagerException( e.getMessage(), e );
720        }
721        catch ( LdapException e )
722        {
723            throw new RbacManagerException( e.getMessage(), e );
724        }
725        finally
726        {
727            closeContext( context );
728            closeLdapConnection( ldapConnection );
729        }
730
731        //return this.rbacImpl.getUserAssignment( username );
732    }
733
734    @Override
735    public List<? extends UserAssignment> getUserAssignmentsForRoles( Collection<String> roleIds )
736        throws RbacManagerException
737    {
738        // TODO from ldap
739        return this.rbacImpl.getUserAssignmentsForRoles( roleIds );
740    }
741
742    @Override
743    public boolean operationExists( Operation operation )
744    {
745        return this.rbacImpl.operationExists( operation );
746    }
747
748    @Override
749    public boolean operationExists( String name )
750    {
751        return this.rbacImpl.operationExists( name );
752    }
753
754    @Override
755    public boolean permissionExists( Permission permission )
756    {
757        return this.rbacImpl.permissionExists( permission );
758    }
759
760    @Override
761    public boolean permissionExists( String name )
762    {
763        return this.rbacImpl.permissionExists( name );
764    }
765
766    @Override
767    public void rbacInit( boolean freshdb )
768    {
769        if ( rbacImpl instanceof RBACManagerListener )
770        {
771            ( (RBACManagerListener) this.rbacImpl ).rbacInit( freshdb );
772        }
773    }
774
775    @Override
776    public void rbacPermissionRemoved( Permission permission )
777    {
778        if ( rbacImpl instanceof RBACManagerListener )
779        {
780            ( (RBACManagerListener) this.rbacImpl ).rbacPermissionRemoved( permission );
781        }
782
783    }
784
785    @Override
786    public void rbacPermissionSaved( Permission permission )
787    {
788        if ( rbacImpl instanceof RBACManagerListener )
789        {
790            ( (RBACManagerListener) this.rbacImpl ).rbacPermissionSaved( permission );
791        }
792
793    }
794
795    @Override
796    public void rbacRoleRemoved( Role role )
797    {
798        if ( rbacImpl instanceof RBACManagerListener )
799        {
800            ( (RBACManagerListener) this.rbacImpl ).rbacRoleRemoved( role );
801        }
802
803    }
804
805    @Override
806    public void rbacRoleSaved( Role role )
807    {
808        if ( rbacImpl instanceof RBACManagerListener )
809        {
810            ( (RBACManagerListener) this.rbacImpl ).rbacRoleSaved( role );
811        }
812
813    }
814
815    @Override
816    public void rbacUserAssignmentRemoved( UserAssignment userAssignment )
817    {
818        if ( rbacImpl instanceof RBACManagerListener )
819        {
820            ( (RBACManagerListener) this.rbacImpl ).rbacUserAssignmentRemoved( userAssignment );
821        }
822
823    }
824
825    @Override
826    public void rbacUserAssignmentSaved( UserAssignment userAssignment )
827    {
828        if ( rbacImpl instanceof RBACManagerListener )
829        {
830            ( (RBACManagerListener) this.rbacImpl ).rbacUserAssignmentSaved( userAssignment );
831        }
832
833    }
834
835    @Override
836    public void removeListener( RBACManagerListener listener )
837    {
838        this.rbacImpl.removeListener( listener );
839    }
840
841    @Override
842    public void removeOperation( Operation operation )
843        throws RbacManagerException
844    {
845        this.rbacImpl.removeOperation( operation );
846    }
847
848    @Override
849    public void removeOperation( String operationName )
850        throws RbacManagerException
851    {
852        this.rbacImpl.removeOperation( operationName );
853    }
854
855    @Override
856    public void removePermission( Permission permission )
857        throws RbacManagerException
858    {
859        this.rbacImpl.removePermission( permission );
860    }
861
862    @Override
863    public void removePermission( String permissionName )
864        throws RbacManagerException
865    {
866        this.rbacImpl.removePermission( permissionName );
867    }
868
869    @Override
870    public void removeResource( Resource resource )
871        throws RbacManagerException
872    {
873        this.rbacImpl.removeResource( resource );
874    }
875
876    @Override
877    public void removeResource( String resourceIdentifier )
878        throws RbacManagerException
879    {
880        this.rbacImpl.removeResource( resourceIdentifier );
881    }
882
883    @Override
884    public void removeRole( Role role )
885        throws RbacManagerException
886    {
887        RBACObjectAssertions.assertValid( role );
888
889        if ( role.isPermanent() )
890        {
891            throw new RbacPermanentException( "Unable to delete permanent role [" + role.getName() + "]" );
892        }
893        rolesCache.remove( role.getName() );
894        if ( writableLdap )
895        {
896            LdapConnection ldapConnection = null;
897            DirContext context = null;
898            try
899            {
900                ldapConnection = ldapConnectionFactory.getConnection();
901                context = ldapConnection.getDirContext();
902                ldapRoleMapper.removeRole( role.getName(), context );
903            }
904            catch ( MappingException e )
905            {
906                throw new RbacManagerException( e.getMessage(), e );
907            }
908            catch ( LdapException e )
909            {
910                throw new RbacManagerException( e.getMessage(), e );
911            } finally {
912                closeContext( context );
913                closeLdapConnection( ldapConnection );
914            }
915            fireRbacRoleRemoved( role );
916        }
917    }
918
919    @Override
920    public void removeRole( String roleName )
921        throws RbacManagerException
922    {
923        if ( roleName == null )
924        {
925            return;
926        }
927        removeRole( new RoleImpl( roleName ) );
928    }
929
930    @Override
931    public void removeUserAssignment( String username )
932        throws RbacManagerException
933    {
934        // TODO ldap cannot or isWritable ldap ?
935        userAssignmentsCache.remove( username );
936        this.rbacImpl.removeUserAssignment( username );
937    }
938
939    @Override
940    public void removeUserAssignment( UserAssignment userAssignment )
941        throws RbacManagerException
942    {
943        if ( userAssignment != null )
944        {
945            userAssignmentsCache.remove( userAssignment.getPrincipal() );
946        }
947        // TODO ldap cannot or isWritable ldap ?
948        this.rbacImpl.removeUserAssignment( userAssignment );
949    }
950
951    @Override
952    public boolean resourceExists( Resource resource )
953    {
954        return this.rbacImpl.resourceExists( resource );
955    }
956
957    @Override
958    public boolean resourceExists( String identifier )
959    {
960        return this.rbacImpl.resourceExists( identifier );
961    }
962
963    @Override
964    public boolean roleExists( Role role )
965        throws RbacManagerException
966    {
967        if ( role == null )
968        {
969            return false;
970        }
971        return roleExists( role.getName() );
972    }
973
974    @Override
975    public boolean roleExists( String name )
976        throws RbacManagerException
977    {
978        if ( StringUtils.isEmpty( name ) )
979        {
980            return false;
981        }
982        if ( rolesCache.get( name ) != null )
983        {
984            return true;
985        }
986        LdapConnection ldapConnection = null;
987        DirContext context = null;
988        try
989        {
990            ldapConnection = ldapConnectionFactory.getConnection();
991            context = ldapConnection.getDirContext();
992            if ( rolesCache.hasKey( name ) )
993            {
994                return true;
995            }
996            return ldapRoleMapper.hasRole( context, name );
997        }
998        catch ( MappingException e )
999        {
1000            throw new RbacManagerException( e.getMessage(), e );
1001        }
1002        catch ( LdapException e )
1003        {
1004            throw new RbacManagerException( e.getMessage(), e );
1005        }
1006        finally
1007        {
1008            closeContext( context );
1009            closeLdapConnection( ldapConnection );
1010        }
1011    }
1012
1013    @Override
1014    public boolean roleExistsById( String id ) throws RbacManagerException
1015    {
1016        Role role = rbacImpl.getRoleById( id );
1017        if (role==null) {
1018            return false;
1019        } else {
1020            return roleExists( role.getName() );
1021        }
1022    }
1023
1024    @Override
1025    public Operation saveOperation( Operation operation )
1026        throws RbacManagerException
1027    {
1028        return this.rbacImpl.saveOperation( operation );
1029    }
1030
1031    @Override
1032    public Permission savePermission( Permission permission )
1033        throws RbacManagerException
1034    {
1035        return this.rbacImpl.savePermission( permission );
1036    }
1037
1038    @Override
1039    public Resource saveResource( Resource resource )
1040        throws RbacManagerException
1041    {
1042        return this.rbacImpl.saveResource( resource );
1043    }
1044
1045    @Override
1046    public synchronized Role saveRole( Role role )
1047        throws RbacManagerException
1048    {
1049        if ( writableLdap )
1050        {
1051            LdapConnection ldapConnection = null;
1052            DirContext context = null;
1053            try
1054            {
1055                ldapConnection = ldapConnectionFactory.getConnection();
1056                context = ldapConnection.getDirContext();
1057                ldapRoleMapper.saveRole( role.getName(), context );
1058
1059                if ( !role.getChildRoleNames().isEmpty() )
1060                {
1061                    for ( String roleName : role.getChildRoleNames() )
1062                    {
1063                        ldapRoleMapper.saveRole( roleName, context );
1064                    }
1065                }
1066                fireRbacRoleSaved( role );
1067            }
1068            catch ( MappingException e )
1069            {
1070                throw new RbacManagerException( e.getMessage(), e );
1071            }
1072            catch ( LdapException e )
1073            {
1074                throw new RbacManagerException( e.getMessage(), e );
1075            }
1076        }
1077        role = this.rbacImpl.saveRole( role );
1078        rolesCache.put( role.getName(), role );
1079
1080        return role;
1081        //return new RoleImpl( role.getName(), role.getPermissions() );
1082    }
1083
1084    @Override
1085    public synchronized void saveRoles( Collection<Role> roles )
1086        throws RbacManagerException
1087    {
1088        if ( writableLdap )
1089        {
1090            LdapConnection ldapConnection = null;
1091            DirContext context = null;
1092            try
1093            {
1094
1095                ldapConnection = ldapConnectionFactory.getConnection();
1096                context = ldapConnection.getDirContext();
1097                for ( Role role : roles )
1098                {
1099                    ldapRoleMapper.saveRole( role.getName(), context );
1100                    fireRbacRoleSaved( role );
1101                }
1102            }
1103            catch ( MappingException e )
1104            {
1105                throw new RbacManagerException( e.getMessage(), e );
1106            }
1107            catch ( LdapException e )
1108            {
1109                throw new RbacManagerException( e.getMessage(), e );
1110            }
1111        }
1112        this.rbacImpl.saveRoles( roles );
1113
1114    }
1115
1116    @Override
1117    public UserAssignment saveUserAssignment( UserAssignment userAssignment )
1118        throws RbacManagerException
1119    {
1120        LdapConnection ldapConnection = null;
1121        DirContext context = null;
1122        try
1123        {
1124            if ( !userManager.userExists( userAssignment.getPrincipal() ) )
1125            {
1126                User user = userManager.createUser( userAssignment.getPrincipal(), null, null );
1127                userManager.addUser( user );
1128            }
1129            ldapConnection = ldapConnectionFactory.getConnection();
1130            context = ldapConnection.getDirContext();
1131            List<String> allRoles = ldapRoleMapper.getAllRoles( context );
1132
1133            List<String> currentUserRoles =
1134                ldapRoleMapper.getRoles( userAssignment.getPrincipal(), context, getRealRoles() );
1135            Map<String, String> currentUserIds = currentUserRoles.stream( ).map( roleName -> {
1136                try
1137                {
1138                    return Optional.of( rbacImpl.getRole( roleName ) );
1139                }
1140                catch ( RbacManagerException e )
1141                {
1142                    return Optional.<Role>empty( );
1143                }
1144            } ).filter( Optional::isPresent ).map(Optional::get)
1145                .collect( Collectors.toMap( Role::getName, Role::getId ) );
1146
1147            for ( String roleId : userAssignment.getRoleIds() )
1148            {
1149                Role rbacRole = rbacImpl.getRoleById( roleId );
1150                String roleName = rbacRole.getName( );
1151                if ( !currentUserRoles.contains( roleName ) && writableLdap )
1152                {
1153                    // role exists in ldap ?
1154                    if ( !allRoles.contains( roleName ) )
1155                    {
1156                        ldapRoleMapper.saveRole( roleName, context );
1157                        allRoles.add( roleName );
1158                    }
1159                    ldapRoleMapper.saveUserRole( roleName, userAssignment.getPrincipal(), context );
1160                    currentUserRoles.add( roleName );
1161                    currentUserIds.put( roleName, rbacRole.getId( ) );
1162                }
1163            }
1164
1165            for ( String roleName : currentUserRoles )
1166            {
1167                if ( !userAssignment.getRoleIds().contains( currentUserIds.get(roleName) ) && writableLdap )
1168                {
1169                    ldapRoleMapper.removeUserRole( roleName, userAssignment.getPrincipal(), context );
1170                }
1171            }
1172
1173            userAssignmentsCache.put( userAssignment.getPrincipal(), userAssignment );
1174            return userAssignment;
1175        }
1176        catch ( UserManagerException e )
1177        {
1178            throw new RbacManagerException( e.getMessage(), e );
1179        }
1180        catch ( MappingException e )
1181        {
1182            throw new RbacManagerException( e.getMessage(), e );
1183        }
1184        catch ( LdapException e )
1185        {
1186            throw new RbacManagerException( e.getMessage(), e );
1187        }
1188        finally
1189        {
1190            closeContext( context );
1191            closeLdapConnection( ldapConnection );
1192        }
1193    }
1194
1195    @Override
1196    public boolean userAssignmentExists( String principal )
1197    {
1198        if ( userAssignmentsCache.hasKey( principal ) )
1199        {
1200            return true;
1201        }
1202        LdapConnection ldapConnection = null;
1203        DirContext context = null;
1204        try
1205        {
1206            ldapConnection = ldapConnectionFactory.getConnection();
1207            context = ldapConnection.getDirContext();
1208            List<String> roles = ldapRoleMapper.getRoles( principal, context, getRealRoles() );
1209            if ( roles == null || roles.isEmpty() )
1210            {
1211                return false;
1212            }
1213            return true;
1214        }
1215        catch ( RbacManagerException e )
1216        {
1217            log.warn( "fail to call userAssignmentExists: {}", e.getMessage() );
1218        }
1219        catch ( LdapException e )
1220        {
1221            log.warn( "fail to call userAssignmentExists: {}", e.getMessage() );
1222        }
1223        catch ( MappingException e )
1224        {
1225            log.warn( "fail to call userAssignmentExists: {}", e.getMessage() );
1226        }
1227        finally
1228        {
1229            closeContext( context );
1230            closeLdapConnection( ldapConnection );
1231        }
1232        return false;
1233    }
1234
1235    @Override
1236    public boolean userAssignmentExists( UserAssignment assignment )
1237    {
1238        if ( assignment == null )
1239        {
1240            return false;
1241        }
1242        return this.userAssignmentExists( assignment.getPrincipal() );
1243    }
1244
1245    public RBACManager getRbacImpl()
1246    {
1247        return rbacImpl;
1248    }
1249
1250    public void setRbacImpl( RBACManager rbacImpl )
1251    {
1252        this.rbacImpl = rbacImpl;
1253    }
1254
1255    public boolean isWritableLdap()
1256    {
1257        return writableLdap;
1258    }
1259
1260    public void setWritableLdap( boolean writableLdap )
1261    {
1262        this.writableLdap = writableLdap;
1263    }
1264
1265    public LdapRoleMapper getLdapRoleMapper()
1266    {
1267        return ldapRoleMapper;
1268    }
1269
1270    public void setLdapRoleMapper( LdapRoleMapper ldapRoleMapper )
1271    {
1272        this.ldapRoleMapper = ldapRoleMapper;
1273    }
1274
1275    private static class RoleImpl
1276        extends AbstractRole
1277    {
1278        private String name;
1279
1280        private String description;
1281        private String id="";
1282        private String modelId="";
1283        private boolean isTemplateInstance=false;
1284        private String resource="";
1285
1286        private List<Permission> permissions = new ArrayList<>();
1287
1288        private List<String> childRoleNames = new ArrayList<>();
1289        private List<String> childRoleIds = new ArrayList<>( );
1290
1291        private RoleImpl( String name )
1292        {
1293            this.name = name;
1294            this.id = name;
1295        }
1296
1297        private RoleImpl(String id, String name) {
1298            this.id = id;
1299            this.name = name;
1300        }
1301
1302        private RoleImpl( String name, List<Permission> permissions )
1303        {
1304            this.name = name;
1305            this.permissions = permissions;
1306        }
1307
1308        @Override
1309        public void addPermission( Permission permission )
1310        {
1311            this.permissions.add( permission );
1312        }
1313
1314        @Override
1315        public void addChildRoleName( String name )
1316        {
1317            this.childRoleNames.add( name );
1318        }
1319
1320        @Override
1321        public List<String> getChildRoleNames()
1322        {
1323            return this.childRoleNames;
1324        }
1325
1326        @Override
1327        public void addChildRoleId( String id )
1328        {
1329            this.childRoleIds.add( id );
1330        }
1331
1332        @Override
1333        public List<String> getChildRoleIds( )
1334        {
1335            return this.childRoleIds;
1336        }
1337
1338        @Override
1339        public String getDescription()
1340        {
1341            return this.description;
1342        }
1343
1344        @Override
1345        public String getName()
1346        {
1347            return this.name;
1348        }
1349
1350        @Override
1351        public List<Permission> getPermissions()
1352        {
1353            return this.permissions;
1354        }
1355
1356        @Override
1357        public boolean isAssignable()
1358        {
1359            return true;
1360        }
1361
1362        @Override
1363        public void removePermission( Permission permission )
1364        {
1365            this.permissions.remove( permission );
1366        }
1367
1368        @Override
1369        public void setAssignable( boolean assignable )
1370        {
1371            // no op
1372        }
1373
1374        @Override
1375        public void setChildRoleNames( List<String> names )
1376        {
1377            this.childRoleNames = names;
1378        }
1379
1380        @Override
1381        public void setChildRoleIds( List<String> ids )
1382        {
1383
1384        }
1385
1386        @Override
1387        public void setDescription( String description )
1388        {
1389            this.description = description;
1390        }
1391
1392        @Override
1393        public void setName( String name )
1394        {
1395            this.name = name;
1396        }
1397
1398        @Override
1399        public void setPermissions( List<Permission> permissions )
1400        {
1401            this.permissions = permissions;
1402        }
1403
1404        @Override
1405        public boolean isPermanent()
1406        {
1407            return true;
1408        }
1409
1410        @Override
1411        public void setPermanent( boolean permanent )
1412        {
1413            // no op
1414        }
1415
1416        @Override
1417        public String toString()
1418        {
1419            final StringBuilder sb = new StringBuilder();
1420            sb.append( "RoleImpl" );
1421            sb.append( "{name='" ).append( name ).append( '\'' );
1422            sb.append( '}' );
1423            return sb.toString();
1424        }
1425
1426        @Override
1427        public boolean equals( Object o )
1428        {
1429            if ( this == o )
1430            {
1431                return true;
1432            }
1433            if ( o == null || getClass() != o.getClass() )
1434            {
1435                return false;
1436            }
1437
1438            RoleImpl role = (RoleImpl) o;
1439
1440            if ( name != null ? !name.equals( role.name ) : role.name != null )
1441            {
1442                return false;
1443            }
1444
1445            return true;
1446        }
1447
1448        @Override
1449        public int hashCode()
1450        {
1451            return name != null ? name.hashCode() : 0;
1452        }
1453
1454
1455        @Override
1456        public String getId( )
1457        {
1458            return id;
1459        }
1460
1461        @Override
1462        public void setId( String id )
1463        {
1464            if (id==null) {
1465                this.id = "";
1466            } else
1467            {
1468                this.id = id;
1469            }
1470        }
1471
1472        @Override
1473        public String getModelId( )
1474        {
1475            return modelId;
1476        }
1477
1478        @Override
1479        public void setModelId( String modelId )
1480        {
1481            if (modelId==null) {
1482                this.modelId = "";
1483            } else
1484            {
1485                this.modelId = modelId;
1486            }
1487        }
1488
1489        @Override
1490        public boolean isTemplateInstance( )
1491        {
1492            return isTemplateInstance;
1493        }
1494
1495        @Override
1496        public void setTemplateInstance( boolean templateInstance )
1497        {
1498            isTemplateInstance = templateInstance;
1499        }
1500
1501        @Override
1502        public String getResource( )
1503        {
1504            return resource;
1505        }
1506
1507        @Override
1508        public void setResource( String resource )
1509        {
1510            if (resource==null) {
1511                this.resource = "";
1512            } else
1513            {
1514                this.resource = resource;
1515            }
1516        }
1517
1518
1519    }
1520
1521    private static class UserAssignmentImpl
1522        implements UserAssignment
1523    {
1524        private String username;
1525
1526        private List<String> roleIds;
1527
1528        private boolean permanent;
1529
1530        private UserAssignmentImpl( String username, Collection<String> roleIds )
1531        {
1532            this.username = username;
1533
1534            if ( roleIds == null )
1535            {
1536                this.roleIds = new ArrayList<>( );
1537            }
1538            else
1539            {
1540                this.roleIds = new ArrayList<>( roleIds );
1541            }
1542        }
1543
1544        @Override
1545        public String getPrincipal()
1546        {
1547            return this.username;
1548        }
1549
1550        @Override
1551        public List<String> getRoleNames()
1552        {
1553            return this.roleIds;
1554        }
1555
1556        @Override
1557        public List<String> getRoleIds( )
1558        {
1559            return this.roleIds;
1560        }
1561
1562        @Override
1563        public void addRoleName( Role role )
1564        {
1565            if ( role == null )
1566            {
1567                return;
1568            }
1569            this.roleIds.add( role.getName() );
1570        }
1571
1572        @Override
1573        public void addRoleName( String roleName )
1574        {
1575            if ( roleName == null )
1576            {
1577                return;
1578            }
1579            this.roleIds.add( roleName );
1580        }
1581
1582        @Override
1583        public void addRoleId( Role role )
1584        {
1585            if ( role == null )
1586            {
1587                return;
1588            }
1589            this.roleIds.add( role.getId() );
1590        }
1591
1592        @Override
1593        public void addRoleId( String roleId )
1594        {
1595            if ( roleId == null )
1596            {
1597                return;
1598            }
1599            this.roleIds.add( roleId );
1600        }
1601
1602        @Override
1603        public void removeRoleName( Role role )
1604        {
1605            if ( role == null )
1606            {
1607                return;
1608            }
1609            this.roleIds.remove( role.getName() );
1610        }
1611
1612        @Override
1613        public void removeRoleName( String roleName )
1614        {
1615            if ( roleName == null )
1616            {
1617                return;
1618            }
1619            this.roleIds.remove( roleName );
1620        }
1621
1622        @Override
1623        public void removeRoleId( Role role )
1624        {
1625            if ( role == null )
1626            {
1627                return;
1628            }
1629            this.roleIds.remove( role.getId() );
1630        }
1631
1632        @Override
1633        public void removeRoleId( String roleId )
1634        {
1635            if ( roleId == null )
1636            {
1637                return;
1638            }
1639            this.roleIds.remove( roleId );
1640        }
1641
1642        @Override
1643        public void setPrincipal( String principal )
1644        {
1645            this.username = principal;
1646        }
1647
1648        @Override
1649        public void setRoleNames( List<String> roles )
1650        {
1651            this.roleIds = roles;
1652        }
1653
1654        @Override
1655        public void setRoleIds( List<String> roles )
1656        {
1657            this.roleIds = roles;
1658        }
1659
1660        @Override
1661        public boolean isPermanent()
1662        {
1663            return this.permanent;
1664        }
1665
1666        @Override
1667        public void setPermanent( boolean permanent )
1668        {
1669            this.permanent = permanent;
1670        }
1671
1672        @Override
1673        public String toString()
1674        {
1675            final StringBuilder sb = new StringBuilder();
1676            sb.append( "UserAssignmentImpl" );
1677            sb.append( "{username='" ).append( username ).append( '\'' );
1678            sb.append( ", roleNames=" ).append( roleIds );
1679            sb.append( ", permanent=" ).append( permanent );
1680            sb.append( '}' );
1681            return sb.toString();
1682        }
1683    }
1684
1685
1686    @Override
1687    public boolean isFinalImplementation()
1688    {
1689        return true;
1690    }
1691
1692    @Override
1693    public String getDescriptionKey()
1694    {
1695        return "archiva.redback.rbacmanager.ldap";
1696    }
1697
1698    @Override
1699    public boolean isReadOnly()
1700    {
1701        return !writableLdap;
1702    }
1703}