org.flowerplatform.web.security.service.PermissionService.java Source code

Java tutorial

Introduction

Here is the source code for org.flowerplatform.web.security.service.PermissionService.java

Source

/* license-start
 * 
 * Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>.
 * 
 * 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 version 3.
 * 
 * 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, at <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *   Crispico - Initial API and implementation
 *
 * license-end
 */
package org.flowerplatform.web.security.service;

import java.io.File;
import java.lang.reflect.Constructor;
import java.security.Permission;
import java.security.Policy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
import java.util.regex.Matcher;

import org.flowerplatform.common.CommonPlugin;
import org.flowerplatform.common.FlowerProperties.AddBooleanProperty;
import org.flowerplatform.communication.CommunicationPlugin;
import org.flowerplatform.communication.service.ServiceInvocationContext;
import org.flowerplatform.communication.service.ServiceRegistry;
import org.flowerplatform.web.database.DatabaseOperation;
import org.flowerplatform.web.database.DatabaseOperationWrapper;
import org.flowerplatform.web.entity.Entity;
import org.flowerplatform.web.entity.EntityFactory;
import org.flowerplatform.web.entity.Group;
import org.flowerplatform.web.entity.ISecurityEntity;
import org.flowerplatform.web.entity.PermissionEntity;
import org.flowerplatform.web.security.dto.PermissionAdminUIDto;
import org.flowerplatform.web.security.dto.PermissionsByResourceFilter;
import org.flowerplatform.web.security.permission.AdminSecurityEntitiesPermission;
import org.flowerplatform.web.security.permission.PermissionDescriptor;
import org.flowerplatform.web.security.sandbox.FlowerWebPolicy;
import org.flowerplatform.web.security.sandbox.SecurityEntityAdaptor;
import org.flowerplatform.web.security.sandbox.SecurityUtils;
import org.hibernate.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Service used to make CRUD operations on <code>Permission</code> entity.
 * 
 * @see BootstrapService#initialize()
 * @see ServiceRegistry
 * 
 * @author Cristi
 * @author Cristina
 * @author Mariana
     
 * 
 */
public class PermissionService {

    public static final String SERVICE_ID = "permissionService";

    private static final Logger logger = LoggerFactory.getLogger(PermissionService.class);

    private static final String SHOW_ALL_APPLICABLE_PERMISSIONS_PER_FILTERED_RESOURCE = "users.permissions.showAllApplicablePermissionsPerFilteredResource";

    static {
        CommonPlugin.getInstance().getFlowerProperties()
                .addProperty(new AddBooleanProperty(SHOW_ALL_APPLICABLE_PERMISSIONS_PER_FILTERED_RESOURCE, "true"));
    }

    public static PermissionService getInstance() {
        return (PermissionService) CommunicationPlugin.getInstance().getServiceRegistry().getService(SERVICE_ID);
    }

    /**
     * Converts a {@link Group} to {@link PermissionAdminUIDto}.
     * 
     * @see #findAllAsAdminUIDto()
     * @see #findByIdAsAdminUIDto()
     * 
     */
    private PermissionAdminUIDto convertPermissionToPermissionAdminUIDto(PermissionEntity permission) {
        PermissionAdminUIDto dto = new PermissionAdminUIDto();

        dto.setId(permission.getId());
        dto.setName(permission.getName());
        dto.setActions(permission.getActions());
        dto.setAssignedTo(permission.getAssignedTo());
        dto.setType(permission.getType());
        dto.setIsEditable(true);

        return dto;
    }

    /**
     * Finds the {@link PermissionEntity permission} given by its id and returns a {@link PermissionAdminUIDto}. 
     * 
     */
    public PermissionAdminUIDto findByIdAsAdminUIDto(final long id) {
        logger.debug("Find permission with id = {}", id);

        DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {

            @Override
            public void run() {
                PermissionEntity permission = wrapper.find(PermissionEntity.class, id);
                if (permission == null)
                    throw new RuntimeException(String.format("Permission with id=%s was not found in the DB.", id));
                wrapper.setOperationResult(convertPermissionToPermissionAdminUIDto(permission));
            }
        });
        return (PermissionAdminUIDto) wrapper.getOperationResult();
    }

    /**
     * Finds all {@link PermissionEntity}s and returns a list of their corresponding {@link PermissionAdminUIDto}.
     * 
     */
    public List<PermissionAdminUIDto> findAllAsAdminUIDto() {
        logger.debug("Find all permissions");

        final List<PermissionAdminUIDto> list = new ArrayList<PermissionAdminUIDto>();
        new DatabaseOperationWrapper(new DatabaseOperation() {

            @Override
            public void run() {
                for (PermissionEntity permission : wrapper.findAll(PermissionEntity.class)) {
                    try {
                        SecurityUtils.checkModifyTreePermission(permission);
                        if (Class.forName(permission.getType()).equals(AdminSecurityEntitiesPermission.class))
                            SecurityUtils.checkCurrentUserIsAdmin(null);
                        list.add(convertPermissionToPermissionAdminUIDto(permission));
                    } catch (Exception e) {
                    }
                }
            }
        });
        return list;
    }

    /**
     * Finds {@link PermissionEntity}s that correspond to given {@link PermissionsByResourceFilter} 
     * and returns a list of their corresponding {@link PermissionAdminUIDto}.
     * 
     */
    @SuppressWarnings("unchecked")
    public List<PermissionAdminUIDto> findAsAdminUIDtoFilterByResource(PermissionsByResourceFilter resourceFilter) {
        if (resourceFilter == null) {
            return findAllAsAdminUIDto();
        }
        final List<String> patterns = new ArrayList<String>();
        patterns.add(resourceFilter.getResource());
        // add filter by root/dir/* for root/dir
        if (!resourceFilter.getResource().endsWith("/*"))
            patterns.add(resourceFilter.getResource() + (resourceFilter.getResource().endsWith("/") ? "*" : "/*"));
        // add filter by *
        patterns.add("*");
        int fromIndex = 0;
        int index = resourceFilter.getResource().indexOf("/", fromIndex);
        while (index != -1) {
            patterns.add(resourceFilter.getResource().substring(0, index) + "/*");
            fromIndex = index + 1;
            index = resourceFilter.getResource().indexOf("/", fromIndex);
        }

        final List<PermissionAdminUIDto> listDtos = new ArrayList<PermissionAdminUIDto>();
        new DatabaseOperationWrapper(new DatabaseOperation() {

            @Override
            public void run() {
                List<PermissionEntity> permissionEntities;
                Query q = wrapper.createQuery(
                        "SELECT e FROM PermissionEntity e WHERE e.name in :names ORDER by e.type, e.name");
                q.setParameterList("names", patterns);
                q.setReadOnly(true);
                permissionEntities = q.list();

                boolean showAllApplicablePermissions = Boolean.valueOf(CommonPlugin.getInstance()
                        .getFlowerProperties().getProperty(SHOW_ALL_APPLICABLE_PERMISSIONS_PER_FILTERED_RESOURCE));

                for (PermissionEntity permission : permissionEntities) {
                    boolean isEditable = false;
                    try {
                        SecurityUtils.checkModifyTreePermission(permission);
                        // permission check did not throw a security exception => permission is editable by the current user
                        isEditable = true;
                    } catch (SecurityException e) {
                        // do nothing
                    }

                    // return this permission if it is editable OR if all applicable permissions must be displayed (even if they are not editable by the current user)
                    if (isEditable || showAllApplicablePermissions) {
                        PermissionAdminUIDto dto = convertPermissionToPermissionAdminUIDto(permission);
                        dto.setIsEditable(isEditable);
                        listDtos.add(dto);
                    }
                }
            }
        });
        return listDtos;
    }

    /**
     * Validates the information found in {@link PermissionAdminUIDto dto} and
     * creates/updates the {@link PermissionEntity}.
     * 
     * <p>
     * The validation with return messages to client if data is invalid.
     * 
     * <p>
     * Also return a special value in the map if the permission is a tree permission
     * and the resource is a folder. The client will then ask the user if similar
     * permissions (e.g. for root/org1 -> root/org1/*) should be modified as well.
     * 
     * @see #validDto()
     * 
     */
    @SuppressWarnings("unchecked")
    public Map<String, String> mergeAdminUIDto(ServiceInvocationContext context, final PermissionAdminUIDto dto) {
        logger.debug("Merge permission = {}", dto.getName());

        DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {

            @Override
            public void run() {
                PermissionEntity permissionEntity;
                if (dto.getId() == 0) {
                    permissionEntity = EntityFactory.eINSTANCE.createPermissionEntity();
                } else {
                    permissionEntity = wrapper.find(PermissionEntity.class, (long) dto.getId());
                    SecurityUtils.checkModifyTreePermission(permissionEntity);
                }
                try {
                    if (Class.forName(dto.getType()).equals(AdminSecurityEntitiesPermission.class)) {
                        SecurityUtils.checkCurrentUserIsAdmin(
                                String.format("Current user can not modify %s because he is not admin.",
                                        AdminSecurityEntitiesPermission.class.getSimpleName()));
                    }
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }

                permissionEntity.setName(dto.getName());
                permissionEntity.setAssignedTo(dto.getAssignedTo());
                permissionEntity.setActions(dto.getActions());
                permissionEntity.setType(dto.getType());

                PermissionDescriptor descriptor = ((FlowerWebPolicy) Policy.getPolicy())
                        .getPermissionDescriptor(permissionEntity.getType());
                Map<String, String> validationResults = descriptor.validate(createPermission(permissionEntity));
                String assignedTo = permissionEntity.getAssignedTo();
                if (!assignedTo.equals(PermissionEntity.ANY_ENTITY)) {
                    String message = SecurityUtils.validateSecurityEntity(assignedTo);
                    if (message != null) {
                        validationResults.put(PermissionDescriptor.ASSIGNED_TO_FIELD, message);
                    }
                }

                if (!validationResults.isEmpty()) {
                    wrapper.setOperationResult(validationResults);
                    return;
                }
                SecurityUtils.checkModifyTreePermission(permissionEntity);

                Map<String, String> rslt = new HashMap<String, String>();

                // check if tree permission on folder
                if (descriptor.isTreePermission()) {
                    if (isFolder(permissionEntity.getName())) {
                        rslt.put("modifySimilarPermission", "");
                    }
                }

                wrapper.merge(permissionEntity);
                wrapper.setOperationResult(rslt);
            }
        });
        return (Map<String, String>) wrapper.getOperationResult();
    }

    /**
     * Deletes all {@link PermissionEntity}s based on the list of their ids.
     * 
     * <p>
     * Returns a list of boolean values: for each deleted permission, return
     * <code>true</code> if the permission is a tree permission and the resource
     * is a folder. The client will then ask the user if similar permissions 
     * (e.g. for root/org1 -> root/org1/*) should be deleted as well.
     * 
     * 
     */
    public List<Boolean> delete(final List<Integer> ids) {
        final List<Boolean> result = new ArrayList<Boolean>();
        new DatabaseOperationWrapper(new DatabaseOperation() {

            @Override
            public void run() {
                for (Integer id : ids) {
                    PermissionEntity permissionEntity = wrapper.find(PermissionEntity.class, Long.valueOf(id));
                    SecurityUtils.checkModifyTreePermission(permissionEntity);
                    try {
                        if (Class.forName(permissionEntity.getType())
                                .equals(AdminSecurityEntitiesPermission.class)) {
                            SecurityUtils.checkCurrentUserIsAdmin(
                                    String.format("Current user can not delete %s because he is not admin.",
                                            AdminSecurityEntitiesPermission.class.getSimpleName()));
                        }
                        PermissionDescriptor descriptor = ((FlowerWebPolicy) Policy.getPolicy())
                                .getPermissionDescriptor(permissionEntity.getType());
                        if (descriptor.isTreePermission()) {
                            result.add(isFolder(permissionEntity.getName()));
                        }
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }

                    logger.debug("Delete {}", permissionEntity);

                    wrapper.delete(permissionEntity);
                }
            }
        });

        return result;
    }

    private boolean isFolder(String path) {
        File file = new File(CommonPlugin.getInstance().getWorkspaceRoot(), path.split("/\\*")[0]);
        return file.isDirectory() && file.exists();
        //      
        //      path = path.split("/\\*")[0];
        //      IResource resource;
        //      try {
        //         resource = ResourcesPlugin.getWorkspace().getRoot().getFolder(new Path(path));
        //      } catch (IllegalArgumentException e) {
        //         return false;
        //      }
        //      return resource.exists();
    }

    @SuppressWarnings("unchecked")
    public List<PermissionEntity> findPermissionsByType(final String type) {
        DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {

            @Override
            public void run() {
                Query q = wrapper
                        .createQuery("SELECT p FROM PermissionEntity p WHERE p.type = :type ORDER BY p.name");
                q.setParameter("type", type);
                wrapper.setOperationResult(q.list());
            }
        });
        return (List<PermissionEntity>) wrapper.getOperationResult();
    }

    public void onSecurityEntityDelete(final ISecurityEntity securityEntity) {
        new DatabaseOperationWrapper(new DatabaseOperation() {

            @Override
            public void run() {
                // delete permissions assigned to deleted entity
                String assignedTo = SecurityEntityAdaptor.getAssignedTo(securityEntity);
                Query query = wrapper
                        .createQuery("DELETE FROM PermissionEntity p WHERE p.assignedTo = :assigned_to");
                query.setParameter("assigned_to", assignedTo);
                query.executeUpdate();

                // delete/edit permissions where actions contain deleted entity
                String actions = SecurityEntityAdaptor.getAssignedTo(securityEntity);
                Query q = wrapper.createQuery("SELECT p FROM PermissionEntity p WHERE p.actions LIKE :actions");
                q.setParameter("actions", "%" + actions + "%");
                @SuppressWarnings("unchecked")
                List<PermissionEntity> list = q.list();

                for (PermissionEntity permission : list) {
                    if (permission.getActions().equals(actions)) {
                        // delete permission
                        wrapper.delete(permission);
                    } else {
                        // update actions
                        List<ISecurityEntity> assignableEntities = SecurityEntityAdaptor
                                .csvStringToSecurityEntityList(permission.getActions(), true);
                        for (Iterator<ISecurityEntity> it = assignableEntities.iterator(); it.hasNext();) {
                            Entity entity = (Entity) it.next();
                            if (entity != null) {
                                if (entity.getId() == ((Entity) securityEntity).getId()) {
                                    it.remove();
                                    break;
                                }
                            } else {
                                it.remove();
                            }
                        }
                        Set<String> names = new HashSet<String>();
                        for (ISecurityEntity entity : assignableEntities) {
                            names.add(SecurityEntityAdaptor.getAssignedTo(entity));
                        }
                        permission.setActions(SecurityEntityAdaptor.toCsvString(names));

                        wrapper.merge(permission);
                    }
                }
            }
        });

    }

    public void onSecurityEntityUpdate(final ISecurityEntity initialEntity, final ISecurityEntity newEntity) {
        new DatabaseOperationWrapper(new DatabaseOperation() {

            @Override
            public void run() {
                String initialAssignedTo = SecurityEntityAdaptor.getAssignedTo(initialEntity);
                String newAssignedTo = SecurityEntityAdaptor.getAssignedTo(newEntity);

                if (!initialAssignedTo.equals(newAssignedTo)) {
                    // update permissions assigned to entity
                    Query query = wrapper.createQuery("UPDATE PermissionEntity "
                            + "SET assignedTo = :newAssignedTo " + "WHERE assignedTo = :initialAssignedTo");
                    query.setParameter("newAssignedTo", newAssignedTo);
                    query.setParameter("initialAssignedTo", initialAssignedTo);
                    query.executeUpdate();

                    // update permissions where actions contain entity
                    Query q = wrapper.createQuery("SELECT p FROM PermissionEntity p WHERE p.actions LIKE :actions");
                    q.setParameter("actions", "%" + initialAssignedTo + "%");
                    @SuppressWarnings("unchecked")
                    List<PermissionEntity> list = q.list();

                    for (PermissionEntity permission : list) {
                        permission.setActions(
                                permission.getActions().replaceAll(Matcher.quoteReplacement(initialAssignedTo),
                                        Matcher.quoteReplacement(newAssignedTo)));
                        wrapper.merge(permission);
                    }
                }
            }
        });
    }

    public List<PermissionDescriptor> getPermissionDescriptors() {
        FlowerWebPolicy policy = (FlowerWebPolicy) Policy.getPolicy();
        return policy.getPermissionDescriptors();
    }

    /**
     * Instantiates a {@link Permission} from the record saved in the database,
     * i.e. in the {@link org.flowerplatform.web.entity.PermissionEntity}.
     * 
     */
    @SuppressWarnings("unchecked")
    public Permission createPermission(org.flowerplatform.web.entity.PermissionEntity permission) {
        Permission instance = null;
        try {
            Class<? extends Permission> permissionClass = (Class<? extends Permission>) Class
                    .forName(permission.getType());
            Constructor<? extends Permission> constructor = permissionClass.getConstructor(String.class,
                    String.class);
            instance = constructor.newInstance(permission.getName(), permission.getActions());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return instance;
    }

    private Observer securityEntityObserver = new Observer() {
        @Override
        public void update(Observable o, Object arg) {
            List<?> list = (List<?>) arg;
            if (list.get(0).equals(ServiceObservable.DELETE) && list.get(1) instanceof ISecurityEntity) {
                ISecurityEntity securityEntity = (ISecurityEntity) list.get(1);
                onSecurityEntityDelete(securityEntity);
            } else {
                if (list.get(0).equals(ServiceObservable.UPDATE) && list.get(1) instanceof ISecurityEntity
                        && list.get(2) instanceof ISecurityEntity) {
                    onSecurityEntityUpdate((ISecurityEntity) list.get(1), (ISecurityEntity) list.get(2));
                }
            }
        }
    };

    public Observer getSecurityEntityObserver() {
        return securityEntityObserver;
    }
}