diff --git a/sormas-backend/src/main/java/de/symeda/sormas/backend/audit/AuditLoggerInterceptor.java b/sormas-backend/src/main/java/de/symeda/sormas/backend/audit/AuditLoggerInterceptor.java index d682d28dc84..87f9d5fe941 100644 --- a/sormas-backend/src/main/java/de/symeda/sormas/backend/audit/AuditLoggerInterceptor.java +++ b/sormas-backend/src/main/java/de/symeda/sormas/backend/audit/AuditLoggerInterceptor.java @@ -22,11 +22,23 @@ import de.symeda.sormas.backend.auditlog.AuditContextProducer; import de.symeda.sormas.backend.auditlog.AuditLogServiceBean; +import de.symeda.sormas.backend.campaign.CampaignService; +import de.symeda.sormas.backend.caze.CaseService; +import de.symeda.sormas.backend.common.AbstractDomainObject; import de.symeda.sormas.backend.common.ConfigFacadeEjb; +import de.symeda.sormas.backend.contact.ContactService; +import de.symeda.sormas.backend.event.EventParticipantService; +import de.symeda.sormas.backend.event.EventService; import de.symeda.sormas.backend.feature.FeatureConfigurationFacadeEjb; import de.symeda.sormas.backend.i18n.I18nFacadeEjb; +import de.symeda.sormas.backend.immunization.DirectoryImmunizationService; +import de.symeda.sormas.backend.immunization.ImmunizationService; import de.symeda.sormas.backend.infrastructure.continent.ContinentFacadeEjb; import de.symeda.sormas.backend.infrastructure.subcontinent.SubcontinentFacadeEjb; +import de.symeda.sormas.backend.sample.PathogenTestService; +import de.symeda.sormas.backend.sample.SampleService; +import de.symeda.sormas.backend.travelentry.services.TravelEntryListService; +import de.symeda.sormas.backend.travelentry.services.TravelEntryService; import de.symeda.sormas.backend.user.CurrentUserService; import de.symeda.sormas.backend.user.UserFacadeEjb; @@ -39,7 +51,23 @@ public class AuditLoggerInterceptor { /** * Cache to track all classes that should be ignored and those who must be audited. False indicates audit, True ignore */ - private static final Map, Boolean> shouldIgnoreClassCache = new HashMap<>(); + private static final Map, Boolean> shouldIgnoreClassCache; + static { + Map, Boolean> map = new HashMap<>(); + // explicitly add all services doing deletion here to bypass the local bean check + map.put(CaseService.class, false); + map.put(CampaignService.class, false); + map.put(ContactService.class, false); + map.put(DirectoryImmunizationService.class, false); + map.put(EventParticipantService.class, false); + map.put(EventService.class, false); + map.put(ImmunizationService.class, false); + map.put(PathogenTestService.class, false); + map.put(SampleService.class, false); + map.put(TravelEntryListService.class, false); + map.put(TravelEntryService.class, false); + shouldIgnoreClassCache = map; + } /** * Cache to track all classes which should be completely ignored for audit. @@ -74,6 +102,35 @@ public class AuditLoggerInterceptor { } } + /** + * Cache to track all local methods which should be audited. + */ + private static final Set allowedLocalAuditMethods; + + public static final String DELETE_PERMANENT = "deletePermanent"; + + static { + try { + allowedLocalAuditMethods = Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + CaseService.class.getMethod(DELETE_PERMANENT, AbstractDomainObject.class), + CampaignService.class.getMethod(DELETE_PERMANENT, AbstractDomainObject.class), + ContactService.class.getMethod(DELETE_PERMANENT, AbstractDomainObject.class), + DirectoryImmunizationService.class.getMethod(DELETE_PERMANENT, AbstractDomainObject.class), + EventParticipantService.class.getMethod(DELETE_PERMANENT, AbstractDomainObject.class), + EventService.class.getMethod(DELETE_PERMANENT, AbstractDomainObject.class), + ImmunizationService.class.getMethod(DELETE_PERMANENT, AbstractDomainObject.class), + PathogenTestService.class.getMethod(DELETE_PERMANENT, AbstractDomainObject.class), + SampleService.class.getMethod(DELETE_PERMANENT, AbstractDomainObject.class), + TravelEntryListService.class.getMethod(DELETE_PERMANENT, AbstractDomainObject.class), + TravelEntryService.class.getMethod(DELETE_PERMANENT, AbstractDomainObject.class) + ))); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + @AroundTimeout public Object logTimeout(InvocationContext context) throws Exception { if (AuditLoggerEjb.isLoggingDisabled()) { @@ -95,20 +152,19 @@ public Object logAudit(InvocationContext context) throws Exception { // ignore certain classes for audit altogether. Statically populated cache. return context.proceed(); } + Method calledMethod = context.getMethod(); // with this we ignore EJB calls which definitely originate from within the backend // as they can never be called direct from outside (i.e., remote) of the backend // expression yields true if it IS a local bean => should not be audited and ignored Boolean shouldIgnoreAudit = shouldIgnoreClassCache.computeIfAbsent(target, k -> target.getAnnotationsByType(LocalBean.class).length > 0); - if (shouldIgnoreAudit) { + if (Boolean.TRUE.equals(shouldIgnoreAudit)) { // ignore local beans return context.proceed(); } - Method calledMethod = context.getMethod(); - - if (ignoreAuditMethods.contains(calledMethod)) { + if (ignoreAuditMethods.contains(calledMethod) || !allowedLocalAuditMethods.contains(calledMethod)) { // ignore certain methods for audit altogether. Statically populated cache. return context.proceed(); }