mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-02 17:35:18 +00:00
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@4137 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
537 lines
18 KiB
Java
537 lines
18 KiB
Java
/*
|
|
* Copyright (C) 2005 Alfresco, Inc.
|
|
*
|
|
* Licensed under the Mozilla Public License version 1.1
|
|
* with a permitted attribution clause. You may obtain a
|
|
* copy of the License at
|
|
*
|
|
* http://www.alfresco.org/legal/license.txt
|
|
*
|
|
* 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 org.alfresco.repo.audit;
|
|
|
|
import java.io.Serializable;
|
|
import java.lang.reflect.Method;
|
|
import java.net.InetAddress;
|
|
import java.net.UnknownHostException;
|
|
import java.util.Date;
|
|
import java.util.List;
|
|
|
|
import org.alfresco.repo.audit.model.TrueFalseUnset;
|
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
|
import org.alfresco.service.Auditable;
|
|
import org.alfresco.service.NotAuditable;
|
|
import org.alfresco.service.cmr.audit.AuditInfo;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.repository.StoreRef;
|
|
import org.aopalliance.intercept.MethodInvocation;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
/**
|
|
* The default audit component implementation. TODO: Implement before, after and exception filtering. At the moment these filters are ignired. TODO: Respect audit internal - at the
|
|
* moment audit internal is fixed to false.
|
|
*
|
|
* @author Andy Hind
|
|
*/
|
|
public class AuditComponentImpl implements AuditComponent
|
|
{
|
|
/**
|
|
* The application name to use for audit entries generated by method interception around public services.
|
|
*/
|
|
private static final String SYSTEM_APPLICATION = "SystemMethodInterceptor";
|
|
|
|
/**
|
|
* Logging
|
|
*/
|
|
private static Log s_logger = LogFactory.getLog(AuditComponentImpl.class);
|
|
|
|
/**
|
|
* Suspend resume auditing
|
|
*/
|
|
private static ThreadLocal<Boolean> auditFlag = new ThreadLocal<Boolean>();
|
|
|
|
/**
|
|
* IOC
|
|
*/
|
|
private PublicServiceIdentifier publicServiceIdentifier;
|
|
|
|
private AuditConfiguration auditConfiguration;
|
|
|
|
private AuditDAO auditDAO;
|
|
|
|
private AuditDAO auditFailedDAO;
|
|
|
|
private AuditModel auditModel;
|
|
|
|
/**
|
|
* Keep hold of the host where the audit occurs. TODO: Check that we get the correct address ...
|
|
*/
|
|
|
|
private InetAddress auditHost;
|
|
|
|
public AuditComponentImpl()
|
|
{
|
|
super();
|
|
// Initialise the host address
|
|
try
|
|
{
|
|
auditHost = InetAddress.getLocalHost();
|
|
}
|
|
catch (UnknownHostException e)
|
|
{
|
|
s_logger.error("Failed to get local host address", e);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* IOC property setters
|
|
*/
|
|
|
|
public void setAuditDAO(AuditDAO auditDAO)
|
|
{
|
|
this.auditDAO = auditDAO;
|
|
}
|
|
|
|
public void setAuditFailedDAO(AuditDAO auditFailedDAO)
|
|
{
|
|
this.auditFailedDAO = auditFailedDAO;
|
|
}
|
|
|
|
public void setAuditConfiguration(AuditConfiguration auditConfiguration)
|
|
{
|
|
this.auditConfiguration = auditConfiguration;
|
|
}
|
|
|
|
public void setPublicServiceIdentifier(PublicServiceIdentifier publicServiceIdentifier)
|
|
{
|
|
this.publicServiceIdentifier = publicServiceIdentifier;
|
|
}
|
|
|
|
public void setAuditModel(AuditModel auditModel)
|
|
{
|
|
this.auditModel = auditModel;
|
|
}
|
|
|
|
public Object audit(MethodInvocation mi) throws Throwable
|
|
{
|
|
if ((auditFlag.get() == null) || (!auditFlag.get().booleanValue()))
|
|
{
|
|
boolean auditInternal = (auditModel.getAuditInternalServiceMethods(mi) == TrueFalseUnset.TRUE);
|
|
try
|
|
{
|
|
Method method = mi.getMethod();
|
|
String methodName = method.getName();
|
|
String serviceName = publicServiceIdentifier.getPublicServiceName(mi);
|
|
|
|
if (!auditInternal)
|
|
{
|
|
auditFlag.set(Boolean.TRUE);
|
|
}
|
|
else
|
|
{
|
|
if (s_logger.isDebugEnabled())
|
|
{
|
|
s_logger.debug("Auditing internal service use for - " + serviceName + "." + methodName);
|
|
}
|
|
}
|
|
|
|
|
|
if (method.isAnnotationPresent(Auditable.class))
|
|
{
|
|
|
|
if (serviceName != null)
|
|
{
|
|
if (s_logger.isDebugEnabled())
|
|
{
|
|
s_logger.debug("Auditing - " + serviceName + "." + methodName);
|
|
}
|
|
return auditImpl(mi);
|
|
}
|
|
else
|
|
{
|
|
if (s_logger.isDebugEnabled())
|
|
{
|
|
s_logger.debug("UnknownService." + methodName);
|
|
}
|
|
return auditImpl(mi);
|
|
}
|
|
|
|
}
|
|
else if (method.isAnnotationPresent(NotAuditable.class))
|
|
{
|
|
if (s_logger.isDebugEnabled())
|
|
{
|
|
s_logger.debug("Not Audited. " + serviceName + "." + methodName);
|
|
}
|
|
return mi.proceed();
|
|
}
|
|
else
|
|
{
|
|
if (s_logger.isDebugEnabled())
|
|
{
|
|
s_logger.debug("Unannotated service method " + serviceName + "." + methodName);
|
|
}
|
|
throw new RuntimeException("Unannotated service method " + serviceName + "." + methodName);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (!auditInternal)
|
|
{
|
|
auditFlag.set(Boolean.FALSE);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return mi.proceed();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Audit a method invocation
|
|
*/
|
|
public Object auditImpl(MethodInvocation mi) throws Throwable
|
|
{
|
|
AuditState auditInfo = new AuditState(auditConfiguration);
|
|
// RecordOptions recordOptions = auditModel.getAuditRecordOptions(mi);
|
|
AuditMode auditMode = AuditMode.UNSET;
|
|
try
|
|
{
|
|
auditMode = beforeInvocation(auditMode, auditInfo, mi);
|
|
Object o = mi.proceed();
|
|
auditMode = postInvocation(auditMode, auditInfo, mi, o);
|
|
if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.SUCCESS))
|
|
{
|
|
auditDAO.audit(auditInfo);
|
|
}
|
|
return o;
|
|
}
|
|
catch (Throwable t)
|
|
{
|
|
auditMode = onError(auditMode, auditInfo, mi, t);
|
|
if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL))
|
|
{
|
|
try
|
|
{
|
|
auditFailedDAO.audit(auditInfo);
|
|
}
|
|
catch (Throwable tt)
|
|
{
|
|
throw new AuditException("Failed to audit exception", new Object[] { tt }, t);
|
|
}
|
|
}
|
|
throw t;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper method to set auditable properties and to determine if auditing is required when an exception is caught in the audited method.
|
|
*
|
|
* @param auditMode
|
|
* @param auditInfo
|
|
* @param t
|
|
* @return
|
|
*/
|
|
private AuditMode onError(AuditMode auditMode, AuditState auditInfo, MethodInvocation mi, Throwable t)
|
|
{
|
|
if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL))
|
|
{
|
|
auditInfo.setFail(true);
|
|
auditInfo.setThrowable(t);
|
|
}
|
|
|
|
return auditMode;
|
|
}
|
|
|
|
/**
|
|
* Helper method to set audited information after method invocation and to determine if auditing should take place based on the method return value.
|
|
*
|
|
* @param auditMode
|
|
* @param auditInfo
|
|
* @param mi
|
|
* @param returnObject
|
|
* @return
|
|
*/
|
|
private AuditMode postInvocation(AuditMode auditMode, AuditState auditInfo, MethodInvocation mi, Object returnObject)
|
|
{
|
|
if (returnObject == null)
|
|
{
|
|
auditInfo.setReturnObject(null);
|
|
}
|
|
else if (returnObject instanceof Serializable)
|
|
{
|
|
auditInfo.setReturnObject((Serializable) returnObject);
|
|
}
|
|
else
|
|
{
|
|
auditInfo.setReturnObject(returnObject.toString());
|
|
}
|
|
|
|
Auditable auditable = mi.getMethod().getAnnotation(Auditable.class);
|
|
if (auditable.key() == Auditable.Key.RETURN)
|
|
{
|
|
if (returnObject != null)
|
|
{
|
|
if (returnObject instanceof NodeRef)
|
|
{
|
|
NodeRef key = (NodeRef) returnObject;
|
|
auditInfo.setKeyStore(key.getStoreRef());
|
|
auditInfo.setKeyGUID(key.getId());
|
|
}
|
|
else if (returnObject instanceof StoreRef)
|
|
{
|
|
auditInfo.setKeyStore((StoreRef) returnObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the user name is not set, try and set it after the method call.
|
|
// This covers authentication when the user is only known after the call.
|
|
|
|
if (auditInfo.getUserIdentifier() == null)
|
|
{
|
|
auditInfo.setUserIdentifier(AuthenticationUtil.getCurrentUserName());
|
|
}
|
|
|
|
return auditMode;
|
|
}
|
|
|
|
/**
|
|
* Set auditable information and determine if auditing is required before method invocation. This would normally be based on the method arguments.
|
|
*
|
|
* @param auditMode
|
|
* @param auditInfo
|
|
* @param mi
|
|
* @return
|
|
*/
|
|
private AuditMode beforeInvocation(AuditMode auditMode, AuditState auditInfo, MethodInvocation mi)
|
|
{
|
|
AuditMode effectiveAuditMode = auditModel.beforeExecution(auditMode, mi);
|
|
|
|
if (auditMode != AuditMode.NONE)
|
|
{
|
|
String methodName = mi.getMethod().getName();
|
|
String serviceName = publicServiceIdentifier.getPublicServiceName(mi);
|
|
auditInfo.setAuditApplication(SYSTEM_APPLICATION);
|
|
auditInfo.setAuditConfiguration(auditConfiguration);
|
|
auditInfo.setAuditMethod(methodName);
|
|
auditInfo.setAuditService(serviceName);
|
|
auditInfo.setClientAddress(null);
|
|
auditInfo.setDate(new Date());
|
|
auditInfo.setFail(false);
|
|
auditInfo.setFiltered(false);
|
|
auditInfo.setHostAddress(auditHost);
|
|
Auditable auditable = mi.getMethod().getAnnotation(Auditable.class);
|
|
Object key = null;
|
|
switch (auditable.key())
|
|
{
|
|
case ARG_0:
|
|
key = mi.getArguments()[0];
|
|
break;
|
|
case ARG_1:
|
|
key = mi.getArguments()[1];
|
|
break;
|
|
case ARG_2:
|
|
key = mi.getArguments()[2];
|
|
break;
|
|
case ARG_3:
|
|
key = mi.getArguments()[3];
|
|
break;
|
|
case ARG_4:
|
|
key = mi.getArguments()[4];
|
|
break;
|
|
case ARG_5:
|
|
key = mi.getArguments()[5];
|
|
break;
|
|
case ARG_6:
|
|
key = mi.getArguments()[6];
|
|
break;
|
|
case ARG_7:
|
|
key = mi.getArguments()[7];
|
|
break;
|
|
case ARG_8:
|
|
key = mi.getArguments()[8];
|
|
break;
|
|
case ARG_9:
|
|
key = mi.getArguments()[9];
|
|
break;
|
|
case NO_KEY:
|
|
default:
|
|
break;
|
|
}
|
|
if (key != null)
|
|
{
|
|
if (key instanceof NodeRef)
|
|
{
|
|
auditInfo.setKeyStore(((NodeRef) key).getStoreRef());
|
|
auditInfo.setKeyGUID(((NodeRef) key).getId());
|
|
}
|
|
else if (key instanceof StoreRef)
|
|
{
|
|
auditInfo.setKeyStore((StoreRef) key);
|
|
}
|
|
}
|
|
auditInfo.setKeyPropertiesAfter(null);
|
|
auditInfo.setKeyPropertiesBefore(null);
|
|
auditInfo.setMessage(null);
|
|
if (mi.getArguments() != null)
|
|
{
|
|
Serializable[] serArgs = new Serializable[mi.getArguments().length];
|
|
for (int i = 0; i < mi.getArguments().length; i++)
|
|
{
|
|
if ((auditable.recordable() == null)
|
|
|| (auditable.recordable().length <= i) || auditable.recordable()[i])
|
|
{
|
|
if (mi.getArguments()[i] == null)
|
|
{
|
|
serArgs[i] = null;
|
|
}
|
|
else if (mi.getArguments()[i] instanceof Serializable)
|
|
{
|
|
serArgs[i] = (Serializable) mi.getArguments()[i];
|
|
}
|
|
else
|
|
{
|
|
serArgs[i] = mi.getArguments()[i].toString();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
serArgs[i] = "********";
|
|
}
|
|
}
|
|
auditInfo.setMethodArguments(serArgs);
|
|
}
|
|
auditInfo.setPath(null);
|
|
auditInfo.setReturnObject(null);
|
|
auditInfo.setSessionId(null);
|
|
auditInfo.setThrowable(null);
|
|
auditInfo.setTxId(AlfrescoTransactionSupport.getTransactionId());
|
|
auditInfo.setUserIdentifier(AuthenticationUtil.getCurrentUserName());
|
|
}
|
|
|
|
return effectiveAuditMode;
|
|
}
|
|
|
|
/**
|
|
* A simple audit entry Currently we ignore filtering here.
|
|
*/
|
|
public void audit(String source, String description, NodeRef key, Object... args)
|
|
{
|
|
AuditState auditInfo = new AuditState(auditConfiguration);
|
|
// RecordOptions recordOptions = auditModel.getAuditRecordOptions(mi);
|
|
AuditMode auditMode = AuditMode.UNSET;
|
|
try
|
|
{
|
|
auditMode = onApplicationAudit(auditMode, auditInfo, source, description, key, args);
|
|
if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.SUCCESS))
|
|
{
|
|
auditDAO.audit(auditInfo);
|
|
}
|
|
}
|
|
catch (Throwable t)
|
|
{
|
|
auditMode = onError(auditMode, auditInfo, t, source, description, key, args);
|
|
if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL))
|
|
{
|
|
try
|
|
{
|
|
auditFailedDAO.audit(auditInfo);
|
|
}
|
|
catch (Throwable tt)
|
|
{
|
|
throw new AuditException("Failed to audit exception", new Object[] { tt }, t);
|
|
}
|
|
}
|
|
throw new AuditException("Application audit failed", t);
|
|
}
|
|
}
|
|
|
|
public List<AuditInfo> getAuditTrail(NodeRef nodeRef)
|
|
{
|
|
return auditDAO.getAuditTrail(nodeRef);
|
|
}
|
|
|
|
private AuditMode onApplicationAudit(AuditMode auditMode, AuditState auditInfo, String source, String description,
|
|
NodeRef key, Object... args)
|
|
{
|
|
AuditMode effectiveAuditMode = auditModel.beforeExecution(auditMode, source, description, key, args);
|
|
|
|
if (auditMode != AuditMode.NONE)
|
|
{
|
|
if (source.equals(SYSTEM_APPLICATION))
|
|
{
|
|
throw new AuditException("Application audit can not use the reserved identifier " + SYSTEM_APPLICATION);
|
|
}
|
|
|
|
auditInfo.setAuditApplication(source);
|
|
auditInfo.setAuditConfiguration(auditConfiguration);
|
|
auditInfo.setAuditMethod(null);
|
|
auditInfo.setAuditService(null);
|
|
auditInfo.setClientAddress(null);
|
|
auditInfo.setDate(new Date());
|
|
auditInfo.setFail(false);
|
|
auditInfo.setFiltered(false);
|
|
auditInfo.setHostAddress(auditHost);
|
|
if (key != null)
|
|
{
|
|
auditInfo.setKeyStore(key.getStoreRef());
|
|
auditInfo.setKeyGUID(key.getId());
|
|
}
|
|
auditInfo.setKeyPropertiesAfter(null);
|
|
auditInfo.setKeyPropertiesBefore(null);
|
|
auditInfo.setMessage(description);
|
|
if (args != null)
|
|
{
|
|
Serializable[] serArgs = new Serializable[args.length];
|
|
for (int i = 0; i < args.length; i++)
|
|
{
|
|
if (args[i] == null)
|
|
{
|
|
serArgs[i] = null;
|
|
}
|
|
else if (args[i] instanceof Serializable)
|
|
{
|
|
serArgs[i] = (Serializable) args[i];
|
|
}
|
|
else
|
|
{
|
|
serArgs[i] = args[i].toString();
|
|
}
|
|
}
|
|
auditInfo.setMethodArguments(serArgs);
|
|
}
|
|
auditInfo.setPath(null);
|
|
auditInfo.setReturnObject(null);
|
|
auditInfo.setSessionId(null);
|
|
auditInfo.setThrowable(null);
|
|
auditInfo.setTxId(AlfrescoTransactionSupport.getTransactionId());
|
|
auditInfo.setUserIdentifier(AuthenticationUtil.getCurrentUserName());
|
|
}
|
|
|
|
return effectiveAuditMode;
|
|
}
|
|
|
|
private AuditMode onError(AuditMode auditMode, AuditState auditInfo, Throwable t, String source,
|
|
String description, NodeRef key, Object... args)
|
|
{
|
|
if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL))
|
|
{
|
|
auditInfo.setFail(true);
|
|
auditInfo.setThrowable(t);
|
|
}
|
|
|
|
return auditMode;
|
|
|
|
}
|
|
}
|