eu.europeana.aas.acl.CassandraAclService.java Source code

Java tutorial

Introduction

Here is the source code for eu.europeana.aas.acl.CassandraAclService.java

Source

/* Copyright 2013 Rigas Grigoropoulos
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package eu.europeana.aas.acl;

import eu.europeana.aas.acl.model.AclEntry;
import eu.europeana.aas.acl.model.AclObjectIdentity;
import eu.europeana.aas.acl.repository.AclRepository;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.acls.domain.*;
import org.springframework.security.acls.model.*;
import org.springframework.security.util.FieldUtils;
import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.util.*;
import java.util.Map.Entry;

/**
 * Implementation of {@link AclService} using the {@link AclRepository} to
 * access ACLs stored in Cassandra.
 *
 * @author Rigas Grigoropoulos
 *
 */
public class CassandraAclService implements AclService {

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

    protected final AclRepository aclRepository;
    protected final AclCache aclCache;
    private final PermissionFactory permissionFactory;
    private final AclAuthorizationStrategy aclAuthorizationStrategy;
    private final PermissionGrantingStrategy grantingStrategy;

    private final Field fieldAces = FieldUtils.getField(AclImpl.class, "aces");

    /**
     * Constructs a new <code>CassandraAclService</code> object.
     *
     * @param aclRepository the {@link AclRepository} to use for access to the
     * database.
     * @param aclCache the {@link AclCache} to use (can be <code>null</code>).
     * @param grantingStrategy the {@link PermissionGrantingStrategy} to use
     * when creating {@link Acl} objects.
     * @param aclAuthorizationStrategy the {@link AclAuthorizationStrategy} to
     * use when creating {@link Acl} objects.
     * @param permissionFactory the {@link PermissionFactory} to use when
     * creating {@link AccessControlEntry} objects.
     */
    public CassandraAclService(AclRepository aclRepository, AclCache aclCache,
            PermissionGrantingStrategy grantingStrategy, AclAuthorizationStrategy aclAuthorizationStrategy,
            PermissionFactory permissionFactory) {
        this.aclRepository = aclRepository;
        this.aclCache = aclCache;
        this.grantingStrategy = grantingStrategy;
        this.aclAuthorizationStrategy = aclAuthorizationStrategy;
        this.permissionFactory = permissionFactory;
        this.fieldAces.setAccessible(true);
    }

    @Override
    public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
        Assert.notNull(parentIdentity, "Object to lookup required");

        if (LOG.isDebugEnabled()) {
            LOG.debug("BEGIN findChildren: parentIdentity: " + parentIdentity);
        }

        List<ObjectIdentity> result = null;
        List<AclObjectIdentity> children = aclRepository
                .findAclObjectIdentityChildren(new AclObjectIdentity(parentIdentity));
        if (children != null && !children.isEmpty()) {
            result = new ArrayList<>();
            for (AclObjectIdentity entry : children) {
                result.add(entry.toObjectIdentity());
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("END findChildren: children: " + result);
        }
        return result;
    }

    @Override
    public Acl readAclById(ObjectIdentity object) throws NotFoundException {
        return readAclById(object, null);
    }

    @Override
    public Acl readAclById(ObjectIdentity object, List<Sid> sids) throws NotFoundException {
        Map<ObjectIdentity, Acl> map = readAclsById(Arrays.asList(object), sids);
        Assert.isTrue(map.containsKey(object), "There should have been an Acl entry for ObjectIdentity " + object);
        return (Acl) map.get(object);
    }

    @Override
    public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects) throws NotFoundException {
        return readAclsById(objects, null);
    }

    @Override
    public Map<ObjectIdentity, Acl> readAclsById(List<ObjectIdentity> objects, List<Sid> sids)
            throws NotFoundException {
        Assert.notEmpty(objects, "Objects to lookup required");

        if (LOG.isDebugEnabled()) {
            LOG.debug("BEGIN readAclById: objectIdentities: " + objects + ", sids: " + sids);
        }

        // contains FULLY loaded Acl objects
        Map<ObjectIdentity, Acl> result = new HashMap<>();
        List<ObjectIdentity> objectsToLookup = new ArrayList<>(objects);

        // Check for Acls in the cache
        if (aclCache != null) {
            for (ObjectIdentity oi : objects) {
                boolean aclLoaded = false;

                Acl acl = aclCache.getFromCache(oi);
                if (acl != null && acl.isSidLoaded(sids)) {
                    // Ensure any cached element supports all the requested SIDs
                    result.put(oi, acl);
                    aclLoaded = true;
                }
                if (aclLoaded) {
                    objectsToLookup.remove(oi);
                }
            }
        }

        if (!objectsToLookup.isEmpty()) {
            Map<ObjectIdentity, Acl> loadedAcls = doLookup(objectsToLookup);
            result.putAll(loadedAcls);

            // Put loaded Acls in the cache
            if (aclCache != null) {
                for (Acl loadedAcl : loadedAcls.values()) {
                    aclCache.putInCache((AclImpl) loadedAcl);
                }
            }
        }

        for (ObjectIdentity oid : objects) {
            if (!result.containsKey(oid)) {
                throw new NotFoundException("Unable to find ACL information for object identity '" + oid + "'");
            }
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("END readAclById: acls: " + result.values());
        }
        return result;
    }

    /**
     * Request Acls from the {@link AclRepository} and convert results.
     *
     * @param objects a list of {@link ObjectIdentity} objects to lookup.
     * @return a map with {@link ObjectIdentity} instances as keys and
     * {@link Acl} instances as values.
     */
    private Map<ObjectIdentity, Acl> doLookup(List<ObjectIdentity> objects) {
        Map<ObjectIdentity, Acl> result = new HashMap<>();

        if (objects != null && !objects.isEmpty()) {
            List<AclObjectIdentity> objectIds = new ArrayList<>();

            for (ObjectIdentity objId : objects) {
                objectIds.add(new AclObjectIdentity(objId));
            }

            Map<AclObjectIdentity, Set<AclEntry>> aeList = aclRepository.findAcls(objectIds);
            Map<ObjectIdentity, Acl> parentAcls = lookupParents(aeList.keySet());

            for (Entry<AclObjectIdentity, Set<AclEntry>> entry : aeList.entrySet()) {
                Acl parentAcl = parentAcls.get(entry.getKey().getParentObjectIdentity());
                AclImpl loadedAcl = convert(entry.getKey(), entry.getValue(), parentAcl);
                result.put(loadedAcl.getObjectIdentity(), loadedAcl);
            }
        }
        return result;
    }

    /**
     * Finds the parents of the provided {@link ObjectIdentity} objects.
     *
     * @param acls a set of {@link AclObjectIdentity} objects to find the
     * parents of.
     * @return a map of the parents, with {@link ObjectIdentity} instances as
     * keys and {@link Acl} instances as values.
     */
    private Map<ObjectIdentity, Acl> lookupParents(Set<AclObjectIdentity> acls) {
        List<ObjectIdentity> objectsToLookup = new ArrayList<>();
        for (AclObjectIdentity aoi : acls) {
            if (aoi.getParentObjectId() != null && !aoi.getParentObjectId().isEmpty()
                    && aoi.getParentObjectClass() != null && !aoi.getParentObjectClass().isEmpty()) {
                objectsToLookup.add(new ObjectIdentityImpl(aoi.getParentObjectClass(), aoi.getParentObjectId()));
            }
        }
        return doLookup(objectsToLookup);
    }

    /**
     * Creates an {@link AclImpl} instance out of the provided data.
     *
     * @param aclObjectIdentity the {@link AclObjectIdentity} holding the basic
     * Acl data.
     * @param aclEntries a set of {@link AclEntry} objects to be converted to
     * {@link AccessControlEntry} objects.
     * @param parentAcl the parent {@link Acl}.
     * @return an {@link AclImpl} instance.
     */
    private AclImpl convert(AclObjectIdentity aclObjectIdentity, Set<AclEntry> aclEntries, Acl parentAcl) {
        AclImpl acl = new AclImpl(aclObjectIdentity.toObjectIdentity(), aclObjectIdentity.getId(),
                aclAuthorizationStrategy, grantingStrategy, parentAcl, null,
                aclObjectIdentity.isEntriesInheriting(), aclObjectIdentity.getOwnerSid());

        List<AccessControlEntry> aces = new ArrayList<>(aclEntries.size());
        for (AclEntry entry : aclEntries) {
            AccessControlEntry ace = new AccessControlEntryImpl(entry.getId(), acl, entry.getSidObject(),
                    permissionFactory.buildFromMask(entry.getMask()), entry.isGranting(), entry.isAuditSuccess(),
                    entry.isAuditFailure());
            aces.add(entry.getOrder(), ace);
        }

        try {
            fieldAces.set(acl, aces);
        } catch (Exception e) {
            LOG.error("Could not set AccessControlEntries in the ACL", e);
        }
        return acl;
    }
}