nl.strohalm.cyclos.services.access.AbstractPermissionCheck.java Source code

Java tutorial

Introduction

Here is the source code for nl.strohalm.cyclos.services.access.AbstractPermissionCheck.java

Source

/*
This file is part of Cyclos (www.cyclos.org).
A project of the Social Trade Organisation (www.socialtrade.org).
    
Cyclos 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 2 of the License, or
(at your option) any later version.
    
Cyclos 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 Cyclos; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    
 */
package nl.strohalm.cyclos.services.access;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import nl.strohalm.cyclos.access.AdminPermission;
import nl.strohalm.cyclos.access.BasicPermission;
import nl.strohalm.cyclos.access.BrokerPermission;
import nl.strohalm.cyclos.access.MemberPermission;
import nl.strohalm.cyclos.access.ModuleType;
import nl.strohalm.cyclos.access.OperatorPermission;
import nl.strohalm.cyclos.access.Permission;
import nl.strohalm.cyclos.access.PermissionCheck;
import nl.strohalm.cyclos.entities.Entity;
import nl.strohalm.cyclos.entities.groups.Group;
import nl.strohalm.cyclos.exceptions.PermissionDeniedException;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Abstract implementation for {@link PermissionCheck}
 * 
 * @author luis
 */
public abstract class AbstractPermissionCheck implements PermissionCheck {

    public static class RequiredValuesBean {
        private final Entity[] entities;
        private final Permission permission;

        public RequiredValuesBean(final Permission permission, final Entity[] entities) {
            this.permission = permission;
            this.entities = entities;
        }

        public Entity[] getEntities() {
            return entities;
        }

        public Permission getPermission() {
            return permission;
        }
    }

    private static final Log LOG = LogFactory.getLog(AbstractPermissionCheck.class);

    private static boolean trace;
    static {
        trace = Boolean.getBoolean("cyclos.tracePermissionChecks");
    }

    /* Map from multivalued permissions to entities. All entities mapped to a permission must be contained in the allowed permission's values */
    protected Map<Permission, RequiredValuesBean> requiredValuesMap;

    protected AdminPermission[] adminPermissions;
    protected BrokerPermission[] brokerPermissions;
    protected MemberPermission[] memberPermissions;
    protected OperatorPermission[] operatorPermissions;
    protected MemberPermission[] operatorMemberPermissions;
    protected BasicPermission[] basicPermissions;
    private boolean permissionChecked;
    private StackTraceElement[] stackTrace;

    public AbstractPermissionCheck() {
        if (trace) {
            stackTrace = Thread.currentThread().getStackTrace();
        }
    }

    @Override
    public PermissionCheck admin(final AdminPermission... permissions) {
        adminPermissions = (AdminPermission[]) (adminPermissions == null ? permissions
                : ArrayUtils.addAll(adminPermissions, permissions));
        return this;
    }

    @Override
    public PermissionCheck adminFor(final AdminPermission permission, final Entity... entities) {
        checkMultivaluedPermission(permission, null);

        admin(new AdminPermission[] { permission });

        addRequiredValues(permission, permission, entities);

        return this;
    }

    @Override
    public PermissionCheck basic(final BasicPermission... permissions) {
        basicPermissions = (BasicPermission[]) (basicPermissions == null ? permissions
                : ArrayUtils.addAll(basicPermissions, permissions));
        return this;
    }

    @Override
    public PermissionCheck broker(final BrokerPermission... permissions) {
        brokerPermissions = (BrokerPermission[]) (brokerPermissions == null ? permissions
                : ArrayUtils.addAll(brokerPermissions, permissions));
        return this;
    }

    @Override
    public PermissionCheck brokerFor(final BrokerPermission permission, final Entity... entities) {
        checkMultivaluedPermission(permission, null);

        broker(new BrokerPermission[] { permission });

        addRequiredValues(permission, permission, entities);

        return this;
    }

    @Override
    public void check() throws PermissionDeniedException {
        if (!hasPermission()) {
            throw new PermissionDeniedException();
        }
    }

    @Override
    public final boolean hasPermission() {
        permissionChecked = true;
        return doHasPermission();
    }

    @Override
    public PermissionCheck member(final MemberPermission... permissions) {
        memberPermissions = (MemberPermission[]) (memberPermissions == null ? permissions
                : ArrayUtils.addAll(memberPermissions, permissions));
        return this;
    }

    @Override
    public PermissionCheck memberFor(final MemberPermission permission, final Entity... entities) {
        checkMultivaluedPermission(permission, null);

        member(new MemberPermission[] { permission });

        addRequiredValues(permission, permission, entities);

        return this;
    }

    @Override
    public PermissionCheck operator() {
        if (operatorPermissions == null) {
            // Just ensure there is an array set in operatorPermissions
            operatorPermissions = new OperatorPermission[0];
        }
        return this;
    }

    @Override
    public PermissionCheck operator(final MemberPermission... permissions) {
        operatorMemberPermissions = (MemberPermission[]) (operatorMemberPermissions == null ? permissions
                : ArrayUtils.addAll(operatorMemberPermissions, permissions));
        return this;
    }

    @Override
    public PermissionCheck operator(final OperatorPermission... permissions) {
        operatorPermissions = (OperatorPermission[]) (operatorPermissions == null ? permissions
                : ArrayUtils.addAll(operatorPermissions, permissions));
        return this;
    }

    @Override
    public PermissionCheck operatorFor(final MemberPermission permission, final Entity... entities) {
        checkMultivaluedPermission(permission, null);

        operator(new MemberPermission[] { permission });

        addRequiredValues(permission, permission, entities);

        return this;
    }

    @Override
    public PermissionCheck operatorFor(final OperatorPermission permission, final Entity... entities) {
        checkMultivaluedPermission(permission, null);

        operator(new OperatorPermission[] { permission });

        addRequiredValues(permission, permission, entities);

        return this;
    }

    @Override
    public PermissionCheck operatorFor(final OperatorPermission permission, final MemberPermission parentPermission,
            final Entity... entities) {
        checkMultivaluedPermission(permission, parentPermission);

        operator(new OperatorPermission[] { permission });

        addRequiredValues(permission, parentPermission, entities);

        return this;
    }

    protected abstract boolean doHasPermission();

    @Override
    protected void finalize() throws Throwable {
        if (!permissionChecked) {
            if (stackTrace == null) {
                LOG.warn(
                        "PermissionCheck object created without actually checking permission. Did you forget a call to check() or hasPermission()? Set the -Dcyclos.tracePermissionChecks=true system argument to view where this permission object was created");
            } else {
                Exception ex = new Exception();
                ex.setStackTrace(stackTrace);
                LOG.warn(
                        "PermissionCheck object created without actually checking permission. Did you forget a call to check() or hasPermission()?",
                        ex);
            }
        }
    }

    protected List<Permission> getPermissions(final Group.Nature groupNature, final ModuleType onlyOfType) {
        // Get the raw list of permissions
        List<Permission> permissions = null;
        if (groupNature != null) {
            switch (groupNature) {
            case ADMIN:
                permissions = join(basicPermissions, adminPermissions);
                break;
            case BROKER:
                if (onlyOfType == ModuleType.MEMBER) {
                    permissions = join(basicPermissions, memberPermissions);
                } else {
                    permissions = join(basicPermissions, memberPermissions, brokerPermissions);
                }
                break;
            case MEMBER:
                permissions = join(basicPermissions, memberPermissions);
                break;
            case OPERATOR:
                permissions = join(basicPermissions, operatorPermissions, operatorMemberPermissions);
                break;
            }
        }
        boolean initiallyEmpty = permissions != null && permissions.isEmpty();

        // Apply the filter
        if (permissions != null && onlyOfType != null) {
            for (Iterator<Permission> iterator = permissions.iterator(); iterator.hasNext();) {
                Permission permission = iterator.next();
                if (permission.getModule().getType() != onlyOfType) {
                    iterator.remove();
                }
            }
            // None of the permissions matched the expected type. Return null, which will make the permission check fail, as initially,
            // permissions were not empty (which happens when we just want someone to have a role, not an specific permission)
            if (permissions.isEmpty() && !initiallyEmpty) {
                permissions = null;
            }
        }
        return permissions;
    }

    private void addRequiredValues(final Permission key, final Permission multivaluedPermission,
            final Entity... entities) {
        if (requiredValuesMap == null) {
            requiredValuesMap = new HashMap<Permission, RequiredValuesBean>();
        } else if (requiredValuesMap.containsKey(key)) {
            throw new IllegalArgumentException(
                    String.format("Permission (%1$s) already added to the allowed values map", key));
        }

        requiredValuesMap.put(key, new RequiredValuesBean(multivaluedPermission, entities));
    }

    /**
     * Checks the multivalued permission<br>
     * @param permission the multivalued permission or a boolean operator permission with a multivalued parent permission.
     * @param parentPermission not null only if permission is an operator permission.
     * @throws IllegalArgumentException if the specified permission doesn't allow get the related relationship from which retrieve the allowed values
     * or if it was already added.
     */
    private void checkMultivaluedPermission(final Permission permission, final MemberPermission parentPermission) {
        // we allow operator permissions without a relationship only if its parent has
        if (permission.relationship() == null && (!(permission instanceof OperatorPermission)
                || parentPermission == null || parentPermission.relationship() == null)) {
            throw new IllegalArgumentException(String.format(
                    "Invalid permission: %1$s.%2$s. The permission (or its parent if any) must has a relationship to allow ensuring entity membership",
                    permission.getClass().getSimpleName(), permission));
        }
    }

    private List<Permission> join(final Permission[]... permissions) {
        List<Permission> result = new ArrayList<Permission>();
        boolean hasNonNull = false;
        for (Permission[] current : permissions) {
            if (current != null) {
                hasNonNull = true;
                CollectionUtils.addAll(result, current);
            }
        }
        if (!hasNonNull) {
            return null;
        }
        return result;
    }

}