/*
* Copyright (c) 2010-2017 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.model.impl.lens.projector.credentials;
import java.util.Collection;
import javax.xml.datatype.XMLGregorianCalendar;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.evolveum.midpoint.model.common.mapping.MappingFactory;
import com.evolveum.midpoint.model.common.stringpolicy.ValuePolicyProcessor;
import com.evolveum.midpoint.model.impl.ModelObjectResolver;
import com.evolveum.midpoint.model.impl.lens.LensContext;
import com.evolveum.midpoint.model.impl.lens.LensFocusContext;
import com.evolveum.midpoint.model.impl.lens.OperationalDataManager;
import com.evolveum.midpoint.model.impl.lens.projector.MappingEvaluator;
import com.evolveum.midpoint.model.impl.security.SecurityHelper;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismProperty;
import com.evolveum.midpoint.prism.PrismPropertyValue;
import com.evolveum.midpoint.prism.crypto.EncryptionException;
import com.evolveum.midpoint.prism.crypto.Protector;
import com.evolveum.midpoint.prism.delta.ObjectDelta;
import com.evolveum.midpoint.prism.delta.PropertyDelta;
import com.evolveum.midpoint.prism.path.ItemPath;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.security.api.SecurityUtil;
import com.evolveum.midpoint.task.api.Task;
import com.evolveum.midpoint.util.exception.ExpressionEvaluationException;
import com.evolveum.midpoint.util.exception.ObjectNotFoundException;
import com.evolveum.midpoint.util.exception.PolicyViolationException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialPolicyType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsPolicyType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsStorageMethodType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.CredentialsStorageTypeType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.FocusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.SecurityPolicyType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.UserType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ValuePolicyType;
import com.evolveum.prism.xml.ns._public.types_3.ProtectedStringType;
/**
* Processor for focus credentials. It validates the credentials, checks
* policies (complexity, history, etc.), adds metadata and so on.
*
* @author Radovan Semancik
*/
@Component
public class CredentialsProcessor {
private static final Trace LOGGER = TraceManager.getTrace(CredentialsProcessor.class);
@Autowired(required=true)
private PrismContext prismContext;
@Autowired(required=true)
private MappingFactory mappingFactory;
@Autowired(required=true)
private MappingEvaluator mappingEvaluator;
@Autowired(required=true)
private OperationalDataManager metadataManager;
@Autowired(required=true)
private ModelObjectResolver resolver;
@Autowired(required=true)
private ValuePolicyProcessor valuePolicyProcessor;
@Autowired(required = true)
private SecurityHelper securityHelper;
@Autowired(required = true)
Protector protector;
public <F extends FocusType> void processFocusCredentials(LensContext<F> context,
XMLGregorianCalendar now, Task task, OperationResult result) throws ExpressionEvaluationException,
ObjectNotFoundException, SchemaException, PolicyViolationException {
LensFocusContext<F> focusContext = context.getFocusContext();
if (focusContext == null || !UserType.class.isAssignableFrom(focusContext.getObjectTypeClass())) {
LOGGER.trace("Skipping processing credentials because focus is not user");
return;
}
processSecurityPolicy(context, now, task, result);
processFocusPassword((LensContext<UserType>) context, now, task, result);
processFocusNonce((LensContext<UserType>) context, now, task, result);
processFocusSecurityQuestions((LensContext<UserType>) context, now, task, result);
}
private <F extends FocusType> void processSecurityPolicy(LensContext<F> context, XMLGregorianCalendar now,
Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException,
SchemaException, PolicyViolationException {
LensFocusContext<F> focusContext = context.getFocusContext();
SecurityPolicyType securityPolicy = focusContext.getSecurityPolicy();
if (securityPolicy == null) {
securityPolicy = securityHelper.locateSecurityPolicy(focusContext.getObjectAny(),
context.getSystemConfiguration(), task, result);
if (securityPolicy == null) {
// store empty policy to avoid repeated lookups
securityPolicy = new SecurityPolicyType();
}
focusContext.setSecurityPolicy(securityPolicy);
}
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("Security policy:\n{}", securityPolicy==null?null:securityPolicy.asPrismObject().debugDump(1));
} else {
LOGGER.debug("Security policy: {}", securityPolicy);
}
}
private <F extends FocusType> void processFocusPassword(LensContext<UserType> context, XMLGregorianCalendar now,
Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException,
SchemaException, PolicyViolationException {
PasswordPolicyEvaluator evaluator = new PasswordPolicyEvaluator();
evaluator.setContext(context);
evaluator.setMetadataManager(metadataManager);
evaluator.setNow(now);
evaluator.setPrismContext(prismContext);
evaluator.setProtector(protector);
evaluator.setResolver(resolver);
evaluator.setResult(result);
evaluator.setTask(task);
evaluator.setValuePolicyProcessor(valuePolicyProcessor);
evaluator.process();
}
//for now just saving metadata
private void processFocusNonce(LensContext<UserType> context, XMLGregorianCalendar now,
Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException,
SchemaException, PolicyViolationException {
NoncePolicyEvaluator evaluator = new NoncePolicyEvaluator();
evaluator.setContext(context);
evaluator.setMetadataManager(metadataManager);
evaluator.setNow(now);
evaluator.setPrismContext(prismContext);
evaluator.setProtector(protector);
evaluator.setResolver(resolver);
evaluator.setResult(result);
evaluator.setTask(task);
evaluator.setValuePolicyProcessor(valuePolicyProcessor);
evaluator.process();
}
private void processFocusSecurityQuestions(LensContext<UserType> context, XMLGregorianCalendar now,
Task task, OperationResult result) throws ExpressionEvaluationException, ObjectNotFoundException,
SchemaException, PolicyViolationException {
SecurityQuestionsPolicyEvaluator evaluator = new SecurityQuestionsPolicyEvaluator();
evaluator.setContext(context);
evaluator.setMetadataManager(metadataManager);
evaluator.setNow(now);
evaluator.setPrismContext(prismContext);
evaluator.setProtector(protector);
evaluator.setResolver(resolver);
evaluator.setResult(result);
evaluator.setTask(task);
evaluator.setValuePolicyProcessor(valuePolicyProcessor);
evaluator.process();
}
/**
* Called from ChangeExecutor. Will modify the execution deltas to hash or remove credentials if needed.
*/
public <O extends ObjectType> ObjectDelta<O> transformFocusExectionDelta(LensContext<O> context, ObjectDelta<O> focusDelta) throws SchemaException, EncryptionException {
LensFocusContext<O> focusContext = context.getFocusContext();
SecurityPolicyType securityPolicy = focusContext.getSecurityPolicy();
if (securityPolicy == null) {
return focusDelta;
}
CredentialsPolicyType credsType = securityPolicy.getCredentials();
if (credsType == null) {
return focusDelta;
}
ObjectDelta<O> transformedDelta = focusDelta.clone();
transformFocusExectionDeltaCredential(context, credsType, credsType.getPassword(), SchemaConstants.PATH_PASSWORD_VALUE, transformedDelta);
// TODO: nonce and others
return transformedDelta;
}
private <O extends ObjectType> void transformFocusExectionDeltaCredential(LensContext<O> context,
CredentialsPolicyType credsType, CredentialPolicyType credPolicyType,
ItemPath valuePropertyPath, ObjectDelta<O> delta) throws SchemaException, EncryptionException {
if (delta.isDelete()) {
return;
}
CredentialPolicyType defaltCredPolicyType = credsType.getDefault();
CredentialsStorageMethodType storageMethod =
SecurityUtil.getCredPolicyItem(defaltCredPolicyType, credPolicyType, pol -> pol.getStorageMethod());
if (storageMethod == null) {
return;
}
CredentialsStorageTypeType storageType = storageMethod.getStorageType();
if (storageType == null || storageType == CredentialsStorageTypeType.ENCRYPTION) {
return;
} else if (storageType == CredentialsStorageTypeType.HASHING) {
PrismPropertyValue<ProtectedStringType> pval = null;
if (delta.isAdd()) {
PrismProperty<ProtectedStringType> prop = delta.getObjectToAdd().findProperty(valuePropertyPath);
hashValues(prop.getValues(), storageMethod);
} else {
PropertyDelta<ProtectedStringType> propDelta = delta.findPropertyDelta(valuePropertyPath);
if (propDelta != null) {
hashValues(propDelta.getValuesToAdd(), storageMethod);
hashValues(propDelta.getValuesToReplace(), storageMethod);
hashValues(propDelta.getValuesToDelete(), storageMethod);
}
}
} else if (storageType == CredentialsStorageTypeType.NONE) {
if (delta.isAdd()) {
delta.getObjectToAdd().removeProperty(valuePropertyPath);
} else {
PropertyDelta<ProtectedStringType> propDelta = delta.findPropertyDelta(valuePropertyPath);
if (propDelta != null) {
// Replace with nothing. We need this to clear any existing value that there might be.
propDelta.setValueToReplace();
}
}
} else {
throw new SchemaException("Unkwnon storage type "+storageType);
}
}
private void hashValues(Collection<PrismPropertyValue<ProtectedStringType>> values,
CredentialsStorageMethodType storageMethod) throws SchemaException, EncryptionException {
if (values == null) {
return;
}
for (PrismPropertyValue<ProtectedStringType> pval: values) {
ProtectedStringType ps = pval.getValue();
if (!ps.isHashed()) {
protector.hash(ps);
}
}
}
/**
* Legacy. Invoked from mappings. TODO: fix
*/
public <F extends ObjectType> ValuePolicyType determinePasswordPolicy(LensFocusContext<F> focusContext, Task task, OperationResult result) {
if (focusContext == null) {
return null;
}
return SecurityUtil.getPasswordPolicy(focusContext.getSecurityPolicy());
}
}