io.lavagna.service.PermissionService.java Source code

Java tutorial

Introduction

Here is the source code for io.lavagna.service.PermissionService.java

Source

/**
 * This file is part of lavagna.
 *
 * lavagna 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.
 *
 * lavagna 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 lavagna.  If not, see <http://www.gnu.org/licenses/>.
 */
package io.lavagna.service;

import io.lavagna.model.Permission;
import io.lavagna.model.ProjectRoleAndPermission;
import io.lavagna.model.Role;
import io.lavagna.model.RoleAndMetadata;
import io.lavagna.model.RoleAndPermission;
import io.lavagna.model.RoleAndProject;
import io.lavagna.model.User;
import io.lavagna.model.UserIdentifier;
import io.lavagna.query.PermissionQuery;

import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.apache.commons.lang3.Validate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
public class PermissionService {

    private final NamedParameterJdbcTemplate jdbc;
    private final PermissionQuery queries;
    private final UserRepository userRepository;

    public PermissionService(NamedParameterJdbcTemplate jdbc, PermissionQuery queries,
            UserRepository userRepository) {
        this.jdbc = jdbc;
        this.queries = queries;
        this.userRepository = userRepository;
    }

    /**
     * A role can be without any permission.
     */
    public Map<String, RoleAndPermissions> findBaseRoleAndPermissionByUserId(int userId) {
        return toMap(queries.findBaseRoleAndPermissionByUserId(userId));
    }

    public Map<String, RoleAndPermissions> findRoleAndPermissionByUserIdInProjectId(int userId, int projectId) {
        return toMap(queries.findRoleAndPermissionByUserIdInProjectId(userId, projectId));
    }

    public Set<Permission> findBasePermissionByUserId(int userId) {
        return toSet(queries.findBaseRoleAndPermissionByUserId(userId));
    }

    public RoleAndMetadata findRoleByName(String name) {
        return queries.findRoleByName(name);
    }

    public RoleAndMetadata findRoleInProjectByName(int projectId, String name) {
        return queries.findRoleInProjectIdByName(projectId, name);
    }

    public ProjectRoleAndPermissionFullHolder findPermissionsGroupedByProjectForUserId(int userId) {
        List<ProjectRoleAndPermission> found = queries.findPermissionsGroupedByProjectForUserId(userId);
        Map<String, Set<Permission>> res = new HashMap<>();
        Map<Integer, Set<Permission>> resById = new HashMap<>();
        for (ProjectRoleAndPermission p : found) {
            if (p.getPermission() == null) {
                continue;
            }

            if (!res.containsKey(p.getProjectShortName())) {
                res.put(p.getProjectShortName(), EnumSet.noneOf(Permission.class));
            }
            res.get(p.getProjectShortName()).add(p.getPermission());

            if (!resById.containsKey(p.getProjectId())) {
                resById.put(p.getProjectId(), EnumSet.noneOf(Permission.class));
            }
            resById.get(p.getProjectId()).add(p.getPermission());
        }
        return new ProjectRoleAndPermissionFullHolder(res, resById);
    }

    public ProjectRoleFullHolder findUserRolesByProject(int userId) {

        Set<String> globalRoles = new HashSet<>();
        for (RoleAndMetadata role : queries.findUserRoles(userId)) {
            globalRoles.add(role.getRoleName());
        }

        Map<String, Set<String>> res = new HashMap<>();
        for (RoleAndProject rp : queries.findUserRolesByProject(userId)) {
            if (!res.containsKey(rp.getProjectName())) {
                res.put(rp.getProjectName(), new HashSet<String>());
            }
            res.get(rp.getProjectName()).add(rp.getRoleName());
        }

        return new ProjectRoleFullHolder(globalRoles, res);
    }

    public Set<Permission> findPermissionByUsernameInProjectId(int userId, int projectId) {
        return toSet(queries.findRoleAndPermissionByUserIdInProjectId(userId, projectId));
    }

    public Map<String, RoleAndPermissions> findAllRolesAndRelatedPermission() {
        return toMap(queries.findAllRolesAndRelatedPermission());
    }

    public Map<String, RoleAndPermissionsWithUsers> findAllRolesAndRelatedPermissionWithUsers() {
        Map<String, RoleAndPermissionsWithUsers> res = new TreeMap<>();
        for (RoleAndPermission rap : queries.findAllRolesAndRelatedPermission()) {
            if (!res.containsKey(rap.getRoleName())) {
                res.put(rap.getRoleName(),
                        new RoleAndPermissionsWithUsers(rap, queries.findUserIdentifierByRole(rap.getRoleName())));
            }
            if (rap.getPermission() != null) {
                res.get(rap.getRoleName()).getRoleAndPermissions().add(rap);
            }
        }
        return res;
    }

    public Map<String, RoleAndPermissionsWithUsers> findAllRolesAndRelatedPermissionWithUsersInProjectId(
            int projectId) {
        Map<String, RoleAndPermissionsWithUsers> res = new TreeMap<>();
        for (RoleAndPermission rap : queries.findAllRolesAndRelatedPermissionInProjectId(projectId)) {
            if (!res.containsKey(rap.getRoleName())) {
                res.put(rap.getRoleName(), new RoleAndPermissionsWithUsers(rap,
                        queries.findUserIdentifierByRoleAndProjectId(rap.getRoleName(), projectId)));
            }
            if (rap.getPermission() != null) {
                res.get(rap.getRoleName()).getRoleAndPermissions().add(rap);
            }
        }
        return res;
    }

    public Map<String, RoleAndPermissions> findAllRolesAndRelatedPermissionInProjectId(int projectId) {
        return toMap(queries.findAllRolesAndRelatedPermissionInProjectId(projectId));
    }

    /**
     * Return 1 if created
     *
     * @param role
     * @return
     */
    @Transactional(readOnly = false)
    public int createRole(Role role) {
        return queries.createRole(Objects.requireNonNull(role).getName());
    }

    @Transactional(readOnly = false)
    public int createRoleInProjectId(Role role, int projectId) {
        return queries.createRoleInProjectId(Objects.requireNonNull(role).getName(), projectId);
    }

    @Transactional(readOnly = false)
    public int createFullRoleInProjectId(Role role, int projectId, boolean removable, boolean hidden,
            boolean readOnly) {
        return queries.createFullRoleInProjectId(Objects.requireNonNull(role).getName(), projectId, removable,
                hidden, readOnly);
    }

    @Transactional(readOnly = false)
    public void createMissingRolesWithPermissions(Map<RoleAndPermission, Set<Permission>> rolesWithPermissions) {

        Set<String> currentRoles = findAllRolesAndRelatedPermission().keySet();

        for (Entry<RoleAndPermission, Set<Permission>> kv : rolesWithPermissions.entrySet()) {
            RoleAndPermission rp = kv.getKey();
            if (!currentRoles.contains(rp.getRoleName())) {
                queries.createFullRole(rp.getRoleName(), rp.isRemovable(), rp.isHidden(), rp.isHidden());
            }
            updatePermissionsToRole(new Role(rp.getRoleName()), kv.getValue());
        }
    }

    @Transactional(readOnly = false)
    public void createMissingRolesWithPermissionForProject(int projectId,
            Map<RoleAndPermission, Set<Permission>> p) {
        createMissingRolesWithPermissionForProjects(Collections.singletonMap(projectId, p));
    }

    @Transactional(readOnly = false)
    public void createMissingRolesWithPermissionForProjects(
            Map<Integer, Map<RoleAndPermission, Set<Permission>>> r) {
        for (Entry<Integer, Map<RoleAndPermission, Set<Permission>>> projIdToRolesAndPermissions : r.entrySet()) {

            int projectId = projIdToRolesAndPermissions.getKey();

            Set<String> currentRoles = findAllRolesAndRelatedPermissionInProjectId(projectId).keySet();

            for (Entry<RoleAndPermission, Set<Permission>> kv : projIdToRolesAndPermissions.getValue().entrySet()) {
                RoleAndPermission rp = kv.getKey();
                if (!currentRoles.contains(rp.getRoleName())) {
                    createFullRoleInProjectId(new Role(rp.getRoleName()), projectId, rp.isRemovable(),
                            rp.isHidden(), rp.isReadOnly());
                }
                updatePermissionsToRoleInProjectId(new Role(rp.getRoleName()), kv.getValue(), projectId);
            }
        }
    }

    /**
     * Return 1 if deleted
     *
     * @param role
     * @return
     */
    @Transactional(readOnly = false)
    public int deleteRole(Role role) {
        Objects.requireNonNull(role);
        queries.removeUsersFromRole(role.getName());
        queries.deletePermissions(role.getName());
        return queries.deleteRole(role.getName());
    }

    @Transactional(readOnly = false)
    public int deleteRoleInProjectId(Role role, int projectId) {
        Objects.requireNonNull(role);
        queries.removeUsersFromRoleInProjectId(role.getName(), projectId);
        queries.deletePermissionsInProjectId(role.getName(), projectId);
        return queries.deleteRoleInProjectId(role.getName(), projectId);
    }

    @Transactional(readOnly = false)
    public void updatePermissionsToRole(Role role, Set<Permission> enabledPermissions) {
        Objects.requireNonNull(role);
        Objects.requireNonNull(enabledPermissions);

        // step 1: remove all permissions
        queries.deletePermissions(role.getName());
        // step 2: add the enabled permission
        jdbc.batchUpdate(queries.addPermission(), from(role, enabledPermissions));
    }

    @Transactional(readOnly = false)
    public void updatePermissionsToRoleInProjectId(Role role, Set<Permission> permissions, int projectId) {
        Objects.requireNonNull(role);
        Objects.requireNonNull(permissions);
        Permission.ensurePermissionForProject(permissions);

        // step 1: remove all permissions
        queries.deletePermissionsInProjectId(role.getName(), projectId);
        // step 2: add the enabled permission
        jdbc.batchUpdate(queries.addPermissionInProjectId(), addProjectId(from(role, permissions), projectId));
    }

    private void checkRoleCondition(String roleName, Set<Integer> usersId) {
        if ("ANONYMOUS".equals(roleName) && !usersId.isEmpty()) {
            Validate.isTrue(usersId.size() == 1);
            Validate.isTrue(userRepository.findById(usersId.iterator().next()).isAnonymous());
        }
    }

    @Transactional(readOnly = false)
    public void assignRolesToUsers(Map<Role, Set<Integer>> rolesToUsersId) {
        for (Entry<Role, Set<Integer>> roleToUsersId : rolesToUsersId.entrySet()) {
            assignRoleToUsers(roleToUsersId.getKey(), roleToUsersId.getValue());
        }
    }

    @Transactional(readOnly = false)
    public void assignRoleToUsers(Role role, Set<Integer> userIds) {
        Objects.requireNonNull(role);
        Objects.requireNonNull(userIds);

        checkRoleCondition(role.getName(), userIds);

        jdbc.batchUpdate(queries.assignRoleToUser(), fromUserIdAndRoleName(role, userIds));
    }

    @Transactional(readOnly = false)
    public void assignRoleToUsersInProjectId(Role role, Set<Integer> userIds, int projectId) {
        Objects.requireNonNull(role);
        Objects.requireNonNull(userIds);

        checkRoleCondition(role.getName(), userIds);

        jdbc.batchUpdate(queries.assignRoleToUsersInProjectId(),
                addProjectId(fromUserIdAndRoleName(role, userIds), projectId));
    }

    @Transactional(readOnly = false)
    public void removeRoleToUsers(Role role, Set<Integer> userIds) {
        Objects.requireNonNull(role);
        Objects.requireNonNull(userIds);

        checkRoleCondition(role.getName(), userIds);

        jdbc.batchUpdate(queries.removeRoleToUsers(), fromUserIdAndRoleName(role, userIds));
    }

    @Transactional(readOnly = false)
    public void removeRoleToUsersInProjectId(Role role, Set<Integer> userIds, int projectId) {
        Objects.requireNonNull(role);
        Objects.requireNonNull(userIds);

        checkRoleCondition(role.getName(), userIds);

        jdbc.batchUpdate(queries.removeRoleToUsersInProjectId(),
                addProjectId(fromUserIdAndRoleName(role, userIds), projectId));
    }

    public List<User> findUserByRole(Role role) {
        Objects.requireNonNull(role);
        return queries.findUserByRole(role.getName());
    }

    public List<User> findUserByRoleAndProjectId(Role role, int projectId) {
        Objects.requireNonNull(role);
        return queries.findUserByRoleAndProjectId(role.getName(), projectId);
    }

    private static Map<String, RoleAndPermissions> toMap(List<RoleAndPermission> l) {
        Map<String, RoleAndPermissions> res = new TreeMap<>();
        for (RoleAndPermission rap : l) {
            if (!res.containsKey(rap.getRoleName())) {
                res.put(rap.getRoleName(), new RoleAndPermissions(rap));
            }
            if (rap.getPermission() != null) {
                res.get(rap.getRoleName()).roleAndPermissions.add(rap);
            }
        }
        return res;
    }

    private static Set<Permission> toSet(List<RoleAndPermission> rp) {
        Set<Permission> permissions = EnumSet.noneOf(Permission.class);
        for (RoleAndPermission rap : rp) {
            if (rap.getPermission() != null) {
                permissions.add(rap.getPermission());
            }
        }
        return permissions;
    }

    private static MapSqlParameterSource[] fromUserIdAndRoleName(Role role, Set<Integer> userIds) {
        List<MapSqlParameterSource> ret = new ArrayList<>(userIds.size());
        for (Integer userId : userIds) {
            ret.add(new MapSqlParameterSource("userId", userId).addValue("roleName", role.getName()));
        }

        return ret.toArray(new MapSqlParameterSource[ret.size()]);
    }

    private static MapSqlParameterSource[] addProjectId(MapSqlParameterSource[] s, int projectId) {
        for (MapSqlParameterSource param : s) {
            param.addValue("projectId", projectId);
        }
        return s;
    }

    private static MapSqlParameterSource[] from(Role role, Set<Permission> l) {
        List<MapSqlParameterSource> ret = new ArrayList<>(l.size());

        for (Permission p : l) {
            ret.add(new MapSqlParameterSource("permission", p.toString()).addValue("roleName", role.getName()));
        }

        return ret.toArray(new MapSqlParameterSource[ret.size()]);
    }

    @Getter
    public static class RoleAndPermissions {
        private final String name;
        private final boolean removable;
        private final boolean hidden;
        private final boolean readOnly;
        private final List<RoleAndPermission> roleAndPermissions = new ArrayList<>();

        private RoleAndPermissions(RoleAndPermission base) {
            this.name = base.getRoleName();
            this.removable = base.isRemovable();
            this.hidden = base.isHidden();
            this.readOnly = base.isReadOnly();
        }
    }

    @Getter
    public static class RoleAndPermissionsWithUsers extends RoleAndPermissions {

        private final List<UserIdentifier> assignedUsers;

        public RoleAndPermissionsWithUsers(RoleAndPermission base, List<UserIdentifier> assignedUsers) {
            super(base);
            this.assignedUsers = assignedUsers;
        }
    }

    @Getter
    @AllArgsConstructor
    public static class ProjectRoleAndPermissionFullHolder {
        private final Map<String, Set<Permission>> permissionsByProject;
        private final Map<Integer, Set<Permission>> permissionsByProjectId;
    }

    @Getter
    @AllArgsConstructor
    public static class ProjectRoleFullHolder {
        private final Set<String> globalRoles;
        private final Map<String, Set<String>> rolesByProject;
    }
}