/* * Copyright (c) 2010-2016 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.init; import com.evolveum.midpoint.audit.api.AuditEventRecord; import com.evolveum.midpoint.audit.api.AuditResultHandler; import com.evolveum.midpoint.audit.api.AuditService; import com.evolveum.midpoint.audit.spi.AuditServiceRegistry; import com.evolveum.midpoint.prism.PrismContext; import com.evolveum.midpoint.prism.PrismObject; import com.evolveum.midpoint.prism.PrismObjectDefinition; import com.evolveum.midpoint.prism.PrismReferenceValue; import com.evolveum.midpoint.prism.Visitable; import com.evolveum.midpoint.prism.Visitor; import com.evolveum.midpoint.prism.delta.ObjectDelta; import com.evolveum.midpoint.prism.path.ItemPath; import com.evolveum.midpoint.prism.polystring.PolyString; import com.evolveum.midpoint.repo.api.RepositoryService; import com.evolveum.midpoint.schema.GetOperationOptions; import com.evolveum.midpoint.schema.ObjectDeltaOperation; import com.evolveum.midpoint.schema.RetrieveOption; import com.evolveum.midpoint.schema.SelectorOptions; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.LightweightIdentifier; import com.evolveum.midpoint.task.api.LightweightIdentifierGenerator; import com.evolveum.midpoint.task.api.Task; import com.evolveum.midpoint.util.exception.ObjectNotFoundException; 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.CleanupPolicyType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType; import org.apache.commons.lang.Validate; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; /** * @author lazyman */ public class AuditServiceProxy implements AuditService, AuditServiceRegistry { private static final Trace LOGGER = TraceManager.getTrace(AuditServiceProxy.class); @Autowired private LightweightIdentifierGenerator lightweightIdentifierGenerator; @Autowired(required = false) // missing in some tests private RepositoryService repositoryService; @Autowired private PrismContext prismContext; private List<AuditService> services = new Vector<AuditService>(); @Override public void audit(AuditEventRecord record, Task task) { if (services.isEmpty()) { LOGGER.warn("Audit event will not be recorded. No audit services registered."); return; } assertCorrectness(record, task); completeRecord(record, task); for (AuditService service : services) { service.audit(record, task); } } @Override public void cleanupAudit(CleanupPolicyType policy, OperationResult parentResult) { Validate.notNull(policy, "Cleanup policy must not be null."); Validate.notNull(parentResult, "Operation result must not be null."); for (AuditService service : services) { service.cleanupAudit(policy, parentResult); } } @Override public void registerService(AuditService service) { Validate.notNull(service, "Audit service must not be null."); if (services.contains(service)) { return; } services.add(service); } @Override public void unregisterService(AuditService service) { Validate.notNull(service, "Audit service must not be null."); services.remove(service); } private void assertCorrectness(AuditEventRecord record, Task task) { if (task == null) { LOGGER.warn("Task is null in a call to audit service"); } } /** * Complete the record with data that can be computed or discovered from the * environment */ private void completeRecord(AuditEventRecord record, Task task) { LightweightIdentifier id = null; if (record.getEventIdentifier() == null) { id = lightweightIdentifierGenerator.generate(); record.setEventIdentifier(id.toString()); } if (record.getTimestamp() == null) { if (id == null) { record.setTimestamp(System.currentTimeMillis()); } else { // To be consistent with the ID record.setTimestamp(id.getTimestamp()); } } if (record.getTaskIdentifier() == null && task != null) { record.setTaskIdentifier(task.getTaskIdentifier()); } if (record.getTaskOID() == null && task != null) { record.setTaskOID(task.getOid()); } if (record.getTaskOID() == null && task != null) { record.setTaskOID(task.getOid()); } if (record.getSessionIdentifier() == null && task != null) { // TODO } if (record.getInitiator() == null && task != null) { record.setInitiator(task.getOwner()); } if (record.getHostIdentifier() == null) { // TODO } if (record.getDeltas() != null) { for (ObjectDeltaOperation<? extends ObjectType> objectDeltaOperation : record.getDeltas()) { ObjectDelta<? extends ObjectType> delta = objectDeltaOperation.getObjectDelta(); final Map<String, PolyString> resolvedOids = new HashMap<>(); Visitor namesResolver = new Visitor() { @Override public void visit(Visitable visitable) { if (visitable instanceof PrismReferenceValue) { PrismReferenceValue refVal = ((PrismReferenceValue) visitable); String oid = refVal.getOid(); if (oid == null) { // sanity check; should not // happen return; } if (refVal.getTargetName() != null) { resolvedOids.put(oid, refVal.getTargetName()); return; } if (resolvedOids.containsKey(oid)) { PolyString resolvedName = resolvedOids.get(oid); // may // be // null refVal.setTargetName(resolvedName); return; } if (refVal.getObject() != null) { PolyString name = refVal.getObject().getName(); refVal.setTargetName(name); resolvedOids.put(oid, name); return; } if (repositoryService == null) { LOGGER.warn("No repository, no OID resolution (for {})", oid); return; } PrismObjectDefinition<? extends ObjectType> objectDefinition = null; if (refVal.getTargetType() != null) { objectDefinition = prismContext.getSchemaRegistry() .findObjectDefinitionByType(refVal.getTargetType()); } Class<? extends ObjectType> objectClass = null; if (objectDefinition != null) { objectClass = objectDefinition.getCompileTimeClass(); } if (objectClass == null) { objectClass = ObjectType.class; // the default // (shouldn't be // needed) } SelectorOptions<GetOperationOptions> getNameOnly = SelectorOptions.create( new ItemPath(ObjectType.F_NAME), GetOperationOptions.createRetrieve(RetrieveOption.INCLUDE)); try { PrismObject<? extends ObjectType> object = repositoryService.getObject( objectClass, oid, Arrays.asList(getNameOnly), new OperationResult("dummy")); PolyString name = object.getName(); refVal.setTargetName(name); resolvedOids.put(oid, name); LOGGER.trace("Resolved {}: {} to {}", objectClass, oid, name); } catch (ObjectNotFoundException e) { LOGGER.trace("Couldn't determine the name for {}: {} as it does not exist", objectClass, oid, e); resolvedOids.put(oid, null); } catch (SchemaException | RuntimeException e) { LOGGER.trace( "Couldn't determine the name for {}: {} because of unexpected exception", objectClass, oid, e); resolvedOids.put(oid, null); } } } }; delta.accept(namesResolver); } } } @Override public List<AuditEventRecord> listRecords(String query, Map<String, Object> params) { List<AuditEventRecord> result = new ArrayList<AuditEventRecord>(); for (AuditService service : services) { if (service.supportsRetrieval()) { List<AuditEventRecord> records = service.listRecords(query, params); if (records != null && !records.isEmpty()) { result.addAll(records); } } } return result; } @Override public void listRecordsIterative(String query, Map<String, Object> params, AuditResultHandler handler) { for (AuditService service : services) { if (service.supportsRetrieval()) { service.listRecordsIterative(query, params, handler); } } } @Override public void reindexEntry(AuditEventRecord record) { for (AuditService service : services) { if (service.supportsRetrieval()) { service.reindexEntry(record); } } } @Override public long countObjects(String query, Map<String, Object> params) { long count = 0; for (AuditService service : services) { if (service.supportsRetrieval()) { long c = service.countObjects(query, params); count += c; } } return count; } @Override public boolean supportsRetrieval() { for (AuditService service : services) { if (service.supportsRetrieval()) { return true; } } return false; } }