/*
* Copyright (c) 2013-2015 Evolveum
*
* 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.evolveum.midpoint.provisioning.impl;
import java.util.Collection;
import javax.xml.namespace.QName;
import org.springframework.stereotype.Component;
import com.evolveum.midpoint.common.refinery.PropertyLimitations;
import com.evolveum.midpoint.common.refinery.RefinedAttributeDefinition;
import com.evolveum.midpoint.common.refinery.RefinedObjectClassDefinition;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.delta.ItemDelta;
import com.evolveum.midpoint.prism.delta.PropertyDelta;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.processor.ResourceAttribute;
import com.evolveum.midpoint.schema.processor.ResourceAttributeContainer;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.util.ShadowUtil;
import com.evolveum.midpoint.util.exception.CommunicationException;
import com.evolveum.midpoint.util.exception.ConfigurationException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.exception.SecurityViolationException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.LayerType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.PropertyAccessType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ResourceType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
/**
* @author Radovan Semancik
*
*/
@Component
public class AccessChecker {
public static final String OPERATION_NAME = AccessChecker.class.getName()+".accessCheck";
private static final Trace LOGGER = TraceManager.getTrace(AccessChecker.class);
public void checkAdd(ProvisioningContext ctx, PrismObject<ShadowType> shadow, OperationResult parentResult)
throws SchemaException, SecurityViolationException, ConfigurationException, ObjectNotFoundException, CommunicationException {
OperationResult result = parentResult.createMinorSubresult(OPERATION_NAME);
ResourceAttributeContainer attributeCont = ShadowUtil.getAttributesContainer(shadow);
for (ResourceAttribute<?> attribute: attributeCont.getAttributes()) {
RefinedAttributeDefinition attrDef = ctx.getObjectClassDefinition().findAttributeDefinition(attribute.getElementName());
// Need to check model layer, not schema. Model means IDM logic which can be overridden in schemaHandling,
// schema layer is the original one.
if (attrDef == null) {
String msg = "No definition for attribute "+attribute.getElementName()+" in "+ctx.getObjectClassDefinition();
result.recordFatalError(msg);
throw new SchemaException(msg);
}
PropertyLimitations limitations = attrDef.getLimitations(LayerType.MODEL);
if (limitations == null) {
continue;
}
// We cannot throw error here. At least not now. Provisioning will internally use ignored attributes
// e.g. for simulated capabilities. This is not a problem for normal operations, but it is a problem
// for delayed operations (e.g. consistency) that are passing through this code again.
// TODO: we need to figure a way how to avoid this loop
// if (limitations.isIgnore()) {
// String message = "Attempt to create shadow with ignored attribute "+attribute.getName();
// LOGGER.error(message);
// throw new SchemaException(message);
// }
PropertyAccessType access = limitations.getAccess();
if (access == null) {
continue;
}
if (access.isAdd() == null || !access.isAdd()) {
String message = "Attempt to add shadow with non-createable attribute "+attribute.getElementName();
LOGGER.error(message);
result.recordFatalError(message);
throw new SecurityViolationException(message);
}
}
result.recordSuccess();
}
public void checkModify(ResourceType resource, PrismObject<ShadowType> shadow,
Collection<? extends ItemDelta> modifications, RefinedObjectClassDefinition objectClassDefinition,
OperationResult parentResult) throws SecurityViolationException, SchemaException {
OperationResult result = parentResult.createMinorSubresult(OPERATION_NAME);
for (ItemDelta modification: modifications) {
if (!(modification instanceof PropertyDelta<?>)) {
continue;
}
PropertyDelta<?> attrDelta = (PropertyDelta<?>)modification;
if (!SchemaConstants.PATH_ATTRIBUTES.equivalent(attrDelta.getParentPath())) {
// Not an attribute
continue;
}
QName attrName = attrDelta.getElementName();
RefinedAttributeDefinition attrDef = objectClassDefinition.findAttributeDefinition(attrName);
if (attrDef == null) {
throw new SchemaException("Cannot find definition of attribute "+attrName+" in "+objectClassDefinition);
}
PropertyLimitations limitations = attrDef.getLimitations(LayerType.MODEL);
if (limitations == null) {
continue;
}
// We cannot throw error here. At least not now. Provisioning will internally use ignored attributes
// e.g. for simulated capabilities. This is not a problem for normal operations, but it is a problem
// for delayed operations (e.g. consistency) that are passing through this code again.
// TODO: we need to figure a way how to avoid this loop
// if (limitations.isIgnore()) {
// String message = "Attempt to create shadow with ignored attribute "+attribute.getName();
// LOGGER.error(message);
// throw new SchemaException(message);
// }
PropertyAccessType access = limitations.getAccess();
if (access == null) {
continue;
}
if (access.isModify() == null || !access.isModify()) {
String message = "Attempt to modify non-updateable attribute "+attrName;
LOGGER.error(message);
result.recordFatalError(message);
throw new SecurityViolationException(message);
}
}
result.recordSuccess();
}
public void filterGetAttributes(ResourceAttributeContainer attributeContainer, RefinedObjectClassDefinition objectClassDefinition, OperationResult parentResult) throws SchemaException {
OperationResult result = parentResult.createMinorSubresult(OPERATION_NAME);
for (ResourceAttribute<?> attribute: attributeContainer.getAttributes()) {
QName attrName = attribute.getElementName();
RefinedAttributeDefinition attrDef = objectClassDefinition.findAttributeDefinition(attrName);
if (attrDef == null) {
String message = "Unknown attribute " + attrName + " in objectclass " + objectClassDefinition;
result.recordFatalError(message);
throw new SchemaException(message);
}
// Need to check model layer, not schema. Model means IDM logic which can be overridden in schemaHandling,
// schema layer is the original one.
PropertyLimitations limitations = attrDef.getLimitations(LayerType.MODEL);
if (limitations == null) {
continue;
}
// We cannot throw error here. At least not now. Provisioning will internally use ignored attributes
// e.g. for simulated capabilities. This is not a problem for normal operations, but it is a problem
// for delayed operations (e.g. consistency) that are passing through this code again.
// TODO: we need to figure a way how to avoid this loop
// if (limitations.isIgnore()) {
// String message = "Attempt to create shadow with ignored attribute "+attribute.getName();
// LOGGER.error(message);
// throw new SchemaException(message);
// }
PropertyAccessType access = limitations.getAccess();
if (access == null) {
continue;
}
if (access.isRead() == null || !access.isRead()) {
LOGGER.trace("Removing non-readable attribute {}", attrName);
attributeContainer.remove(attribute);
}
}
result.recordSuccess();
}
}