com.haulmont.cuba.core.sys.PersistenceSecurityImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.core.sys.PersistenceSecurityImpl.java

Source

/*
 * Copyright (c) 2008-2016 Haulmont.
 *
 * 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 com.haulmont.cuba.core.sys;

import com.google.common.collect.Multimap;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.cuba.core.EntityManager;
import com.haulmont.cuba.core.Persistence;
import com.haulmont.cuba.core.PersistenceSecurity;
import com.haulmont.cuba.core.Query;
import com.haulmont.cuba.core.app.AttributeSecuritySupport;
import com.haulmont.cuba.core.entity.BaseEntityInternalAccess;
import com.haulmont.cuba.core.entity.BaseGenericIdEntity;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.sys.jpql.JpqlSyntaxException;
import com.haulmont.cuba.security.entity.ConstraintOperationType;
import com.haulmont.cuba.security.global.ConstraintData;
import com.haulmont.cuba.security.global.UserSession;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.io.Serializable;
import java.util.*;

import static java.lang.String.format;

@PerformanceLog
public class PersistenceSecurityImpl extends SecurityImpl implements PersistenceSecurity {
    private final Logger log = LoggerFactory.getLogger(PersistenceSecurityImpl.class);

    @Inject
    protected SecurityTokenManager securityTokenManager;

    @Inject
    protected Configuration configuration;

    @Inject
    protected Persistence persistence;

    @Inject
    protected ReferenceToEntitySupport referenceToEntitySupport;

    @Inject
    protected AttributeSecuritySupport attributeSecuritySupport;

    @Inject
    protected EntityStates entityStates;

    @Override
    public boolean applyConstraints(Query query) {
        QueryParser parser = QueryTransformerFactory.createParser(query.getQueryString());
        String entityName = parser.getEntityName();

        List<ConstraintData> constraints = getConstraints(metadata.getClassNN(entityName),
                constraint -> constraint.getCheckType().database()
                        && (constraint.getOperationType() == ConstraintOperationType.READ
                                || constraint.getOperationType() == ConstraintOperationType.ALL));

        if (constraints.isEmpty())
            return false;

        QueryTransformer transformer = QueryTransformerFactory.createTransformer(query.getQueryString());

        for (ConstraintData constraint : constraints) {
            processConstraint(transformer, constraint, entityName);
        }
        query.setQueryString(transformer.getResult());

        for (String paramName : transformer.getAddedParams()) {
            setQueryParam(query, paramName);
        }
        return true;
    }

    @Override
    public void setQueryParam(Query query, String paramName) {
        if (paramName.startsWith(CONSTRAINT_PARAM_SESSION_ATTR)) {
            UserSession userSession = userSessionSource.getUserSession();

            String attrName = paramName.substring(CONSTRAINT_PARAM_SESSION_ATTR.length());

            if (CONSTRAINT_PARAM_USER_LOGIN.equals(attrName)) {
                String userLogin = userSession.getSubstitutedUser() != null
                        ? userSession.getSubstitutedUser().getLogin()
                        : userSession.getUser().getLogin();
                query.setParameter(paramName, userLogin);

            } else if (CONSTRAINT_PARAM_USER_ID.equals(attrName)) {
                UUID userId = userSession.getSubstitutedUser() != null ? userSession.getSubstitutedUser().getId()
                        : userSession.getUser().getId();
                query.setParameter(paramName, userId);

            } else if (CONSTRAINT_PARAM_USER_GROUP_ID.equals(attrName)) {
                Object groupId = userSession.getSubstitutedUser() == null ? userSession.getUser().getGroup().getId()
                        : userSession.getSubstitutedUser().getGroup().getId();
                query.setParameter(paramName, groupId);

            } else {
                Serializable value = userSession.getAttribute(attrName);
                query.setParameter(paramName, value);
            }
        }
    }

    @Override
    public boolean filterByConstraints(Collection<Entity> entities) {
        boolean filtered = false;
        for (Iterator<Entity> iterator = entities.iterator(); iterator.hasNext();) {
            Entity entity = iterator.next();
            if (!isPermittedInMemory(entity)) {
                //we ignore situations when the collection is immutable
                iterator.remove();
                filtered = true;
            }
        }
        return filtered;
    }

    @Override
    public boolean filterByConstraints(Entity entity) {
        return !isPermittedInMemory(entity);
    }

    @Override
    public void applyConstraints(Collection<Entity> entities) {
        Set<EntityId> handled = new LinkedHashSet<>();
        entities.forEach(entity -> applyConstraints(entity, handled));
    }

    @Override
    public void applyConstraints(Entity entity) {
        applyConstraints(entity, new HashSet<>());
    }

    @Override
    public void calculateFilteredData(Entity entity) {
        calculateFilteredData(entity, new HashSet<>(), false);
    }

    @Override
    public void calculateFilteredData(Collection<Entity> entities) {
        Set<EntityId> handled = new LinkedHashSet<>();
        entities.forEach(entity -> calculateFilteredData(entity, handled, false));
    }

    @Override
    public void restoreSecurityState(Entity entity) {
        securityTokenManager.readSecurityToken(entity);
    }

    @Override
    @SuppressWarnings("unchecked")
    public void restoreFilteredData(Entity entity) {
        MetaClass metaClass = metadata.getClassNN(entity.getClass());
        String storeName = metadataTools.getStoreName(metaClass);
        EntityManager entityManager = persistence.getEntityManager(storeName);

        Multimap<String, Object> filtered = BaseEntityInternalAccess.getFilteredData(entity);
        if (filtered == null) {
            return;
        }

        for (Map.Entry<String, Collection<Object>> entry : filtered.asMap().entrySet()) {
            MetaProperty property = metaClass.getPropertyNN(entry.getKey());
            Collection filteredIds = entry.getValue();

            if (property.getRange().isClass() && CollectionUtils.isNotEmpty(filteredIds)) {
                Class entityClass = property.getRange().asClass().getJavaClass();
                Class propertyClass = property.getJavaType();
                if (Collection.class.isAssignableFrom(propertyClass)) {
                    Collection currentCollection = entity.getValue(property.getName());
                    if (currentCollection == null) {
                        throw new RowLevelSecurityException(format(
                                "Could not restore an object to currentValue because it is null [%s]. Entity [%s].",
                                property.getName(), metaClass.getName()), metaClass.getName());
                    }

                    for (Object entityId : filteredIds) {
                        Entity reference = entityManager.getReference((Class<Entity>) entityClass, entityId);
                        //we ignore situations when the currentValue is immutable
                        currentCollection.add(reference);
                    }
                } else if (Entity.class.isAssignableFrom(propertyClass)) {
                    Object entityId = filteredIds.iterator().next();
                    Entity reference = entityManager.getReference((Class<Entity>) entityClass, entityId);
                    //we ignore the situation when the field is read-only
                    entity.setValue(property.getName(), reference);
                }
            }
        }
    }

    @Override
    public void checkSecurityToken(Entity entity, View view) {
        if (BaseEntityInternalAccess.getSecurityToken(entity) == null) {
            MetaClass metaClass = metadata.getClassNN(entity.getClass());
            for (MetaProperty metaProperty : metaClass.getProperties()) {
                if (metaProperty.getRange().isClass() && metadataTools.isPersistent(metaProperty)) {
                    if (entityStates.isDetached(entity) && !entityStates.isLoaded(entity, metaProperty.getName())) {
                        continue;
                    } else if (view != null && !view.containsProperty(metaProperty.getName())) {
                        continue;
                    }
                    List<ConstraintData> existingConstraints = getConstraints(metaProperty.getRange().asClass(),
                            constraint -> constraint.getCheckType().memory());
                    if (CollectionUtils.isNotEmpty(existingConstraints)) {
                        throw new RowLevelSecurityException(format(
                                "Could not read security token from entity %s, "
                                        + "even though there are active constraints for the related entities.",
                                entity), entity.getMetaClass().getName());
                    }
                }
            }
            if (attributeSecuritySupport.isAttributeAccessEnabled(metaClass)) {
                throw new RowLevelSecurityException(
                        format("Could not read security token from entity %s, "
                                + "even though there are active attribute access for the entity.", entity),
                        entity.getMetaClass().getName());
            }
        }
    }

    protected void processConstraint(QueryTransformer transformer, ConstraintData constraint, String entityName) {
        String join = constraint.getJoin();
        String where = constraint.getWhereClause();
        try {
            if (StringUtils.isBlank(join)) {
                if (!StringUtils.isBlank(where)) {
                    transformer.addWhere(where);
                }
            } else {
                transformer.addJoinAndWhere(join, where);
            }
        } catch (JpqlSyntaxException e) {
            log.error("Syntax errors found in constraint's JPQL expressions. Entity [{}]. Constraint ID [{}].",
                    entityName, constraint.getId(), e);

            throw new RowLevelSecurityException(
                    "Syntax errors found in constraint's JPQL expressions. Please see the logs.", entityName);
        } catch (Exception e) {
            log.error("An error occurred when applying security constraint. Entity [{}]. Constraint ID [{}].",
                    entityName, constraint.getId(), e);

            throw new RowLevelSecurityException(
                    "An error occurred when applying security constraint. Please see the logs.", entityName);
        }
    }

    @SuppressWarnings("unchecked")
    protected void applyConstraints(Entity entity, Set<EntityId> handled) {
        MetaClass metaClass = entity.getMetaClass();
        EntityId entityId = new EntityId(referenceToEntitySupport.getReferenceId(entity), metaClass.getName());
        if (handled.contains(entityId)) {
            return;
        }
        handled.add(entityId);
        if (entity instanceof BaseGenericIdEntity) {
            BaseGenericIdEntity baseGenericIdEntity = (BaseGenericIdEntity) entity;
            Multimap<String, Object> filteredData = BaseEntityInternalAccess.getFilteredData(baseGenericIdEntity);
            for (MetaProperty property : metaClass.getProperties()) {
                if (metadataTools.isPersistent(property)
                        && PersistenceHelper.isLoaded(entity, property.getName())) {
                    Object value = entity.getValue(property.getName());
                    if (value instanceof Collection) {
                        Collection entities = (Collection) value;
                        for (Iterator<Entity> iterator = entities.iterator(); iterator.hasNext();) {
                            Entity item = iterator.next();
                            if (filteredData != null && filteredData.containsEntry(property.getName(),
                                    referenceToEntitySupport.getReferenceId(item))) {
                                iterator.remove();
                            } else {
                                applyConstraints(item, handled);
                            }
                        }
                    } else if (value instanceof Entity) {
                        if (filteredData != null && filteredData.containsEntry(property.getName(),
                                referenceToEntitySupport.getReferenceId((Entity) value))) {
                            baseGenericIdEntity.setValue(property.getName(), null);
                        } else {
                            applyConstraints((Entity) value, handled);
                        }
                    }
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    protected boolean calculateFilteredData(Entity entity, Set<EntityId> handled, boolean checkPermitted) {
        MetaClass metaClass = entity.getMetaClass();
        if (!isPermittedInMemory(entity) && checkPermitted) {
            return true;
        }
        EntityId entityId = new EntityId(referenceToEntitySupport.getReferenceId(entity), metaClass.getName());
        if (handled.contains(entityId)) {
            return false;
        }
        handled.add(entityId);
        if (entity instanceof BaseGenericIdEntity) {
            BaseGenericIdEntity baseGenericIdEntity = (BaseGenericIdEntity) entity;
            for (MetaProperty property : metaClass.getProperties()) {
                if (metadataTools.isPersistent(property)
                        && PersistenceHelper.isLoaded(entity, property.getName())) {
                    Object value = entity.getValue(property.getName());
                    if (value instanceof Collection) {
                        Set filtered = new LinkedHashSet();
                        for (Entity item : (Collection<Entity>) value) {
                            if (calculateFilteredData(item, handled, true)) {
                                filtered.add(referenceToEntitySupport.getReferenceId(item));
                            }
                        }
                        if (!filtered.isEmpty()) {
                            securityTokenManager.addFiltered(baseGenericIdEntity, property.getName(), filtered);
                        }
                    } else if (value instanceof Entity) {
                        Entity valueEntity = (Entity) value;
                        if (calculateFilteredData(valueEntity, handled, true)) {
                            securityTokenManager.addFiltered(baseGenericIdEntity, property.getName(),
                                    referenceToEntitySupport.getReferenceId(valueEntity));
                        }
                    }
                }
            }
            securityTokenManager.writeSecurityToken(baseGenericIdEntity);
        }
        return false;
    }

    protected boolean isPermittedInMemory(Entity entity) {
        return isPermitted(entity,
                constraint -> constraint.getCheckType().memory()
                        && (constraint.getOperationType() == ConstraintOperationType.READ
                                || constraint.getOperationType() == ConstraintOperationType.ALL));
    }

    protected static class EntityId {
        Object id;
        String metaClassName;

        public EntityId(Object id, String metaClassName) {
            this.id = id;
            this.metaClassName = metaClassName;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o)
                return true;
            if (o == null || getClass() != o.getClass())
                return false;

            EntityId entityId = (EntityId) o;

            if (!id.equals(entityId.id))
                return false;
            return metaClassName.equals(entityId.metaClassName);
        }

        @Override
        public int hashCode() {
            int result = id.hashCode();
            result = 31 * result + metaClassName.hashCode();
            return result;
        }
    }
}