RBACService.java

/*
 * Copyright (c) 2002-2022, City of Paris
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice
 *     and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright notice
 *     and the following disclaimer in the documentation and/or other materials
 *     provided with the distribution.
 *
 *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
 *     contributors may be used to endorse or promote products derived from
 *     this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * License 1.0
 */
package fr.paris.lutece.portal.service.rbac;

import fr.paris.lutece.api.user.User;
import fr.paris.lutece.api.user.UserRole;
import fr.paris.lutece.portal.business.rbac.RBAC;
import fr.paris.lutece.portal.business.rbac.RBACHome;
import fr.paris.lutece.portal.business.user.AdminUser;
import fr.paris.lutece.util.ReferenceItem;
import fr.paris.lutece.util.ReferenceList;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * This class provides the main methods to control the access to a resource depending on the user's roles
 */
public final class RBACService
{

    /**
     * ReferenceItem as an RBACResource
     */
    private static final class RBACReferenceItem implements RBACResource
    {
        private final String _strResourceType;
        private final ReferenceItem _item;

        /**
         * Constructor
         * 
         * @param strResourceType
         *            the resource type
         * @param item
         *            the reference item
         */
        public RBACReferenceItem( String strResourceType, ReferenceItem item )
        {
            _strResourceType = strResourceType;
            _item = item;
        }

        @Override
        public String getResourceTypeCode( )
        {
            return _strResourceType;
        }

        @Override
        public String getResourceId( )
        {
            return _item.getCode( );
        }

        /**
         * The reference item
         * 
         * @return the reference item
         */
        public ReferenceItem getItem( )
        {
            return _item;
        }

    }

    /**
     * Constructor
     */
    private RBACService( )
    {
    }

    /**
     * Check that a given user is allowed to access a resource for a given permission
     * 
     * @param strResourceTypeCode
     *            the key of the resource type being considered
     * @param strResourceId
     *            the id of the resource being considered
     * @param strPermission
     *            the permission needed
     * @param user
     *            the user trying to access the ressource
     * @return true if the user can access the given resource with the given permission, false otherwise
     */
    public static boolean isAuthorized( String strResourceTypeCode, String strResourceId, String strPermission, User user )
    {
        // Check user roles
        Collection<String> colRoles = RBACHome.findRoleKeys( strResourceTypeCode, strResourceId, strPermission );

        for ( String strRole : colRoles )
        {
            if ( isUserInRole( user, strRole ) )
            {
                return true;
            }
        }

        return false;
    }

    /**
     * Check that a given user is allowed to access a resource for a given permission
     * 
     * @param strResourceTypeCode
     *            the key of the resource type being considered
     * @param strResourceId
     *            the id of the resource being considered
     * @param strPermission
     *            the permission needed
     * @param user
     *            the user trying to access the ressource
     * @return true if the user can access the given resource with the given permission, false otherwise
     * @deprecated use isAuthorized( String, String, String, User )
     */
    @Deprecated
    public static boolean isAuthorized( String strResourceTypeCode, String strResourceId, String strPermission, AdminUser user )
    {
        return isAuthorized( strResourceTypeCode, strResourceId, strPermission, (User) user );
    }

    /**
     * Check that a given user is allowed to access a resource for a given permission
     * 
     * @param resource
     *            the resource object being considered
     * @param strPermission
     *            the permission needed
     * @param user
     *            the user trying to access the ressource
     * @return true if the user can access the given resource with the given permission, false otherwise
     */
    public static boolean isAuthorized( RBACResource resource, String strPermission, User user )
    {
        boolean bAuthorized = false;

        if ( resource != null )
        {
            bAuthorized = isAuthorized( resource.getResourceTypeCode( ), resource.getResourceId( ), strPermission, user );
        }

        return bAuthorized;
    }

    /**
     * Check that a given user is allowed to access a resource for a given permission
     * 
     * @param resource
     *            the resource object being considered
     * @param strPermission
     *            the permission needed
     * @param user
     *            the user trying to access the ressource
     * @return true if the user can access the given resource with the given permission, false otherwise
     * @deprecated use isAuthorized( RBACResource, String, User )
     */
    @Deprecated
    public static boolean isAuthorized( RBACResource resource, String strPermission, AdminUser user )
    {
        return isAuthorized( resource, strPermission, (User) user );
    }

    /**
     * Check that a given user is in the given role
     * 
     * @param user
     *            The user
     * @param strRole
     *            The role
     * @return true if the user has the given role, false otherwise
     */
    public static boolean isUserInRole( User user, String strRole )
    {
        Map<String, UserRole> userRoles = user.getUserRoles( );

        return userRoles.containsKey( strRole );
    }

    /**
     * Check that a given user is in the given role
     * 
     * @param user
     *            The user
     * @param strRole
     *            The role
     * @return true if the user has the given role, false otherwise
     * @deprecated use isUserInRole( User, String )
     */
    @Deprecated
    public static boolean isUserInRole( AdminUser user, String strRole )
    {
        return isUserInRole( (User) user, strRole );
    }

    /**
     * Filter a collection of resources for a given user
     * 
     * @param <E>
     *            The RBAC resource
     * @param collection
     *            The collection to filter
     * @param strPermission
     *            Permission to check
     * @param user
     *            The user
     * @return A filtered collection of resources
     */
    public static <E extends RBACResource> Collection<E> getAuthorizedCollection( Collection<E> collection, String strPermission, User user )
    {
        if ( user == null )
        {
            return Collections.emptyList( );
        }
        Map<String, Collection<RBAC>> rbacsByResourceType = new HashMap<>( );
        RBACHome.findByPermissionAndRoles( strPermission, user.getUserRoles( ).keySet( ) ).stream( ).forEach( rbac -> {
            rbacsByResourceType.computeIfAbsent( rbac.getResourceTypeKey( ), t -> new ArrayList<>( ) ).add( rbac );
        } );
        return collection.stream( )
                .filter( resource -> rbacsByResourceType
                        .getOrDefault( resource.getResourceTypeCode( ), Collections.emptyList( ) ).stream( )
                        .anyMatch( rbac -> RBAC.WILDCARD_RESOURCES_ID.equals( rbac.getResourceId( ) )
                                || resource.getResourceId( ).equals( rbac.getResourceId( ) ) ) )
                .collect( Collectors.toList( ) );
    }

    /**
     * Filter a collection of resources for a given user
     * 
     * @param <E>
     *            The RBAC resource
     * @param collection
     *            The collection to filter
     * @param strPermission
     *            Permission to check
     * @param user
     *            The user
     * @return A filtered collection of resources
     * @deprecated use getAuthorizedCollection( Collection, String, User )
     */
    @Deprecated
    public static <E extends RBACResource> Collection<E> getAuthorizedCollection( Collection<E> collection, String strPermission, AdminUser user )
    {
        return getAuthorizedCollection( collection, strPermission, (User) user );
    }

    /**
     * Filter a Reference List for a given user
     * 
     * @param listResources
     *            The list to filter
     * @param strResourceType
     *            The resource type
     * @param strPermission
     *            The permission to check
     * @param user
     *            The user
     * @return The filtered collection
     */
    public static ReferenceList getAuthorizedReferenceList( ReferenceList listResources, String strResourceType, String strPermission, User user )
    {
        return getAuthorizedCollection( listResources.stream( )
                .map( item -> new RBACReferenceItem( strResourceType, item ) ).collect( Collectors.toList( ) ),
                strPermission, user ).stream( ).map( RBACReferenceItem::getItem )
                        .collect( Collectors.toCollection( ReferenceList::new ) );
    }

    /**
     * Filter a Reference List for a given user
     * 
     * @param listResources
     *            The list to filter
     * @param strResourceType
     *            The resource type
     * @param strPermission
     *            The permission to check
     * @param user
     *            The user
     * @return The filtered collection
     * @deprecated use getAuthorizedReferenceList( ReferenceList, String, String, User )
     */
    @Deprecated
    public static ReferenceList getAuthorizedReferenceList( ReferenceList listResources, String strResourceType, String strPermission, AdminUser user )
    {
        return getAuthorizedReferenceList( listResources, strResourceType, strPermission, (User) user );
    }

    /**
     * Filter a collection of RBACAction for a given user
     * 
     * @param <E>
     *            The RBAC resource
     * @param collection
     *            The collection to filter
     * @param resource
     *            The resource
     * @param user
     *            The user
     * @return The filtered collection
     */
    public static <E extends RBACAction> Collection<E> getAuthorizedActionsCollection( Collection<E> collection, RBACResource resource, User user )
    {
        if ( collection.isEmpty( ) )
        {
            return collection;
        }
        Set<String> permissions = RBACHome
                .findByPermissionsAndRoles(
                        collection.stream( ).map( RBACAction::getPermission ).collect( Collectors.toSet( ) ),
                        user.getUserRoles( ).keySet( ) )
                .stream( ).filter( rbac -> resource.getResourceTypeCode( ).equals( rbac.getResourceTypeKey( ) ) )
                .filter( rbac -> RBAC.WILDCARD_RESOURCES_ID.equals( rbac.getResourceId( ) )
                        || resource.getResourceId( ).equals( rbac.getResourceId( ) ) )
                .map( RBAC::getPermissionKey ).collect( Collectors.toSet( ) );
        return collection.stream( ).filter( action -> permissions.contains( action.getPermission( ) )
                || permissions.contains( RBAC.WILDCARD_PERMISSIONS_KEY ) ).collect( Collectors.toList( ) );
    }

    /**
     * Filter a collection of RBACAction for a given user
     * 
     * @param <E>
     *            The RBAC resource
     * @param collection
     *            The collection to filter
     * @param resource
     *            The resource
     * @param user
     *            The user
     * @return The filtered collection
     * @deprecated use getAuthorizedActionsCollection( Collection, RBACResource, User )
     */
    @Deprecated
    public static <E extends RBACAction> Collection<E> getAuthorizedActionsCollection( Collection<E> collection, RBACResource resource, AdminUser user )
    {
        return getAuthorizedActionsCollection( collection, resource, (User) user );
    }

}