org.sigmah.server.security.impl.AccessRights.java Source code

Java tutorial

Introduction

Here is the source code for org.sigmah.server.security.impl.AccessRights.java

Source

package org.sigmah.server.security.impl;

/*
 * #%L
 * Sigmah
 * %%
 * Copyright (C) 2010 - 2016 URD
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.sigmah.client.page.Page;
import org.sigmah.server.domain.User;
import org.sigmah.server.handler.util.Handlers;
import org.sigmah.server.mapper.Mapper;
import org.sigmah.server.servlet.base.ServletExecutionContext;
import org.sigmah.shared.command.AddOrgUnit;
import org.sigmah.shared.command.AddPartner;
import org.sigmah.shared.command.AmendmentActionCommand;
import org.sigmah.shared.command.BackupArchiveManagementCommand;
import org.sigmah.shared.command.BatchCommand;
import org.sigmah.shared.command.ChangePasswordCommand;
import org.sigmah.shared.command.ChangePhase;
import org.sigmah.shared.command.CheckModelUsage;
import org.sigmah.shared.command.CopyLogFrame;
import org.sigmah.shared.command.CreateEntity;
import org.sigmah.shared.command.DeactivateUsers;
import org.sigmah.shared.command.Delete;
import org.sigmah.shared.command.DeleteCategories;
import org.sigmah.shared.command.DeleteFlexibleElements;
import org.sigmah.shared.command.DeleteImportationSchemeModels;
import org.sigmah.shared.command.DeleteImportationSchemes;
import org.sigmah.shared.command.DeletePrivacyGroups;
import org.sigmah.shared.command.DeleteProfiles;
import org.sigmah.shared.command.DeleteReportModels;
import org.sigmah.shared.command.DisableFlexibleElements;
import org.sigmah.shared.command.DownloadSlice;
import org.sigmah.shared.command.GenerateElement;
import org.sigmah.shared.command.GetAdminEntities;
import org.sigmah.shared.command.GetAvailableStatusForModel;
import org.sigmah.shared.command.GetBaseMaps;
import org.sigmah.shared.command.GetCalendar;
import org.sigmah.shared.command.GetCategories;
import org.sigmah.shared.command.GetCountries;
import org.sigmah.shared.command.GetCountry;
import org.sigmah.shared.command.GetFilesFromFavoriteProjects;
import org.sigmah.shared.command.GetGlobalExportSettings;
import org.sigmah.shared.command.GetGlobalExports;
import org.sigmah.shared.command.GetHistory;
import org.sigmah.shared.command.GetLinkedProjects;
import org.sigmah.shared.command.GetMonitoredPoints;
import org.sigmah.shared.command.GetOrgUnit;
import org.sigmah.shared.command.GetOrgUnitModel;
import org.sigmah.shared.command.GetOrganization;
import org.sigmah.shared.command.GetProject;
import org.sigmah.shared.command.GetProjectDocuments;
import org.sigmah.shared.command.GetProjectModel;
import org.sigmah.shared.command.GetProjectModels;
import org.sigmah.shared.command.GetProjectReport;
import org.sigmah.shared.command.GetProjectReports;
import org.sigmah.shared.command.GetProjects;
import org.sigmah.shared.command.GetProjectsByModel;
import org.sigmah.shared.command.GetProjectsFromId;
import org.sigmah.shared.command.GetReminders;
import org.sigmah.shared.command.GetUsers;
import org.sigmah.shared.command.GetUsersByOrganization;
import org.sigmah.shared.command.GetUsersWithProfiles;
import org.sigmah.shared.command.GetValue;
import org.sigmah.shared.command.SecureNavigationCommand;
import org.sigmah.shared.command.Synchronize;
import org.sigmah.shared.command.UpdateProject;
import org.sigmah.shared.command.UpdateProjectFavorite;
import org.sigmah.shared.command.UploadSlice;
import org.sigmah.shared.command.base.Command;
import org.sigmah.shared.dto.profile.ProfileDTO;
import org.sigmah.shared.dto.referential.GlobalPermissionEnum;
import org.sigmah.shared.servlet.ServletConstants.Servlet;
import org.sigmah.shared.servlet.ServletConstants.ServletMethod;
import org.sigmah.shared.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Access rights configuration.
 * 
 * @author Denis Colliot (dcolliot@ideia.fr)
 */
final class AccessRights {

    /**
     * Logger.
     */
    private static final Logger LOG = LoggerFactory.getLogger(AccessRights.class);

    /**
     * Permissions map linking a secured token to a set of {@link GlobalPermissionEnum}.
     */
    private static final Map<String, Pair<GrantType, Set<GlobalPermissionEnum>>> permissions = new HashMap<>();

    /**
     * Unchecked resources tokens (they are always granted).
     */
    private static final Set<String> grantedTokens = new HashSet<>();

    /**
     * Token representing <em>missing tokens</em>.<br/>
     * If a token is not declared among security permissions, this token is used.
     */
    private static final String MISSING_TOKEN = "*";

    /**
     * Permissions configuration.
     */
    // TODO Complete permissions.
    static {

        // FIXME For the time being, all missing tokens are considered NOT secured. This line should be deleted in
        // production.
        sperm(MISSING_TOKEN, GrantType.BOTH);

        // Pages.
        sperm(pageToken(Page.LOGIN), GrantType.ANONYMOUS_ONLY);
        sperm(pageToken(Page.RESET_PASSWORD), GrantType.ANONYMOUS_ONLY);
        sperm(pageToken(Page.LOST_PASSWORD), GrantType.ANONYMOUS_ONLY);
        sperm(pageToken(Page.CHANGE_OWN_PASSWORD), GrantType.AUTHENTICATED_ONLY,
                GlobalPermissionEnum.CHANGE_PASSWORD);
        sperm(pageToken(Page.MOCKUP), GrantType.BOTH);
        sperm(pageToken(Page.CREDITS), GrantType.AUTHENTICATED_ONLY);
        sperm(pageToken(Page.HELP), GrantType.AUTHENTICATED_ONLY);

        sperm(pageToken(Page.DASHBOARD), GrantType.AUTHENTICATED_ONLY);

        sperm(pageToken(Page.PROJECT_DASHBOARD), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_PROJECT);
        sperm(pageToken(Page.PROJECT_DETAILS), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_PROJECT);
        sperm(pageToken(Page.PROJECT_CALENDAR), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_PROJECT,
                GlobalPermissionEnum.VIEW_PROJECT_AGENDA);
        sperm(pageToken(Page.PROJECT_INDICATORS_ENTRIES), GrantType.AUTHENTICATED_ONLY,
                GlobalPermissionEnum.VIEW_PROJECT, GlobalPermissionEnum.VIEW_INDICATOR);
        sperm(pageToken(Page.PROJECT_INDICATORS_MANAGEMENT), GrantType.AUTHENTICATED_ONLY,
                GlobalPermissionEnum.VIEW_PROJECT, GlobalPermissionEnum.VIEW_INDICATOR);
        sperm(pageToken(Page.PROJECT_INDICATORS_MAP), GrantType.AUTHENTICATED_ONLY,
                GlobalPermissionEnum.VIEW_PROJECT, GlobalPermissionEnum.VIEW_INDICATOR);
        sperm(pageToken(Page.PROJECT_LOGFRAME), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_PROJECT,
                GlobalPermissionEnum.VIEW_LOGFRAME);
        sperm(pageToken(Page.PROJECT_REPORTS), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_PROJECT);

        sperm(pageToken(Page.INDICATOR_EDIT), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.MANAGE_INDICATOR);
        sperm(pageToken(Page.SITE_EDIT), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.MANAGE_INDICATOR);

        sperm(pageToken(Page.CREATE_PROJECT), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.CREATE_PROJECT);

        sperm(pageToken(Page.ORGUNIT_DASHBOARD), GrantType.AUTHENTICATED_ONLY);
        sperm(pageToken(Page.ORGUNIT_CALENDAR), GrantType.AUTHENTICATED_ONLY,
                GlobalPermissionEnum.VIEW_PROJECT_AGENDA);
        sperm(pageToken(Page.ORGUNIT_DETAILS), GrantType.AUTHENTICATED_ONLY);
        sperm(pageToken(Page.ORGUNIT_REPORTS), GrantType.AUTHENTICATED_ONLY);

        sperm(pageToken(Page.ADMIN_PARAMETERS), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_ADMIN,
                GlobalPermissionEnum.MANAGE_SETTINGS);
        sperm(pageToken(Page.ADMIN_CATEGORIES), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_ADMIN,
                GlobalPermissionEnum.MANAGE_CATEGORIES);
        sperm(pageToken(Page.ADMIN_ORG_UNITS), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_ADMIN,
                GlobalPermissionEnum.MANAGE_ORG_UNITS);
        sperm(pageToken(Page.ADMIN_USERS), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_ADMIN,
                GlobalPermissionEnum.MANAGE_USERS);
        sperm(pageToken(Page.ADMIN_PROJECTS_MODELS), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_ADMIN,
                GlobalPermissionEnum.MANAGE_PROJECT_MODELS);
        sperm(pageToken(Page.ADMIN_REPORTS_MODELS), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_ADMIN,
                GlobalPermissionEnum.MANAGE_REPORT_MODELS);
        sperm(pageToken(Page.ADMIN_ORG_UNITS_MODELS), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.VIEW_ADMIN,
                GlobalPermissionEnum.MANAGE_ORG_UNIT_MODELS);
        sperm(pageToken(Page.ADMIN_IMPORTATION_SCHEME), GrantType.AUTHENTICATED_ONLY,
                GlobalPermissionEnum.VIEW_ADMIN, GlobalPermissionEnum.MANAGE_IMPORTATION_SCHEMES);
        sperm(pageToken(Page.ADMIN_ADD_IMPORTATION_SCHEME), GrantType.AUTHENTICATED_ONLY,
                GlobalPermissionEnum.VIEW_ADMIN, GlobalPermissionEnum.MANAGE_IMPORTATION_SCHEMES);
        sperm(pageToken(Page.ADMIN_ADD_VARIABLE_IMPORTATION_SCHEME), GrantType.AUTHENTICATED_ONLY,
                GlobalPermissionEnum.VIEW_ADMIN, GlobalPermissionEnum.MANAGE_IMPORTATION_SCHEMES);

        // Commands.
        sperm(commandToken(AddOrgUnit.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(AddPartner.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(AmendmentActionCommand.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(BackupArchiveManagementCommand.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(BatchCommand.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(ChangePasswordCommand.class), GrantType.AUTHENTICATED_ONLY,
                GlobalPermissionEnum.CHANGE_PASSWORD);
        sperm(commandToken(ChangePhase.class), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.CHANGE_PHASE);
        sperm(commandToken(CheckModelUsage.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(CreateEntity.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(CopyLogFrame.class), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.EDIT_LOGFRAME);
        sperm(commandToken(DeactivateUsers.class), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.MANAGE_USERS);
        sperm(commandToken(DisableFlexibleElements.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(DeleteCategories.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(DeleteFlexibleElements.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(Delete.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(DeleteImportationSchemeModels.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(DeleteImportationSchemes.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(DeletePrivacyGroups.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(DeleteProfiles.class), GrantType.AUTHENTICATED_ONLY, GlobalPermissionEnum.MANAGE_USERS);
        sperm(commandToken(DeleteReportModels.class), GrantType.AUTHENTICATED_ONLY,
                GlobalPermissionEnum.MANAGE_REPORT_MODELS);
        sperm(commandToken(DownloadSlice.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GenerateElement.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetAdminEntities.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetAvailableStatusForModel.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetBaseMaps.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetCalendar.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetCategories.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetCountries.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetCountry.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetFilesFromFavoriteProjects.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetGlobalExportSettings.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetGlobalExports.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetHistory.class), GrantType.AUTHENTICATED_ONLY);
        // TODO: Add the missing commands
        sperm(commandToken(GetLinkedProjects.class), GrantType.AUTHENTICATED_ONLY,
                GlobalPermissionEnum.VIEW_PROJECT);
        sperm(commandToken(GetMonitoredPoints.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetOrganization.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetOrgUnit.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetOrgUnitModel.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetProject.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetProjectDocuments.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetProjectModel.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetProjectModels.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetProjectReport.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetProjectReports.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetProjects.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetProjectsByModel.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetProjectsFromId.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetReminders.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetUsersByOrganization.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetUsers.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetUsersWithProfiles.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(GetValue.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(Synchronize.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(UpdateProject.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(UpdateProjectFavorite.class), GrantType.AUTHENTICATED_ONLY);
        sperm(commandToken(UploadSlice.class), GrantType.AUTHENTICATED_ONLY);

        // Servlet methods.
        sperm(servletToken(Servlet.FILE, ServletMethod.DOWNLOAD_LOGO), GrantType.AUTHENTICATED_ONLY);
        sperm(servletToken(Servlet.FILE, ServletMethod.DOWNLOAD_FILE), GrantType.AUTHENTICATED_ONLY);
        sperm(servletToken(Servlet.FILE, ServletMethod.DOWNLOAD_ARCHIVE), GrantType.AUTHENTICATED_ONLY);
    }

    /**
     * Granted tokens that are always granted in order to optimize application processes.
     */
    static {
        grantedTokens.add(commandToken(SecureNavigationCommand.class));
    }

    /**
     * Grants or refuse {@code user} access to the given {@code token}.
     * 
     * @param user
     *          The user (authenticated or anonymous).
     * @param token
     *          The resource token (page, command, servlet method, etc.).
     * @param originPageToken
     *          The origin page token <em>(TODO Not used yet)</em>.
     * @param mapper
     *          The mapper service.
     * @return {@code true} if the user is granted, {@code false} otherwise.
     */
    static boolean isGranted(final User user, final String token, final String originPageToken,
            final Mapper mapper) {

        if (grantedTokens.contains(token)) {
            // Granted tokens ; avoids profile aggregation if user is authenticated.
            return true;
        }

        if (!permissions.containsKey(token)) {
            if (LOG.isWarnEnabled()) {
                LOG.warn(
                        "No security permission can be found for token '{}'. Did you forget to declare corresponding 'sperm'?",
                        token);
            }
            return isGranted(user, MISSING_TOKEN, originPageToken, mapper);
        }

        final Pair<GrantType, Set<GlobalPermissionEnum>> grantData = permissions.get(token);
        final GrantType grantType = grantData.left;

        final boolean granted;

        if (user == null || ServletExecutionContext.ANONYMOUS_USER.equals(user)) {
            // Anonymous user.
            granted = grantType != null && grantType != GrantType.AUTHENTICATED_ONLY;

        } else {
            // Authenticated user.
            if (grantType != null && grantType == GrantType.ANONYMOUS_ONLY) {
                granted = false;

            } else {
                final ProfileDTO aggregatedProfile = Handlers.aggregateProfiles(user, mapper);
                granted = CollectionUtils.containsAll(aggregatedProfile.getGlobalPermissions(), grantData.right);
            }
        }

        return granted;
    }

    // -------------------------------------------------------------------------------------
    //
    // TOKEN METHODS.
    //
    // -------------------------------------------------------------------------------------

    /**
     * Return the <em>resource</em> token for the given servlet arguments.
     * 
     * @param servlet
     *          The {@link Servlet} name.
     * @param method
     *          The {@link Servlet} method.
     * @return the <em>resource</em> token for the given servlet arguments, or {@code null}.
     */
    static String servletToken(final Servlet servlet, final ServletMethod method) {
        if (servlet == null || method == null) {
            return null;
        }
        return servlet.name() + '#' + method.name();
    }

    /**
     * Return the <em>resource</em> token for the given {@code commandClass}.
     * 
     * @param commandClass
     *          The {@link Command} class.
     * @return the <em>resource</em> token for the given {@code commandClass}, or {@code null}.
     */
    @SuppressWarnings("rawtypes")
    static String commandToken(final Class<? extends Command> commandClass) {
        if (commandClass == null) {
            return null;
        }
        return commandClass.getName();
    }

    /**
     * Return the <em>resource</em> token for the given {@code page}.
     * 
     * @param page
     *          The {@link Page} instance.
     * @return the <em>resource</em> token for the given {@code page}, or {@code null}.
     */
    static String pageToken(final Page page) {
        if (page == null) {
            return null;
        }
        return page.getToken();
    }

    // -------------------------------------------------------------------------------------
    //
    // UTILITY METHODS.
    //
    // -------------------------------------------------------------------------------------

    private static enum GrantType {

        /**
         * Access granted to <em>anonymous</em> user <b>only</b>.
         */
        ANONYMOUS_ONLY,

        /**
         * Access granted to <em>authenticated</em> users <b>only</b>.
         */
        AUTHENTICATED_ONLY,

        /**
         * Access granted to <em>anonymous</em> <b>and</b> <em>authenticated</em> users.
         */
        BOTH;

    }

    /**
     * <p>
     * Registers a new <u>S</u>ecurity <u>PERM</u>ission for the given {@code token}.
     * </p>
     * <p>
     * ;-)
     * </p>
     * 
     * @param token
     *          The resource token.
     * @param grantType
     *          The grant type, see {@link GrantType}.
     * @param gpes
     *          The {@link GlobalPermissionEnum} that the user needs to possess in order to be granted for the
     *          {@code token}.
     */
    private static void sperm(final String token, final GrantType grantType, final GlobalPermissionEnum... gpes) {
        permissions.put(token, new Pair<>(grantType, toSet(gpes)));
    }

    /**
     * Transforms the given {@code gpes} array into a {@link Set}.<br/>
     * Ignores {@code null} values in the process.
     * 
     * @param gpes
     *          The {@link GlobalPermissionEnum} array.
     * @return the given {@code gpes} array transformed into a {@link Set} with no {@code null} values.
     */
    private static Set<GlobalPermissionEnum> toSet(final GlobalPermissionEnum... gpes) {

        final Set<GlobalPermissionEnum> set = new HashSet<GlobalPermissionEnum>();

        if (ArrayUtils.isEmpty(gpes)) {
            return set;
        }

        for (final GlobalPermissionEnum gpe : gpes) {
            if (gpe == null) {
                continue;
            }
            set.add(gpe);
        }

        return set;
    }

    /**
     * Utility class constructor.
     */
    private AccessRights() {
        // Only provides static constants.
    }

}