/*
* Copyright (c) 2010-2014 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.wf.impl.processors;
import com.evolveum.midpoint.audit.api.AuditEventRecord;
import com.evolveum.midpoint.audit.api.AuditEventStage;
import com.evolveum.midpoint.audit.api.AuditEventType;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.repo.api.RepositoryService;
import com.evolveum.midpoint.schema.constants.ObjectTypes;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
import com.evolveum.midpoint.schema.util.ObjectTypeUtil;
import com.evolveum.midpoint.schema.util.WfContextUtil;
import com.evolveum.midpoint.security.api.SecurityEnforcer;
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.LoggingUtils;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.wf.api.WorkflowConstants;
import com.evolveum.midpoint.wf.api.WorkflowException;
import com.evolveum.midpoint.wf.impl.tasks.WfTask;
import com.evolveum.midpoint.wf.util.ApprovalUtils;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
import static com.evolveum.midpoint.audit.api.AuditEventStage.EXECUTION;
import static com.evolveum.midpoint.audit.api.AuditEventType.WORKFLOW_PROCESS_INSTANCE;
/**
* @author mederly
*/
@Component
public class BaseAuditHelper {
private static final Trace LOGGER = TraceManager.getTrace(BaseAuditHelper.class);
@Autowired private SecurityEnforcer securityEnforcer;
@Autowired private PrismContext prismContext;
@Autowired
@Qualifier("cacheRepositoryService")
private RepositoryService repositoryService;
public AuditEventRecord prepareProcessInstanceAuditRecord(WfTask wfTask, AuditEventStage stage, OperationResult result) {
WfContextType wfc = wfTask.getTask().getWorkflowContext();
AuditEventRecord record = new AuditEventRecord();
record.setEventType(WORKFLOW_PROCESS_INSTANCE);
record.setEventStage(stage);
record.setInitiator(wfTask.getRequesterIfExists(result));
ObjectReferenceType objectRef = resolveIfNeeded(wfc.getObjectRef(), result);
record.setTarget(objectRef.asReferenceValue());
record.setOutcome(OperationResultStatus.SUCCESS);
record.addReferenceValueIgnoreNull(WorkflowConstants.AUDIT_OBJECT, objectRef);
record.addReferenceValueIgnoreNull(WorkflowConstants.AUDIT_TARGET, resolveIfNeeded(wfc.getTargetRef(), result));
if (stage == EXECUTION) {
String stageInfo = wfTask.getCompleteStageInfo();
record.setParameter(stageInfo);
String answer = wfTask.getAnswerNice();
record.setResult(answer);
record.setMessage(stageInfo != null ? stageInfo + " : " + answer : answer);
record.addPropertyValueIgnoreNull(WorkflowConstants.AUDIT_STAGE_NUMBER, wfc.getStageNumber());
record.addPropertyValueIgnoreNull(WorkflowConstants.AUDIT_STAGE_COUNT, WfContextUtil.getStageCount(wfc));
record.addPropertyValueIgnoreNull(WorkflowConstants.AUDIT_STAGE_NAME, WfContextUtil.getStageName(wfc));
record.addPropertyValueIgnoreNull(WorkflowConstants.AUDIT_STAGE_DISPLAY_NAME, WfContextUtil.getStageDisplayName(wfc));
}
record.addPropertyValue(WorkflowConstants.AUDIT_PROCESS_INSTANCE_ID, wfc.getProcessInstanceId());
OperationBusinessContextType businessContext = WfContextUtil.getBusinessContext(wfc);
String requesterComment = businessContext != null ? businessContext.getComment() : null;
if (requesterComment != null) {
record.addPropertyValue(WorkflowConstants.AUDIT_REQUESTER_COMMENT, requesterComment);
}
return record;
}
private ObjectReferenceType resolveIfNeeded(ObjectReferenceType ref, OperationResult result) {
if (ref == null || ref.getOid() == null || ref.asReferenceValue().getObject() != null || ref.getType() == null) {
return ref;
}
ObjectTypes types = ObjectTypes.getObjectTypeFromTypeQName(ref.getType());
if (types == null) {
return ref;
}
try {
return ObjectTypeUtil.createObjectRef(
repositoryService.getObject(types.getClassDefinition(), ref.getOid(), null, result));
} catch (ObjectNotFoundException|SchemaException e) {
LoggingUtils.logUnexpectedException(LOGGER, "Couldn't resolve {}", e, ref);
return ref;
}
}
private List<ObjectReferenceType> resolveIfNeeded(List<ObjectReferenceType> refs, OperationResult result) {
return refs.stream()
.map(ref -> resolveIfNeeded(ref, result))
.collect(Collectors.toList());
}
public AuditEventRecord prepareWorkItemAuditReportCommon(WorkItemType workItem, WfTask wfTask, AuditEventStage stage,
OperationResult result) throws WorkflowException {
AuditEventRecord record = new AuditEventRecord();
record.setEventType(AuditEventType.WORK_ITEM);
record.setEventStage(stage);
ObjectReferenceType objectRef = resolveIfNeeded(WfContextUtil.getObjectRef(workItem), result);
record.setTarget(objectRef.asReferenceValue());
record.setOutcome(OperationResultStatus.SUCCESS);
record.setParameter(wfTask.getCompleteStageInfo());
record.addReferenceValueIgnoreNull(WorkflowConstants.AUDIT_OBJECT, objectRef);
record.addReferenceValueIgnoreNull(WorkflowConstants.AUDIT_TARGET, resolveIfNeeded(WfContextUtil.getTargetRef(workItem), result));
record.addReferenceValueIgnoreNull(WorkflowConstants.AUDIT_ORIGINAL_ASSIGNEE, resolveIfNeeded(workItem.getOriginalAssigneeRef(), result));
record.addReferenceValues(WorkflowConstants.AUDIT_CURRENT_ASSIGNEE, resolveIfNeeded(workItem.getAssigneeRef(), result));
record.addPropertyValueIgnoreNull(WorkflowConstants.AUDIT_STAGE_NUMBER, workItem.getStageNumber());
record.addPropertyValueIgnoreNull(WorkflowConstants.AUDIT_STAGE_COUNT, WfContextUtil.getStageCount(workItem));
record.addPropertyValueIgnoreNull(WorkflowConstants.AUDIT_STAGE_NAME, WfContextUtil.getStageName(workItem));
record.addPropertyValueIgnoreNull(WorkflowConstants.AUDIT_STAGE_DISPLAY_NAME, WfContextUtil.getStageDisplayName(workItem));
record.addPropertyValueIgnoreNull(WorkflowConstants.AUDIT_ESCALATION_LEVEL_NUMBER, WfContextUtil.getEscalationLevelNumber(workItem));
record.addPropertyValueIgnoreNull(WorkflowConstants.AUDIT_ESCALATION_LEVEL_NAME, WfContextUtil.getEscalationLevelName(workItem));
record.addPropertyValueIgnoreNull(WorkflowConstants.AUDIT_ESCALATION_LEVEL_DISPLAY_NAME, WfContextUtil.getEscalationLevelDisplayName(workItem));
record.addPropertyValue(WorkflowConstants.AUDIT_WORK_ITEM_ID, workItem.getExternalId());
record.addPropertyValue(WorkflowConstants.AUDIT_PROCESS_INSTANCE_ID, WfContextUtil.getProcessInstanceId(workItem));
return record;
}
// workItem contains taskRef, assignee, originalAssignee, candidates resolved (if possible)
public AuditEventRecord prepareWorkItemCreatedAuditRecord(WorkItemType workItem, WfTask wfTask, OperationResult result)
throws WorkflowException {
AuditEventRecord record = prepareWorkItemAuditReportCommon(workItem, wfTask, AuditEventStage.REQUEST, result);
record.setInitiator(wfTask.getRequesterIfExists(result));
record.setMessage(wfTask.getCompleteStageInfo());
return record;
}
// workItem contains taskRef, assignee, candidates resolved (if possible)
public AuditEventRecord prepareWorkItemDeletedAuditRecord(WorkItemType workItem, WorkItemEventCauseInformationType cause,
WfTask wfTask, OperationResult result) throws WorkflowException {
AuditEventRecord record = prepareWorkItemAuditReportCommon(workItem, wfTask, AuditEventStage.EXECUTION, result);
setCurrentUserAsInitiator(record);
if (cause != null) {
if (cause.getType() != null) {
record.addPropertyValue(WorkflowConstants.AUDIT_CAUSE_TYPE, cause.getType().value());
}
if (cause.getName() != null) {
record.addPropertyValue(WorkflowConstants.AUDIT_CAUSE_NAME, cause.getName());
}
if (cause.getDisplayName() != null) {
record.addPropertyValue(WorkflowConstants.AUDIT_CAUSE_DISPLAY_NAME, cause.getDisplayName());
}
}
// message + result
StringBuilder message = new StringBuilder();
String stageInfo = wfTask.getCompleteStageInfo();
if (stageInfo != null) {
message.append(stageInfo).append(" : ");
}
AbstractWorkItemOutputType output = workItem.getOutput();
if (output != null) {
String answer = ApprovalUtils.makeNiceFromUri(output.getOutcome());
record.setResult(answer);
message.append(answer);
if (output.getComment() != null) {
message.append(" : ").append(output.getComment());
record.addPropertyValue(WorkflowConstants.AUDIT_COMMENT, output.getComment());
}
} else {
message.append("(no decision)"); // TODO
}
record.setMessage(message.toString());
return record;
}
private void setCurrentUserAsInitiator(AuditEventRecord record) {
try {
@SuppressWarnings("unchecked")
PrismObject<UserType> principal = securityEnforcer.getPrincipal().getUser().asPrismObject();
record.setInitiator(principal);
} catch (SecurityViolationException e) {
record.setInitiator(null);
LOGGER.warn("No initiator known for auditing work item event: " + e.getMessage(), e);
}
}
}