You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by di...@apache.org on 2020/08/06 09:04:00 UTC

[syncope] branch master updated: [SYNCOPE-1584] WA events management (#210)

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

dimaayash pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new 2e4be06  [SYNCOPE-1584] WA events management (#210)
2e4be06 is described below

commit 2e4be06d2b8460df7fd17a8e230adbd6f117ec13
Author: DimaAy <di...@tirasa.net>
AuthorDate: Thu Aug 6 11:03:53 2020 +0200

    [SYNCOPE-1584] WA events management (#210)
    
    * [SYNCOPE-1584] WA events management
    
    addd ITcase
    
    add wa test
    
    wa audit test
    
    * rename
---
 .../src/test/resources/domains/MasterContent.xml   |  2 +
 .../org/apache/syncope/fit/core/LoggerITCase.java  | 27 ++++++++
 pom.xml                                            |  5 ++
 wa/starter/pom.xml                                 |  4 ++
 .../starter/audit/SyncopeWAAuditTrailManager.java  |  3 +-
 .../wa/starter/config/SyncopeWAConfiguration.java  | 27 ++++++--
 .../SyncopeWAEventRepository.java}                 | 76 ++++++++++++----------
 .../audit/SyncopeWAAuditTrailManagerTest.java      | 59 +++++++++++++++++
 .../events/SyncopeWAEventRepositoryTest.java       | 60 +++++++++++++++++
 9 files changed, 224 insertions(+), 39 deletions(-)

diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 324c3be..17f3dd4 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -2482,6 +2482,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.[WA]:[LoggerLogic]:[AuthenticationEvent]:[auth]:[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 70171ff..227350f 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
@@ -298,6 +298,33 @@ public class LoggerITCase extends AbstractITCase {
         assertNotNull(events);
         assertEquals(1, events.getSize());
     }
+    
+    @Test
+    public void saveAuthEvent() {
+        AuditEntry auditEntry = new AuditEntry();
+        auditEntry.setWho("syncope-user " + UUID.randomUUID().toString());
+        auditEntry.setLogger(new AuditLoggerName(
+                EventCategoryType.WA,
+                "LoggerLogic",
+                "AuthenticationEvent",
+                "auth",
+                AuditElements.Result.SUCCESS));
+        auditEntry.setDate(new Date());
+        auditEntry.setBefore(UUID.randomUUID().toString());
+        auditEntry.setOutput(UUID.randomUUID().toString());
+        assertDoesNotThrow(() -> loggerService.create(auditEntry));
+
+        PagedResult<AuditEntry> events = loggerService.search(new AuditQuery.Builder().
+                size(1).
+                type(auditEntry.getLogger().getType()).
+                category(auditEntry.getLogger().getCategory()).
+                subcategory(auditEntry.getLogger().getSubcategory()).
+                event(auditEntry.getLogger().getEvent()).
+                result(auditEntry.getLogger().getResult()).
+                build());
+        assertNotNull(events);
+        assertEquals(1, events.getSize());
+    }
 
     @Test
     public void customAuditAppender() throws IOException, InterruptedException {
diff --git a/pom.xml b/pom.xml
index 2f93b31..a7372d6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1402,6 +1402,11 @@ under the License.
       </dependency>
       <dependency>
         <groupId>org.apereo.cas</groupId>
+        <artifactId>cas-server-core-events-api</artifactId>
+        <version>${cas.version}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.apereo.cas</groupId>
         <artifactId>cas-server-core-authentication</artifactId>
         <version>${cas.version}</version>
       </dependency>
diff --git a/wa/starter/pom.xml b/wa/starter/pom.xml
index f62cf31..2b9e7fd 100644
--- a/wa/starter/pom.xml
+++ b/wa/starter/pom.xml
@@ -76,6 +76,10 @@ under the License.
     </dependency>
     <dependency>
       <groupId>org.apereo.cas</groupId>
+      <artifactId>cas-server-core-events-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/audit/SyncopeWAAuditTrailManager.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManager.java
index b7f0ce5..42f1adc 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManager.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManager.java
@@ -18,6 +18,7 @@
  */
 package org.apache.syncope.wa.starter.audit;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import java.time.LocalDate;
 import java.util.Map;
 import java.util.Set;
@@ -76,7 +77,7 @@ public class SyncopeWAAuditTrailManager extends AbstractAuditTrailManager {
 
             auditEntry.setLogger(auditLogger);
             syncopeClient.getService(LoggerService.class).create(auditEntry);
-        } catch (Exception e) {
+        } catch (JsonProcessingException e) {
             LOG.error("During serialization", e);
         }
     }
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
index c53c8fa..421b2a5 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/config/SyncopeWAConfiguration.java
@@ -26,11 +26,6 @@ import io.swagger.v3.oas.models.info.Info;
 import org.apache.commons.lang3.StringUtils;
 import com.warrenstrange.googleauth.IGoogleAuthenticator;
 import io.swagger.v3.oas.models.security.SecurityScheme;
-import java.time.LocalDate;
-import java.time.ZoneId;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
 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;
@@ -79,6 +74,15 @@ import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.Bean;
 
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.syncope.wa.starter.events.SyncopeWAEventRepository;
+import org.apereo.cas.support.events.CasEventRepository;
+import org.apereo.cas.support.events.CasEventRepositoryFilter;
+
 public class SyncopeWAConfiguration {
 
     @Autowired
@@ -196,6 +200,19 @@ public class SyncopeWAConfiguration {
         return plan -> plan.registerAuditTrailManager(new SyncopeWAAuditTrailManager(restClient));
     }
 
+    @ConditionalOnMissingBean(name = "syncopWaEventRepositoryFilter")
+    @Bean
+    public CasEventRepositoryFilter syncopeWAEventRepositoryFilter() {
+        return CasEventRepositoryFilter.noOp();
+    }
+
+    @Autowired
+    @Bean
+    public CasEventRepository casEventRepository(final WARestClient restClient) {
+        return new SyncopeWAEventRepository(syncopeWAEventRepositoryFilter(), restClient);
+    }
+
+
     @Autowired
     @Bean
     public DelegatedClientFactoryCustomizer<Client<?>> delegatedClientCustomizer(final WARestClient restClient) {
diff --git a/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManager.java b/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepository.java
similarity index 50%
copy from wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManager.java
copy to wa/starter/src/main/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepository.java
index b7f0ce5..fdcb4c3 100644
--- a/wa/starter/src/main/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManager.java
+++ b/wa/starter/src/main/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepository.java
@@ -16,78 +16,88 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.syncope.wa.starter.audit;
+package org.apache.syncope.wa.starter.events;
 
-import java.time.LocalDate;
-import java.util.Map;
-import java.util.Set;
-import org.apereo.cas.audit.spi.AbstractAuditTrailManager;
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.client.lib.SyncopeClient;
 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.wa.bootstrap.WARestClient;
-import org.apereo.inspektr.audit.AuditActionContext;
+import org.apereo.cas.support.events.CasEventRepositoryFilter;
+import org.apereo.cas.support.events.dao.AbstractCasEventRepository;
+import org.apereo.cas.support.events.dao.CasEvent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import org.apache.syncope.client.lib.SyncopeClient;
+public class SyncopeWAEventRepository extends AbstractCasEventRepository {
 
-public class SyncopeWAAuditTrailManager extends AbstractAuditTrailManager {
-
-    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAAuditTrailManager.class);
+    private static final Logger LOG = LoggerFactory.getLogger(SyncopeWAEventRepository.class);
 
     private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
 
     private final WARestClient waRestClient;
 
-    public SyncopeWAAuditTrailManager(final WARestClient restClient) {
-        super(true);
+    public SyncopeWAEventRepository(final CasEventRepositoryFilter eventRepositoryFilter,
+            final WARestClient restClient) {
+        super(eventRepositoryFilter);
         this.waRestClient = restClient;
     }
 
+    public void put(final Map<String, String> properties, final String key, final String value) {
+        if (StringUtils.isNotBlank(value)) {
+            properties.put(key, value);
+        }
+    }
+
     @Override
-    protected void saveAuditRecord(final AuditActionContext audit) {
+    public void saveInternal(final CasEvent event) {
         SyncopeClient syncopeClient = waRestClient.getSyncopeClient();
         if (syncopeClient == null) {
             LOG.debug("Syncope client is not yet ready to store audit record");
             return;
         }
 
-        LOG.info("Loading application definitions");
+        LOG.info("Saving Cas events");
         try {
-            String output = OBJECT_MAPPER.writeValueAsString(Map.of("resource", audit.getResourceOperatedUpon(),
-                    "clientIpAddress", audit.getClientIpAddress(),
-                    "serverIpAddress", audit.getServerIpAddress()));
+            Map<String, String> properties = new HashMap<>();
+            if (event.getGeoLocation() != null) {
+                put(properties, "geoLatitude", event.getGeoLocation().getLatitude());
+                put(properties, "geoLongitude", event.getGeoLocation().getLongitude());
+                put(properties, "geoAccuracy", event.getGeoLocation().getAccuracy());
+                put(properties, "geoTimestamp", event.getGeoLocation().getTimestamp());
+            }
+            put(properties, "clientIpAddress", event.getClientIpAddress());
+            put(properties, "serverIpAddress", event.getServerIpAddress());
+
+            String output = OBJECT_MAPPER.writeValueAsString(properties);
 
             AuditEntry auditEntry = new AuditEntry();
-            auditEntry.setWho(audit.getPrincipal());
-            auditEntry.setDate(audit.getWhenActionWasPerformed());
+            auditEntry.setWho(event.getPrincipalId());
+            if (event.getTimestamp() != null) {
+                auditEntry.setDate(new Date(event.getTimestamp()));
+            }
             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);
-
+                    "LoggerLogic", event.getType().toUpperCase(),
+                    String.valueOf(event.getId()), AuditElements.Result.SUCCESS);
             auditEntry.setLogger(auditLogger);
             syncopeClient.getService(LoggerService.class).create(auditEntry);
-        } catch (Exception e) {
+        } catch (JsonProcessingException e) {
             LOG.error("During serialization", e);
         }
     }
 
     @Override
-    public Set<? extends AuditActionContext> getAuditRecordsSince(final LocalDate sinceDate) {
-        throw new UnsupportedOperationException("Fetching audit events from WA is not supported");
+    public Collection<? extends CasEvent> load() {
+        throw new UnsupportedOperationException("Fetching authentication 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/test/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManagerTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManagerTest.java
new file mode 100644
index 0000000..79d2a64
--- /dev/null
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/audit/SyncopeWAAuditTrailManagerTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.audit;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Date;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.log.AuditEntry;
+import org.apache.syncope.common.rest.api.service.LoggerService;
+import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.apache.syncope.wa.starter.AbstractTest;
+import org.apereo.inspektr.audit.AuditActionContext;
+import org.junit.jupiter.api.Test;
+
+public class SyncopeWAAuditTrailManagerTest extends AbstractTest {
+
+    private static LoggerService loggerService;
+
+    private static WARestClient getWaRestClient() {
+        WARestClient restClient = mock(WARestClient.class);
+        SyncopeClient syncopeClient = mock(SyncopeClient.class);
+        loggerService = mock(LoggerService.class);
+
+        when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
+        when(syncopeClient.getService(LoggerService.class)).thenReturn(loggerService);
+
+        return restClient;
+    }
+
+    @Test
+    public void saveAuditRecord() {
+        AuditActionContext audit = new AuditActionContext("principal", "resourceOperatedUpon", "actionPerformed",
+                "applicationCode", new Date(), "clientIpAddress", "serverIpAddress");
+        SyncopeWAAuditTrailManager auditTrailManager = new SyncopeWAAuditTrailManager(getWaRestClient());
+        auditTrailManager.saveAuditRecord(audit);
+        verify(loggerService).create(any(AuditEntry.class));
+    }
+
+}
diff --git a/wa/starter/src/test/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepositoryTest.java b/wa/starter/src/test/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepositoryTest.java
new file mode 100644
index 0000000..e823ed3
--- /dev/null
+++ b/wa/starter/src/test/java/org/apache/syncope/wa/starter/events/SyncopeWAEventRepositoryTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.events;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Map;
+import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.log.AuditEntry;
+import org.apache.syncope.common.rest.api.service.LoggerService;
+import org.apache.syncope.wa.bootstrap.WARestClient;
+import org.apache.syncope.wa.starter.AbstractTest;
+import org.apereo.cas.support.events.CasEventRepositoryFilter;
+import org.apereo.cas.support.events.dao.CasEvent;
+import org.junit.jupiter.api.Test;
+
+public class SyncopeWAEventRepositoryTest extends AbstractTest {
+
+    private static LoggerService loggerService;
+
+    private static WARestClient getWaRestClient() {
+        WARestClient restClient = mock(WARestClient.class);
+        SyncopeClient syncopeClient = mock(SyncopeClient.class);
+        loggerService = mock(LoggerService.class);
+
+        when(restClient.getSyncopeClient()).thenReturn(syncopeClient);
+        when(syncopeClient.getService(LoggerService.class)).thenReturn(loggerService);
+
+        return restClient;
+    }
+
+    @Test
+    public void saveInternal() {
+        CasEvent event = new CasEvent(1L, "Auth", "principalId", "creationTime", Map.of("timestamp", "1"));
+        SyncopeWAEventRepository eventRepository = new SyncopeWAEventRepository(CasEventRepositoryFilter.noOp(),
+                getWaRestClient());
+        eventRepository.saveInternal(event);
+        verify(loggerService).create(any(AuditEntry.class));
+    }
+
+}