/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.rrm.ehour.audit.aspect;
import net.rrm.ehour.audit.annot.Auditable;
import net.rrm.ehour.audit.annot.NonAuditable;
import net.rrm.ehour.audit.service.AuditService;
import net.rrm.ehour.config.EhourConfig;
import net.rrm.ehour.config.service.ConfigurationService;
import net.rrm.ehour.domain.Audit;
import net.rrm.ehour.domain.AuditActionType;
import net.rrm.ehour.domain.AuditType;
import net.rrm.ehour.domain.User;
import net.rrm.ehour.ui.common.session.EhourWebSession;
import org.apache.wicket.core.request.handler.IPageRequestHandler;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.cycle.PageRequestHandlerTracker;
import org.apache.wicket.request.cycle.RequestCycle;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.lang.annotation.Annotation;
import java.util.Calendar;
import java.util.Date;
/**
* Auditable Aspect
*/
@Aspect
@Service
public class AuditAspect {
@Autowired
private AuditService auditService;
@Autowired
private ConfigurationService configurationService;
@Pointcut("execution(public * net.rrm.ehour.*.service.*Service*.get*(..)) && " +
"!@annotation(net.rrm.ehour.audit.annot.Auditable) && " +
"!@annotation(net.rrm.ehour.audit.annot.NonAuditable)")
public void publicGetMethod() {
}
@Pointcut("execution(public * net.rrm.ehour.*.service.*Service.persist*(..)) && " +
"!@annotation(net.rrm.ehour.audit.annot.Auditable) && " +
"!@annotation(net.rrm.ehour.audit.annot.NonAuditable)")
public void publicPersistMethod() {
}
@Pointcut("execution(public * net.rrm.ehour.*.service.*Service.delete*(..)) && " +
"!@annotation(net.rrm.ehour.audit.annot.Auditable) && " +
"!@annotation(net.rrm.ehour.audit.annot.NonAuditable)")
public void publicDeleteMethod() {
}
/**
* Around advise for read-only get methods
*
* @param pjp
* @return
* @throws Throwable
*/
@Around("net.rrm.ehour.audit.aspect.AuditAspect.publicGetMethod()")
public Object auditOnGet(ProceedingJoinPoint pjp) throws Throwable {
return doAudit(pjp, AuditActionType.READ);
}
/**
* Around advise for persist methods
*
* @param pjp
* @return
* @throws Throwable
*/
@Around("net.rrm.ehour.audit.aspect.AuditAspect.publicPersistMethod()")
public Object auditOnPersist(ProceedingJoinPoint pjp) throws Throwable {
return doAudit(pjp, AuditActionType.UPDATE);
}
/**
* Around advise for delete methods
*
* @param pjp
* @return
* @throws Throwable
*/
@Around("net.rrm.ehour.audit.aspect.AuditAspect.publicDeleteMethod()")
public Object auditOnDelete(ProceedingJoinPoint pjp) throws Throwable {
return doAudit(pjp, AuditActionType.DELETE);
}
/**
* Audit
*
* @param pjp
* @param auditable
* @return
* @throws Throwable
*/
@Around("@annotation(auditable)")
public Object doAudit(ProceedingJoinPoint pjp, Auditable auditable) throws Throwable {
return doAudit(pjp, auditable.actionType());
}
/**
* @param pjp
* @param auditActionType
* @return
* @throws Throwable
*/
private Object doAudit(ProceedingJoinPoint pjp, AuditActionType auditActionType) throws Throwable {
Object returnObject;
boolean isAuditable = isAuditable(pjp) && isAuditEnabled(auditActionType);
User user = getUser();
try {
returnObject = pjp.proceed();
} catch (Exception t) {
if (isAuditable) {
auditService.doAudit(createAudit(user, Boolean.FALSE, auditActionType, pjp));
}
throw t;
}
if (isAuditable) {
auditService.doAudit(createAudit(user, Boolean.TRUE, auditActionType, pjp));
}
return returnObject;
}
/**
* Is audit type enabled
*
* @param actionType
* @return
*/
private boolean isAuditEnabled(AuditActionType actionType) {
EhourConfig config = configurationService.getConfiguration();
if (config.getAuditType() == AuditType.NONE) {
return false;
}
if (config.getAuditType() == AuditType.WRITE &&
actionType.getAuditType() == config.getAuditType()) {
return true;
}
return config.getAuditType() == AuditType.ALL;
}
/**
* Check if type is annotated with NonAuditable annotation
*
* @param pjp
* @return
*/
private boolean isAuditable(ProceedingJoinPoint pjp) {
Annotation[] annotations = pjp.getSignature().getDeclaringType().getAnnotations();
boolean nonAuditable = false;
for (Annotation annotation : annotations) {
nonAuditable = annotation.annotationType() == NonAuditable.class;
if (nonAuditable) {
break;
}
}
return !nonAuditable;
}
private User getUser() {
User user;
try {
user = EhourWebSession.getUser();
} catch (Exception t) {
user = null;
}
return user;
}
private Audit createAudit(User user, Boolean success, AuditActionType auditActionType, ProceedingJoinPoint pjp) {
String parameters = getAuditParameters(pjp);
String page = null;
RequestCycle cycle = RequestCycle.get();
if (cycle != null) {
IPageRequestHandler lastHandler = PageRequestHandlerTracker.getLastHandler(cycle);
if (lastHandler != null) {
Class<? extends IRequestablePage> pageClass = lastHandler.getPageClass();
if (pageClass != null) {
page = pageClass.getCanonicalName();
}
}
}
return new Audit()
.setUser(user)
.setUserFullName(user != null ? user.getFullName() : null)
.setDate(new Date())
.setSuccess(success)
.setAction(pjp.getSignature().toShortString())
.setAuditActionType(auditActionType)
.setParameters(parameters)
.setPage(page)
;
}
/**
* Get parameters of advised method
*
* @param pjp
* @return
*/
private String getAuditParameters(ProceedingJoinPoint pjp) {
StringBuilder parameters = new StringBuilder();
int i = 0;
for (Object object : pjp.getArgs()) {
parameters.append(i++).append(":");
if (object == null) {
parameters.append("null");
} else if (object instanceof Calendar) {
parameters.append(((Calendar) object).getTime().toString());
} else {
parameters.append(object.toString());
}
}
return parameters.length() > 1024 ? parameters.substring(0, 1023) : parameters.toString();
}
/**
* @param auditService the auditService to set
*/
public void setAuditService(AuditService auditService) {
this.auditService = auditService;
}
/**
* @param configurationService the configurationService to set
*/
public void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
}