You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by mm...@apache.org on 2020/04/10 11:29:17 UTC

[syncope] branch SYNCOPE-1552 created (now 13d6c85)

This is an automated email from the ASF dual-hosted git repository.

mmoayyed pushed a change to branch SYNCOPE-1552
in repository https://gitbox.apache.org/repos/asf/syncope.git.


      at 13d6c85  SYNCOPE-1552: Allow WA audit events to be stored in core

This branch includes the following new commits:

     new 13d6c85  SYNCOPE-1552: Allow WA audit events to be stored in core

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[syncope] 01/01: SYNCOPE-1552: Allow WA audit events to be stored in core

Posted by mm...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mmoayyed pushed a commit to branch SYNCOPE-1552
in repository https://gitbox.apache.org/repos/asf/syncope.git

commit 13d6c850779e0996daea41fabb30c265b541313b
Author: Misagh Moayyed <mm...@gmail.com>
AuthorDate: Fri Apr 10 15:58:56 2020 +0430

    SYNCOPE-1552: Allow WA audit events to be stored in core
---
 .../client/console/rest/LoggerRestClient.java      |  4 +
 .../syncope/common/lib/types/AuditElements.java    |  1 +
 .../common/lib/types/IdRepoEntitlement.java        |  2 +
 .../common/rest/api/service/LoggerService.java     | 12 +++
 .../org/apache/syncope/core/logic/LoggerLogic.java | 27 ++++++-
 .../core/rest/cxf/service/LoggerServiceImpl.java   |  5 ++
 .../core/persistence/api/dao/LoggerDAO.java        |  7 +-
 .../src/main/resources/domains/MasterContent.xml   |  2 -
 .../core/persistence/jpa/dao/JPALoggerDAO.java     | 15 +++-
 .../src/test/resources/domains/MasterContent.xml   |  2 +
 .../org/apache/syncope/fit/core/LoggerITCase.java  | 32 ++++++++
 pom.xml                                            |  5 ++
 wa/starter/pom.xml                                 | 11 ++-
 .../wa/starter/SyncopeWAAuditTrailManager.java     | 89 ++++++++++++++++++++++
 .../syncope/wa/starter/SyncopeWAConfiguration.java | 17 ++++-
 15 files changed, 219 insertions(+), 12 deletions(-)

diff --git a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerRestClient.java b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerRestClient.java
index 462b608..4ebc5f2 100644
--- a/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerRestClient.java
+++ b/client/idrepo/console/src/main/java/org/apache/syncope/client/console/rest/LoggerRestClient.java
@@ -58,6 +58,10 @@ public class LoggerRestClient extends BaseRestClient {
         return result;
     }
 
+    public static void create(final AuditEntry auditEntry) {
+        getService(LoggerService.class).create(auditEntry);
+    }
+    
     public static List<LoggerTO> listLogs() {
         List<LoggerTO> logs = getService(LoggerService.class).list(LoggerType.LOG);
         logs.sort(Comparator.comparing(LoggerTO::getKey));
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/AuditElements.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/AuditElements.java
index 32e87a2..e1116bd 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/AuditElements.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/AuditElements.java
@@ -33,6 +33,7 @@ public final class AuditElements implements Serializable {
     public enum EventCategoryType {
 
         LOGIC("LOGIC"),
+        WA("WA"),
         TASK("TASK"),
         PROPAGATION("PropagationTask"),
         PULL("PullTask"),
diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoEntitlement.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoEntitlement.java
index 8ab42af..2fdec6b 100644
--- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoEntitlement.java
+++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/IdRepoEntitlement.java
@@ -212,6 +212,8 @@ public final class IdRepoEntitlement {
 
     public static final String AUDIT_READ = "AUDIT_READ";
 
+    public static final String AUDIT_CREATE = "AUDIT_CREATE";
+
     public static final String AUDIT_ENABLE = "AUDIT_ENABLE";
 
     public static final String AUDIT_DISABLE = "AUDIT_DISABLE";
diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/LoggerService.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/LoggerService.java
index 1b494a3..7adb977 100644
--- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/LoggerService.java
+++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/LoggerService.java
@@ -32,6 +32,7 @@ import javax.ws.rs.BeanParam;
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
+import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
@@ -100,6 +101,17 @@ public interface LoggerService extends JAXRSService {
     PagedResult<AuditEntry> search(@BeanParam AuditQuery auditQuery);
 
     /**
+     * Create an audit entry.
+     *
+     * @param auditEntry audit entry to persist.
+     */
+    @POST
+    @Path("AUDIT/entries")
+    @Consumes({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML })
+    void create(@NotNull AuditEntry auditEntry);
+    
+    /**
      * Returns logger with matching type and name.
      *
      * @param type LoggerType to be selected.
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
index 3b1fe49..3b65d05 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
@@ -59,6 +59,7 @@ import org.apache.syncope.core.persistence.api.dao.NotFoundException;
 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
 import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.api.entity.Logger;
+import org.apache.syncope.core.provisioning.api.AuditManager;
 import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
 import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
@@ -98,6 +99,9 @@ public class LoggerLogic extends AbstractTransactionalLogic<EntityTO> {
     @Autowired
     private LoggerDataBinder binder;
 
+    @Autowired
+    private AuditManager auditManager;
+    
     @PreAuthorize("hasRole('" + IdRepoEntitlement.LOG_LIST + "') and authentication.details.domain == "
             + "T(org.apache.syncope.common.lib.SyncopeConstants).MASTER_DOMAIN")
     @Transactional(readOnly = true)
@@ -409,12 +413,33 @@ public class LoggerLogic extends AbstractTransactionalLogic<EntityTO> {
             final AuditElements.Result result,
             final List<OrderByClause> orderByClauses) {
 
-        int count = loggerDAO.countAuditEntries(entityKey);
+        int count = loggerDAO.countAuditEntries(entityKey, type, category, subcategory, events, result);
         List<AuditEntry> matching = loggerDAO.findAuditEntries(
                 entityKey, page, size, type, category, subcategory, events, result, orderByClauses);
         return Pair.of(count, matching);
     }
 
+    @PreAuthorize("hasRole('" + IdRepoEntitlement.AUDIT_CREATE + "')")
+    public void create(final AuditEntry auditEntry) {
+        boolean auditRequested = auditManager.auditRequested(auditEntry.getWho(),
+            EventCategoryType.CUSTOM,
+            auditEntry.getLogger().getCategory(),
+            auditEntry.getLogger().getSubcategory(),
+            auditEntry.getLogger().getEvent());
+
+        if (auditRequested) {
+            auditManager.audit(auditEntry.getWho(),
+                EventCategoryType.CUSTOM,
+                auditEntry.getLogger().getCategory(),
+                auditEntry.getLogger().getSubcategory(),
+                auditEntry.getLogger().getEvent(),
+                auditEntry.getLogger().getResult(),
+                auditEntry.getBefore(),
+                auditEntry.getOutput(),
+                auditEntry.getInputs());
+        }
+    }
+
     @Override
     protected EntityTO resolveReference(final Method method, final Object... args)
             throws UnresolvedReferenceException {
diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/LoggerServiceImpl.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/LoggerServiceImpl.java
index b8ab5b4..9367c9d 100644
--- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/LoggerServiceImpl.java
+++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/LoggerServiceImpl.java
@@ -52,6 +52,11 @@ public class LoggerServiceImpl extends AbstractServiceImpl implements LoggerServ
     public List<LogStatement> getLastLogStatements(final String memoryAppender) {
         return logic.getLastLogStatements(memoryAppender);
     }
+    
+    @Override
+    public void create(final AuditEntry auditEntry) {
+        logic.create(auditEntry);
+    }
 
     @Override
     public void delete(final LoggerType type, final String name) {
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/LoggerDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/LoggerDAO.java
index e8cfe5a..e7e1b43 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/LoggerDAO.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/LoggerDAO.java
@@ -52,5 +52,10 @@ public interface LoggerDAO extends DAO<Logger> {
             AuditElements.Result result,
             List<OrderByClause> orderByClauses);
 
-    int countAuditEntries(String entityKey);
+    int countAuditEntries(String entityKey,
+                          AuditElements.EventCategoryType type,
+                          String category,
+                          String subcategory,
+                          List<String> events,
+                          AuditElements.Result result);
 }
diff --git a/core/persistence-jpa-json/src/main/resources/domains/MasterContent.xml b/core/persistence-jpa-json/src/main/resources/domains/MasterContent.xml
index 9ae7908..b987957 100644
--- a/core/persistence-jpa-json/src/main/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa-json/src/main/resources/domains/MasterContent.xml
@@ -20,8 +20,6 @@ under the License.
 <dataset>
   <Realm id="ea696a4f-e77a-4ef1-be67-8f8093bc8686" name="/"/>  
 
-<<<<<<< HEAD
-=======
   <SyncopeSchema id="password.cipher.algorithm"/>
   <PlainSchema id="password.cipher.algorithm" type="String"
                mandatoryCondition="true" multivalue="0" uniqueConstraint="0" readonly="0"/>
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPALoggerDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPALoggerDAO.java
index 6e85952..1bd2c99 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPALoggerDAO.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPALoggerDAO.java
@@ -139,9 +139,20 @@ public class JPALoggerDAO extends AbstractDAO<Logger> implements LoggerDAO {
     }
 
     @Override
-    public int countAuditEntries(final String entityKey) {
+    public int countAuditEntries(final String entityKey,
+                                 final AuditElements.EventCategoryType type,
+                                 final String category,
+                                 final String subcategory,
+                                 final List<String> events,
+                                 final AuditElements.Result result) {
         String queryString = "SELECT COUNT(0) FROM " + AUDIT_TABLE
-                + " WHERE " + messageCriteriaBuilder(entityKey).build();
+            + " WHERE " + messageCriteriaBuilder(entityKey).
+            type(type).
+            category(category).
+            subcategory(subcategory).
+            result(result).
+            events(events).
+            build();
         Query countQuery = entityManager().createNativeQuery(queryString);
 
         return ((Number) countQuery.getSingleResult()).intValue();
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index a222994..dc7369d 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -2472,6 +2472,8 @@ $$ }&#10;
 
   <SyncopeLogger logType="AUDIT" logName="syncope.audit.[LOGIC]:[SyncopeLogic]:[]:[isSelfRegAllowed]:[SUCCESS]" logLevel="DEBUG"/>
 
+  <SyncopeLogger logType="AUDIT" logName="syncope.audit.[WA]:[LoggerLogic]:[AUTHENTICATION]:[validate]:[SUCCESS]" logLevel="DEBUG"/>
+
   <SyncopeLogger logType="AUDIT" logName="syncope.audit.[LOGIC]:[ConnectorLogic]:[]:[create]:[SUCCESS]" logLevel="DEBUG"/>
   <SyncopeLogger logType="AUDIT" logName="syncope.audit.[LOGIC]:[ConnectorLogic]:[]:[update]:[SUCCESS]" logLevel="DEBUG"/>
 
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java
index 7857c71..356431c 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 
 import java.io.File;
 import java.io.IOException;
@@ -33,18 +34,23 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardOpenOption;
 import java.text.ParseException;
+import java.util.Date;
 import java.util.List;
 import java.util.Properties;
+import java.util.UUID;
+
 import javax.ws.rs.core.Response;
 import javax.xml.ws.WebServiceException;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.syncope.common.lib.SyncopeClientException;
+import org.apache.syncope.common.lib.log.AuditEntry;
 import org.apache.syncope.common.lib.log.EventCategory;
 import org.apache.syncope.common.lib.log.LogAppender;
 import org.apache.syncope.common.lib.log.LogStatement;
 import org.apache.syncope.common.lib.log.LoggerTO;
 import org.apache.syncope.common.lib.to.ConnInstanceTO;
 import org.apache.syncope.common.lib.to.ConnPoolConfTO;
+import org.apache.syncope.common.lib.to.PagedResult;
 import org.apache.syncope.common.lib.to.PushTaskTO;
 import org.apache.syncope.common.lib.to.ResourceTO;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -57,6 +63,7 @@ import org.apache.syncope.common.lib.types.MatchingRule;
 import org.apache.syncope.common.lib.types.ResourceOperation;
 import org.apache.syncope.common.lib.types.UnmatchingRule;
 import org.apache.syncope.common.rest.api.LoggerWrapper;
+import org.apache.syncope.common.rest.api.beans.AuditQuery;
 import org.apache.syncope.common.rest.api.beans.ReconQuery;
 import org.apache.syncope.core.logic.ConnectorLogic;
 import org.apache.syncope.core.logic.ReportLogic;
@@ -268,6 +275,31 @@ public class LoggerITCase extends AbstractITCase {
     }
 
     @Test
+    public void saveAuditEvent() {
+        AuditLoggerName logger = new AuditLoggerName(EventCategoryType.CUSTOM, "LoggerLogic",
+            AuditElements.AUTHENTICATION_CATEGORY.toUpperCase(), "validate",
+            AuditElements.Result.SUCCESS);
+        AuditEntry auditEntry = new AuditEntry();
+        String who = "syncope-user " + UUID.randomUUID().toString();
+        auditEntry.setWho(who);
+        auditEntry.setLogger(logger);
+        auditEntry.setDate(new Date());
+        auditEntry.setBefore(UUID.randomUUID().toString());
+        auditEntry.setOutput(UUID.randomUUID().toString());
+        assertDoesNotThrow(() -> loggerService.create(auditEntry));
+
+        AuditQuery query = new AuditQuery();
+        query.setSize(1);
+        query.setType(auditEntry.getLogger().getType());
+        query.setResult(auditEntry.getLogger().getResult());
+        query.setCategory(auditEntry.getLogger().getCategory());
+        query.setEvents(List.of(auditEntry.getLogger().getEvent()));
+        PagedResult<AuditEntry> events = loggerService.search(query);
+        assertNotNull(events);
+        assertEquals(1, events.getSize());
+    }
+
+    @Test
     public void customAuditAppender() throws IOException, InterruptedException {
         AuditLoggerName auditLoggerResUpd = new AuditLoggerName(
                 EventCategoryType.LOGIC,
diff --git a/pom.xml b/pom.xml
index d66f64c..86f32e8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1585,6 +1585,11 @@ under the License.
       </dependency>
       <dependency>
         <groupId>org.apereo.cas</groupId>
+        <artifactId>cas-server-core-audit-api</artifactId>
+        <version>${cas.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apereo.cas</groupId>
         <artifactId>cas-server-support-saml-idp</artifactId>
         <version>${cas.version}</version>
       </dependency>
diff --git a/wa/starter/pom.xml b/wa/starter/pom.xml
index afd48ac..9f32ca6 100644
--- a/wa/starter/pom.xml
+++ b/wa/starter/pom.xml
@@ -43,13 +43,16 @@ under the License.
       <artifactId>syncope-wa-bootstrap</artifactId>
       <version>${project.version}</version>
     </dependency>
-
     <dependency>
       <groupId>org.apache.syncope.common.keymaster</groupId>
       <artifactId>syncope-common-keymaster-client-api</artifactId>
       <version>${project.version}</version>
     </dependency>
-
+    <dependency>
+      <groupId>org.apache.syncope.core</groupId>
+      <artifactId>syncope-core-provisioning-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.apache.syncope.client.am</groupId>
       <artifactId>syncope-client-am-lib</artifactId>
@@ -74,6 +77,10 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-core-audit-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apereo.cas</groupId>
       <artifactId>cas-server-core-cookie</artifactId>
     </dependency>
     <dependency>
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAAuditTrailManager.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAAuditTrailManager.java
new file mode 100644
index 0000000..9ad5a76
--- /dev/null
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAAuditTrailManager.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.syncope.wa.starter;
+
+import org.apereo.cas.audit.spi.AbstractAuditTrailManager;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.log.AuditEntry;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.common.rest.api.service.LoggerService;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.wa.WARestClient;
+import org.apereo.inspektr.audit.AuditActionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.LocalDate;
+import java.util.Map;
+import java.util.Set;
+
+public class SyncopeWAAuditTrailManager extends AbstractAuditTrailManager {
+    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAAuditTrailManager.class);
+
+    private final WARestClient restClient;
+
+    SyncopeWAAuditTrailManager(final WARestClient restClient) {
+        super(true);
+        this.restClient = restClient;
+    }
+
+    @Override
+    protected void saveAuditRecord(final AuditActionContext audit) {
+        if (!WARestClient.isReady()) {
+            LOG.debug("Syncope client is not yet ready to store audit record");
+            return;
+        }
+
+        LOG.info("Loading application definitions");
+        LoggerService loggerService = restClient.getSyncopeClient().
+            getService(LoggerService.class);
+
+        String output = POJOHelper.serialize(Map.of("resource", audit.getResourceOperatedUpon(),
+            "clientIpAddress", audit.getClientIpAddress(),
+            "serverIpAddress", audit.getServerIpAddress()));
+
+        AuditEntry auditEntry = new AuditEntry();
+        auditEntry.setWho(audit.getPrincipal());
+        auditEntry.setDate(audit.getWhenActionWasPerformed());
+        auditEntry.setOutput(output);
+        AuditElements.Result result = StringUtils.containsIgnoreCase(audit.getActionPerformed(), "fail")
+            ? AuditElements.Result.FAILURE
+            : AuditElements.Result.SUCCESS;
+
+        AuditLoggerName auditLogger = new AuditLoggerName(AuditElements.EventCategoryType.WA,
+            "LoggerLogic", AuditElements.AUTHENTICATION_CATEGORY.toUpperCase(),
+            audit.getActionPerformed(), result);
+
+        auditEntry.setLogger(auditLogger);
+        loggerService.create(auditEntry);
+    }
+
+    @Override
+    public Set<? extends AuditActionContext> getAuditRecordsSince(final LocalDate sinceDate) {
+        throw new UnsupportedOperationException("Fetching audit events from WA is not supported");
+    }
+
+    @Override
+    public void removeAll() {
+        throw new UnsupportedOperationException("Removing audit events from WA is not supported");
+    }
+}
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
index 938eb39..8b61f58 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/SyncopeWAConfiguration.java
@@ -18,19 +18,22 @@
  */
 package org.apache.syncope.wa.starter;
 
-import java.util.Collection;
+import org.apereo.cas.audit.AuditTrailExecutionPlanConfigurer;
+import org.apereo.cas.services.ServiceRegistryExecutionPlanConfigurer;
+import org.apereo.cas.services.ServiceRegistryListener;
+
 import org.apache.syncope.common.keymaster.client.api.model.NetworkService;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStart;
 import org.apache.syncope.common.keymaster.client.api.startstop.KeymasterStop;
 import org.apache.syncope.wa.WARestClient;
-import org.apereo.cas.services.ServiceRegistryExecutionPlanConfigurer;
-import org.apereo.cas.services.ServiceRegistryListener;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
+import java.util.Collection;
+
 @Configuration
 public class SyncopeWAConfiguration {
 
@@ -45,11 +48,17 @@ public class SyncopeWAConfiguration {
     @Bean
     public ServiceRegistryExecutionPlanConfigurer syncopeServiceRegistryConfigurer(final WARestClient restClient) {
         SyncopeServiceRegistry registry =
-                new SyncopeServiceRegistry(restClient, applicationContext, serviceRegistryListeners);
+            new SyncopeServiceRegistry(restClient, applicationContext, serviceRegistryListeners);
         return plan -> plan.registerServiceRegistry(registry);
     }
 
     @Bean
+    @Autowired
+    public AuditTrailExecutionPlanConfigurer auditConfigurer(final WARestClient restClient) {
+        return plan -> plan.registerAuditTrailManager(new SyncopeWAAuditTrailManager(restClient));
+    }
+
+    @Bean
     public KeymasterStart keymasterStart() {
         return new KeymasterStart(NetworkService.Type.WA);
     }