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 2019/11/27 14:43:22 UTC
[syncope] branch 2_1_X updated: SYNCOPE-1511: Provide Audit APIs
(#139)
This is an automated email from the ASF dual-hosted git repository.
mmoayyed pushed a commit to branch 2_1_X
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/2_1_X by this push:
new 9b816eb SYNCOPE-1511: Provide Audit APIs (#139)
9b816eb is described below
commit 9b816eb9a954a6238ce0bbb93b9d60c643b4aef1
Author: Misagh Moayyed <mm...@gmail.com>
AuthorDate: Wed Nov 27 18:43:15 2019 +0400
SYNCOPE-1511: Provide Audit APIs (#139)
* initial draft of audit services
* cntd with audit services design
* add builder methods
* clean up types
* working on build failures; increased fit timeout
* clean up API
* clean up API
* clean up API
* update audit config for tests
* clean up changes after review
* audit tests for groups
---
.../apache/syncope/common/lib/to/AuditEntryTO.java | 142 +++++++++++++++++++
.../common/lib/types/StandardEntitlement.java | 2 +
.../syncope/common/rest/api/beans/AuditQuery.java | 51 +++++++
.../common/rest/api/service/AuditService.java | 54 ++++++++
.../org/apache/syncope/core/logic/AuditLogic.java | 66 +++++++++
.../syncope/core/persistence/api/dao/AuditDAO.java | 30 +++++
.../core/persistence/api/entity/AuditEntry.java | 43 ++++++
.../core/persistence/jpa/dao/JPAAuditDAO.java | 108 +++++++++++++++
.../src/test/resources/domains/MasterContent.xml | 56 ++++++++
.../core/provisioning/api/AuditEntryImpl.java} | 133 ++++++++++++++++--
.../provisioning/api/data/AuditDataBinder.java | 28 ++++
.../provisioning/java/DefaultAuditManager.java | 37 +++--
.../java/data/AuditDataBinderImpl.java | 57 ++++++++
.../java/job/report/AuditReportlet.java | 5 +-
.../core/rest/cxf/service/AbstractAnyService.java | 150 ++++++++++-----------
.../core/rest/cxf/service/AuditServiceImpl.java | 48 +++++++
fit/core-reference/pom.xml | 2 +-
.../org/apache/syncope/fit/AbstractITCase.java | 4 +
.../org/apache/syncope/fit/core/AuditITCase.java | 108 +++++++++++++++
19 files changed, 1017 insertions(+), 107 deletions(-)
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/to/AuditEntryTO.java b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AuditEntryTO.java
new file mode 100644
index 0000000..fbaf0ca
--- /dev/null
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/to/AuditEntryTO.java
@@ -0,0 +1,142 @@
+/*
+ * 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.common.lib.to;
+
+import org.apache.syncope.common.lib.BaseBean;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+@XmlRootElement(name = "audit")
+@XmlType
+public class AuditEntryTO extends BaseBean implements EntityTO {
+ private static final long serialVersionUID = 1215115961911228005L;
+
+ private final List<String> inputs = new ArrayList<>();
+
+ private String who;
+
+ private String subCategory;
+
+ private String event;
+
+ private String result;
+
+ private String before;
+
+ private String output;
+
+ private Date date;
+
+ private String throwable;
+
+ private String key;
+
+ private String loggerName;
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(final Date date) {
+ this.date = date;
+ }
+
+ public String getThrowable() {
+ return throwable;
+ }
+
+ public void setThrowable(final String throwable) {
+ this.throwable = throwable;
+ }
+
+ public String getOutput() {
+ return output;
+ }
+
+ public void setOutput(final String output) {
+ this.output = output;
+ }
+
+ public String getBefore() {
+ return before;
+ }
+
+ public void setBefore(final String before) {
+ this.before = before;
+ }
+
+ public List<String> getInputs() {
+ return inputs;
+ }
+
+ public String getSubCategory() {
+ return subCategory;
+ }
+
+ public void setSubCategory(final String subCategory) {
+ this.subCategory = subCategory;
+ }
+
+ public String getEvent() {
+ return event;
+ }
+
+ public void setEvent(final String event) {
+ this.event = event;
+ }
+
+ public String getResult() {
+ return result;
+ }
+
+ public void setResult(final String result) {
+ this.result = result;
+ }
+
+ public String getWho() {
+ return who;
+ }
+
+ public void setWho(final String who) {
+ this.who = who;
+ }
+
+ public String getLoggerName() {
+ return loggerName;
+ }
+
+ public void setLoggerName(final String loggerName) {
+ this.loggerName = loggerName;
+ }
+
+ @Override
+ public String getKey() {
+ return this.key;
+ }
+
+ @Override
+ public void setKey(final String key) {
+ this.key = key;
+ }
+}
diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
index 3ca55a3..3d7bcf9 100644
--- a/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
+++ b/common/lib/src/main/java/org/apache/syncope/common/lib/types/StandardEntitlement.java
@@ -110,6 +110,8 @@ public final class StandardEntitlement {
public static final String SCHEMA_DELETE = "SCHEMA_DELETE";
+ public static final String AUDIT_SEARCH = "AUDIT_SEARCH";
+
public static final String USER_SEARCH = "USER_SEARCH";
public static final String USER_CREATE = "USER_CREATE";
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AuditQuery.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AuditQuery.java
new file mode 100644
index 0000000..d8152de
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AuditQuery.java
@@ -0,0 +1,51 @@
+/*
+ * 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.common.rest.api.beans;
+
+import javax.ws.rs.QueryParam;
+
+public class AuditQuery extends AbstractQuery {
+
+ private static final long serialVersionUID = -2863334226169614417L;
+
+ private String key;
+
+ public String getKey() {
+ return key;
+ }
+
+ @QueryParam("key")
+ public void setKey(final String key) {
+ this.key = key;
+ }
+
+ public static class Builder extends AbstractQuery.Builder<AuditQuery, Builder> {
+
+ public Builder key(final String keyword) {
+ getInstance().setKey(keyword);
+ return this;
+ }
+
+ @Override
+ protected AuditQuery newInstance() {
+ return new AuditQuery();
+ }
+ }
+
+}
diff --git a/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuditService.java b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuditService.java
new file mode 100644
index 0000000..897131e
--- /dev/null
+++ b/common/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuditService.java
@@ -0,0 +1,54 @@
+/*
+ * 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.common.rest.api.service;
+
+import io.swagger.v3.oas.annotations.security.SecurityRequirement;
+import io.swagger.v3.oas.annotations.security.SecurityRequirements;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.apache.syncope.common.lib.to.AuditEntryTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.rest.api.RESTHeaders;
+import org.apache.syncope.common.rest.api.beans.AuditQuery;
+
+import javax.ws.rs.BeanParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+/**
+ * REST operations for audit events.
+ */
+@Tag(name = "Audits")
+@SecurityRequirements({
+ @SecurityRequirement(name = "BasicAuthentication"),
+ @SecurityRequirement(name = "Bearer")})
+@Path("audits")
+public interface AuditService {
+
+ /**
+ * Returns a paged list of audit objects matching the given query.
+ *
+ * @param auditQuery query conditions
+ * @return paged list of objects matching the given query
+ */
+ @GET
+ @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML})
+ PagedResult<AuditEntryTO> search(@BeanParam AuditQuery auditQuery);
+}
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java
new file mode 100644
index 0000000..30ef3a2
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java
@@ -0,0 +1,66 @@
+/*
+ * 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.core.logic;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.to.AuditEntryTO;
+import org.apache.syncope.common.lib.types.StandardEntitlement;
+import org.apache.syncope.core.persistence.api.dao.AuditDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.AuditEntry;
+import org.apache.syncope.core.provisioning.api.data.AuditDataBinder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Component
+public class AuditLogic extends AbstractTransactionalLogic<AuditEntryTO> {
+ @Autowired
+ private AuditDataBinder binder;
+
+ @Autowired
+ private AuditDAO auditDAO;
+
+ @Override
+ protected AuditEntryTO resolveReference(final Method method, final Object... args)
+ throws UnresolvedReferenceException {
+ throw new UnresolvedReferenceException();
+ }
+
+ @PreAuthorize("hasRole('" + StandardEntitlement.AUDIT_SEARCH + "')")
+ @Transactional(readOnly = true)
+ public Pair<Integer, List<AuditEntryTO>> search(
+ final String key,
+ final int page,
+ final int size,
+ final List<OrderByClause> orderByClauses) {
+
+ Integer count = auditDAO.count(key);
+ List<AuditEntry> matching = auditDAO.findByEntityKey(key, page, size, orderByClauses);
+ List<AuditEntryTO> results = matching.stream().
+ map(audit -> binder.returnAuditTO(binder.getAuditTO(audit))).
+ collect(Collectors.toList());
+ return Pair.of(count, results);
+ }
+}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditDAO.java
new file mode 100644
index 0000000..19cf75a
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AuditDAO.java
@@ -0,0 +1,30 @@
+/*
+ * 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.core.persistence.api.dao;
+
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.AuditEntry;
+
+import java.util.List;
+
+public interface AuditDAO<T extends AuditEntry> {
+ List<AuditEntry> findByEntityKey(String key, int page, int size, List<OrderByClause> orderByClauses);
+
+ Integer count(String key);
+}
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AuditEntry.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AuditEntry.java
new file mode 100644
index 0000000..7bcb134
--- /dev/null
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/entity/AuditEntry.java
@@ -0,0 +1,43 @@
+/*
+ * 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.core.persistence.api.entity;
+
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+
+import java.io.Serializable;
+import java.util.Date;
+
+public interface AuditEntry extends Serializable {
+
+ String getWho();
+
+ AuditLoggerName getLogger();
+
+ Object getBefore();
+
+ Object getOutput();
+
+ Object[] getInput();
+
+ String getThrowable();
+
+ Date getDate();
+
+ String getKey();
+}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditDAO.java
new file mode 100644
index 0000000..919217a
--- /dev/null
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/JPAAuditDAO.java
@@ -0,0 +1,108 @@
+/*
+ * 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.core.persistence.jpa.dao;
+
+import org.apache.syncope.core.persistence.api.DomainsHolder;
+import org.apache.syncope.core.persistence.api.dao.AuditDAO;
+import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
+import org.apache.syncope.core.persistence.api.entity.AuditEntry;
+import org.apache.syncope.core.provisioning.api.AuditEntryImpl;
+import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.sql.DataSource;
+
+import java.sql.Timestamp;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+@Transactional(rollbackFor = Throwable.class)
+@Repository
+public class JPAAuditDAO extends AbstractDAO implements AuditDAO<AuditEntry> {
+ private static final Logger LOG = LoggerFactory.getLogger(JPAAuditDAO.class);
+
+ private static final String TABLE_NAME = "SYNCOPEAUDIT";
+
+ @Autowired
+ private DomainsHolder domainsHolder;
+
+ private static String buildWhereClauseForEntityKey(final String key) {
+ return " WHERE MESSAGE LIKE '%" + key + "%' ";
+ }
+
+ @Override
+ public List<AuditEntry> findByEntityKey(final String key, final int page,
+ final int itemsPerPage,
+ final List<OrderByClause> orderByClauses) {
+ try {
+ String queryString = "SELECT * FROM " + TABLE_NAME + buildWhereClauseForEntityKey(key);
+ if (!orderByClauses.isEmpty()) {
+ queryString += " ORDER BY " + orderByClauses.
+ stream().
+ map(orderBy -> orderBy.getField() + ' ' + orderBy.getDirection().name()).
+ collect(Collectors.joining(","));
+ }
+ JdbcTemplate template = getJdbcTemplate();
+ template.setMaxRows(itemsPerPage);
+ template.setFetchSize(itemsPerPage * (page <= 0 ? 0 : page - 1));
+ return template.query(queryString, (resultSet, i) -> {
+ AuditEntryImpl entry = POJOHelper.deserialize(resultSet.getString("MESSAGE"), AuditEntryImpl.class);
+ String throwable = resultSet.getString("THROWABLE");
+ entry.setThrowable(throwable);
+ Timestamp date = resultSet.getTimestamp("EVENT_DATE");
+ entry.setDate(new Date(date.getTime()));
+ entry.setKey(key);
+ return entry;
+ });
+ } catch (Exception e) {
+ LOG.error("Unable to execute search query to find entity " + key, e);
+ }
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Integer count(final String key) {
+ try {
+ String queryString = "SELECT COUNT(0) FROM " + TABLE_NAME + buildWhereClauseForEntityKey(key);
+ return Objects.requireNonNull(getJdbcTemplate().queryForObject(queryString, Integer.class));
+ } catch (Exception e) {
+ LOG.error("Unable to execute count query for entity " + key, e);
+ }
+ return 0;
+ }
+
+ private JdbcTemplate getJdbcTemplate() {
+ String domain = AuthContextUtils.getDomain();
+ DataSource datasource = domainsHolder.getDomains().get(domain);
+ if (datasource == null) {
+ throw new IllegalArgumentException("Could not get to DataSource for domain " + domain);
+ }
+ return new JdbcTemplate(datasource);
+ }
+}
diff --git a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
index 05326aa..078f57e 100644
--- a/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
+++ b/core/persistence-jpa/src/test/resources/domains/MasterContent.xml
@@ -2561,4 +2561,60 @@ $$ }
<SyncopeLogger logName="syncope.audit.[LOGIC]:[SyncopeLogic]:[]:[isSelfRegAllowed]:[SUCCESS]" logLevel="DEBUG" logType="AUDIT"/>
<SecurityQuestion id="887028ea-66fc-41e7-b397-620d7ea6dfbb" content="What's your mother's maiden name?"/>
+
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[assign]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[assign]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[confirmPasswordReset]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[confirmPasswordReset]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[create]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[create]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[delete]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[delete]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[deprovision]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[deprovision]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[link]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[link]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[mustChangePassword]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[mustChangePassword]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[provision]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[provision]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[read]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[read]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[requestPasswordReset]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[requestPasswordReset]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[search]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[search]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[selfCreate]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[selfCreate]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[selfDelete]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[selfDelete]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[selfRead]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[selfRead]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[selfStatus]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[selfStatus]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[selfUpdate]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[selfUpdate]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[status]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[status]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[unassign]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[unassign]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[unlink]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[unlink]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[update]:[FAILURE]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[UserLogic]:[]:[update]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[assign]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[create]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[delete]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[deprovision]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[link]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[own]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[provisionMembers]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[provision]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[read]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[search]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[unassign]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[unlink]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[GroupLogic]:[]:[update]:[SUCCESS]" LOGLEVEL="DEBUG"/>
+ <SYNCOPELOGGER LOGTYPE="AUDIT" LOGNAME="syncope.audit.[LOGIC]:[SyncopeLogic]:[]:[isSelfRegAllowed]:[SUCCESS]" LOGLEVEL="DEBUG"/>
</dataset>
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditEntry.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java
similarity index 50%
rename from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditEntry.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java
index b117beb..a6fde1b 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditEntry.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java
@@ -16,18 +16,20 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java;
+package org.apache.syncope.core.provisioning.api;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
-import java.io.Serializable;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.syncope.common.lib.patch.UserPatch;
import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.core.persistence.api.entity.AuditEntry;
-public class AuditEntry implements Serializable {
+import java.util.Date;
+
+public class AuditEntryImpl implements AuditEntry {
private static final long serialVersionUID = -2299082316063743582L;
@@ -43,13 +45,19 @@ public class AuditEntry implements Serializable {
private final Object[] input;
+ private String throwable;
+
+ private Date date;
+
+ private String key;
+
@JsonCreator
- public AuditEntry(
- @JsonProperty("who") final String who,
- @JsonProperty("logger") final AuditLoggerName logger,
- @JsonProperty("before") final Object before,
- @JsonProperty("output") final Object output,
- @JsonProperty("input") final Object[] input) {
+ public AuditEntryImpl(
+ @JsonProperty("who") final String who,
+ @JsonProperty("logger") final AuditLoggerName logger,
+ @JsonProperty("before") final Object before,
+ @JsonProperty("output") final Object output,
+ @JsonProperty("input") final Object[] input) {
super();
@@ -65,7 +73,7 @@ public class AuditEntry implements Serializable {
}
}
- private Object maskSensitive(final Object object) {
+ private static Object maskSensitive(final Object object) {
Object masked;
if (object instanceof UserTO) {
@@ -86,23 +94,128 @@ public class AuditEntry implements Serializable {
return masked;
}
+ @Override
public String getWho() {
return who;
}
+ @Override
public AuditLoggerName getLogger() {
return logger;
}
+ @Override
public Object getBefore() {
return before;
}
+ @Override
public Object getOutput() {
return output;
}
+ @Override
public Object[] getInput() {
return input;
}
+
+ @Override
+ public String getThrowable() {
+ return throwable;
+ }
+
+ public void setThrowable(final String throwable) {
+ this.throwable = throwable;
+ }
+
+ @Override
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(final Date date) {
+ this.date = date;
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(final String key) {
+ this.key = key;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder {
+ private String who;
+
+ private AuditLoggerName logger;
+
+ private Object before;
+
+ private Object output;
+
+ private Object[] input;
+
+ private String throwable;
+
+ private Date date;
+
+ private String key;
+
+ private Builder() {
+ }
+
+ public Builder date(final Date date) {
+ this.date = date;
+ return this;
+ }
+
+ public Builder throwable(final String throwable) {
+ this.throwable = throwable;
+ return this;
+ }
+
+ public Builder key(final String key) {
+ this.key = key;
+ return this;
+ }
+
+ public Builder who(final String who) {
+ this.who = who;
+ return this;
+ }
+
+ public Builder logger(final AuditLoggerName logger) {
+ this.logger = logger;
+ return this;
+ }
+
+ public Builder before(final Object before) {
+ this.before = before;
+ return this;
+ }
+
+ public Builder output(final Object output) {
+ this.output = output;
+ return this;
+ }
+
+ public Builder input(final Object[] input) {
+ this.input = input;
+ return this;
+ }
+
+ public AuditEntryImpl build() {
+ AuditEntryImpl entry = new AuditEntryImpl(who, logger, before, output, input);
+ entry.setDate(date);
+ entry.setThrowable(throwable);
+ entry.setKey(key);
+ return entry;
+ }
+ }
}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuditDataBinder.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuditDataBinder.java
new file mode 100644
index 0000000..81e3241
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/data/AuditDataBinder.java
@@ -0,0 +1,28 @@
+/*
+ * 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.core.provisioning.api.data;
+
+import org.apache.syncope.common.lib.to.AuditEntryTO;
+import org.apache.syncope.core.persistence.api.entity.AuditEntry;
+
+public interface AuditDataBinder {
+ AuditEntryTO getAuditTO(AuditEntry application);
+
+ AuditEntryTO returnAuditTO(AuditEntryTO user);
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java
index 3d44e35..0f9e169 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultAuditManager.java
@@ -18,6 +18,8 @@
*/
package org.apache.syncope.core.provisioning.java;
+import org.apache.syncope.core.persistence.api.entity.AuditEntry;
+import org.apache.syncope.core.provisioning.api.AuditEntryImpl;
import org.apache.syncope.core.provisioning.api.AuditManager;
import org.apache.syncope.common.lib.types.AuditElements;
import org.apache.syncope.common.lib.types.AuditElements.Result;
@@ -47,12 +49,10 @@ public class DefaultAuditManager implements AuditManager {
final String subcategory,
final String event) {
- AuditEntry auditEntry = new AuditEntry(
- who,
- new AuditLoggerName(type, category, subcategory, event, Result.SUCCESS),
- null,
- null,
- null);
+ AuditEntry auditEntry = AuditEntryImpl.builder()
+ .who(who)
+ .logger(new AuditLoggerName(type, category, subcategory, event, Result.SUCCESS))
+ .build();
org.apache.syncope.core.persistence.api.entity.Logger syncopeLogger =
loggerDAO.find(auditEntry.getLogger().toLoggerName());
boolean auditRequested = syncopeLogger != null && syncopeLogger.getLevel() == LoggerLevel.DEBUG;
@@ -61,12 +61,10 @@ public class DefaultAuditManager implements AuditManager {
return true;
}
- auditEntry = new AuditEntry(
- who,
- new AuditLoggerName(type, category, subcategory, event, Result.FAILURE),
- null,
- null,
- null);
+ auditEntry = AuditEntryImpl.builder()
+ .who(who)
+ .logger(new AuditLoggerName(type, category, subcategory, event, Result.FAILURE))
+ .build();
syncopeLogger = loggerDAO.find(auditEntry.getLogger().toLoggerName());
auditRequested = syncopeLogger != null && syncopeLogger.getLevel() == LoggerLevel.DEBUG;
@@ -106,13 +104,14 @@ public class DefaultAuditManager implements AuditManager {
throwable = (Throwable) output;
}
- AuditEntry auditEntry = new AuditEntry(
- who,
- new AuditLoggerName(type, category, subcategory, event, condition),
- before,
- throwable == null ? output : throwable.getMessage(),
- input);
-
+ AuditEntry auditEntry = AuditEntryImpl.builder()
+ .who(who)
+ .logger(new AuditLoggerName(type, category, subcategory, event, condition))
+ .before(before)
+ .output(throwable == null ? output : throwable.getMessage())
+ .input(input)
+ .build();
+
org.apache.syncope.core.persistence.api.entity.Logger syncopeLogger =
loggerDAO.find(auditEntry.getLogger().toLoggerName());
if (syncopeLogger != null && syncopeLogger.getLevel() == LoggerLevel.DEBUG) {
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuditDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuditDataBinderImpl.java
new file mode 100644
index 0000000..c268270
--- /dev/null
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuditDataBinderImpl.java
@@ -0,0 +1,57 @@
+package org.apache.syncope.core.provisioning.java.data;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+import org.apache.syncope.common.lib.to.AuditEntryTO;
+import org.apache.syncope.core.persistence.api.entity.AuditEntry;
+import org.apache.syncope.core.provisioning.api.data.AuditDataBinder;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+@Component
+public class AuditDataBinderImpl implements AuditDataBinder {
+
+ @Override
+ public AuditEntryTO getAuditTO(final AuditEntry auditEntry) {
+ AuditEntryTO auditTO = new AuditEntryTO();
+ auditTO.setKey(auditEntry.getKey());
+ auditTO.setWho(auditEntry.getWho());
+ auditTO.setDate(auditEntry.getDate());
+ auditTO.setThrowable(auditEntry.getThrowable());
+ auditTO.setLoggerName(auditEntry.getLogger().toLoggerName());
+
+
+ auditTO.setSubCategory(auditEntry.getLogger().getSubcategory());
+ auditTO.setEvent(auditEntry.getLogger().getEvent());
+
+ if (auditEntry.getLogger().getResult() != null) {
+ auditTO.setResult(auditEntry.getLogger().getResult().name());
+ }
+
+ if (auditEntry.getBefore() != null) {
+ String before = ToStringBuilder.reflectionToString(
+ auditEntry.getBefore(), ToStringStyle.JSON_STYLE);
+ auditTO.setBefore(before);
+ }
+
+ if (auditEntry.getInput() != null) {
+ auditTO.getInputs().addAll(Arrays.stream(auditEntry.getInput())
+ .map(input -> ToStringBuilder.reflectionToString(input, ToStringStyle.JSON_STYLE))
+ .collect(Collectors.toList()));
+ }
+
+ if (auditEntry.getOutput() != null) {
+ auditTO.setOutput(ToStringBuilder.reflectionToString(
+ auditEntry.getOutput(), ToStringStyle.JSON_STYLE));
+ }
+
+ return auditTO;
+ }
+
+ @Override
+ public AuditEntryTO returnAuditTO(final AuditEntryTO auditEntryTO) {
+ return auditEntryTO;
+ }
+}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
index 6ba9a53..c7af73d 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/report/AuditReportlet.java
@@ -27,7 +27,8 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.syncope.common.lib.report.AuditReportletConf;
import org.apache.syncope.common.lib.report.ReportletConf;
-import org.apache.syncope.core.provisioning.java.AuditEntry;
+import org.apache.syncope.core.persistence.api.entity.AuditEntry;
+import org.apache.syncope.core.provisioning.api.AuditEntryImpl;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
import org.apache.syncope.core.persistence.api.DomainsHolder;
@@ -59,7 +60,7 @@ public class AuditReportlet extends AbstractReportlet {
handler.startElement("", "", "events", null);
AttributesImpl atts = new AttributesImpl();
for (Map<String, Object> row : rows) {
- AuditEntry auditEntry = POJOHelper.deserialize(row.get("MESSAGE").toString(), AuditEntry.class);
+ AuditEntry auditEntry = POJOHelper.deserialize(row.get("MESSAGE").toString(), AuditEntryImpl.class);
atts.clear();
if (StringUtils.isNotBlank(auditEntry.getWho())) {
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
index 2d8c3b4..84f0213 100644
--- a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AbstractAnyService.java
@@ -56,8 +56,8 @@ import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
import org.apache.syncope.core.spring.security.SecureRandomUtils;
public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
- extends AbstractServiceImpl
- implements AnyService<TO> {
+ extends AbstractServiceImpl
+ implements AnyService<TO> {
protected abstract AnyDAO<?> getAnyDAO();
@@ -122,20 +122,20 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
// if an assignable query is provided in the FIQL string, start anyway from root realm
boolean isAssignableCond = StringUtils.isBlank(anyQuery.getFiql())
- ? false
- : -1 != anyQuery.getFiql().indexOf(SpecialAttr.ASSIGNABLE.toString());
+ ? false
+ : -1 != anyQuery.getFiql().indexOf(SpecialAttr.ASSIGNABLE.toString());
SearchCond searchCond = StringUtils.isBlank(anyQuery.getFiql())
- ? null
- : getSearchCond(anyQuery.getFiql(), realm);
+ ? null
+ : getSearchCond(anyQuery.getFiql(), realm);
Pair<Integer, List<TO>> result = getAnyLogic().search(
- searchCond,
- anyQuery.getPage(),
- anyQuery.getSize(),
- getOrderByClauses(anyQuery.getOrderBy()),
- isAssignableCond ? SyncopeConstants.ROOT_REALM : realm,
- anyQuery.getDetails());
+ searchCond,
+ anyQuery.getPage(),
+ anyQuery.getSize(),
+ getOrderByClauses(anyQuery.getOrderBy()),
+ isAssignableCond ? SyncopeConstants.ROOT_REALM : realm,
+ anyQuery.getDetails());
return buildPagedResult(result.getRight(), anyQuery.getPage(), anyQuery.getSize(), result.getLeft());
}
@@ -159,7 +159,7 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
}
private void addUpdateOrReplaceAttr(
- final String key, final SchemaType schemaType, final AttrTO attrTO, final PatchOperation operation) {
+ final String key, final SchemaType schemaType, final AttrTO attrTO, final PatchOperation operation) {
if (attrTO.getSchema() == null) {
throw new NotFoundException("Must specify schema");
@@ -193,10 +193,10 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
@Override
public void delete(final String key, final SchemaType schemaType, final String schema) {
addUpdateOrReplaceAttr(
- getActualKey(getAnyDAO(), key),
- schemaType,
- new AttrTO.Builder().schema(schema).build(),
- PatchOperation.DELETE);
+ getActualKey(getAnyDAO(), key),
+ schemaType,
+ new AttrTO.Builder().schema(schema).build(),
+ PatchOperation.DELETE);
}
@Override
@@ -242,13 +242,13 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
item.getHeaders().put(RESTHeaders.RESOURCE_KEY, Arrays.asList(resource));
item.setStatus(updated.getEntity().getResources().contains(resource)
- ? Response.Status.BAD_REQUEST.getStatusCode()
- : Response.Status.OK.getStatusCode());
+ ? Response.Status.BAD_REQUEST.getStatusCode()
+ : Response.Status.OK.getStatusCode());
if (getPreference() == Preference.RETURN_NO_CONTENT) {
item.getHeaders().put(
- RESTHeaders.PREFERENCE_APPLIED,
- Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
+ RESTHeaders.PREFERENCE_APPLIED,
+ Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
} else {
item.setContent(POJOHelper.serialize(updated.getEntity()));
}
@@ -257,34 +257,34 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
}).collect(Collectors.toList());
} else {
batchResponseItems = updated.getPropagationStatuses().stream().
- map(status -> {
- BatchResponseItem item = new BatchResponseItem();
+ map(status -> {
+ BatchResponseItem item = new BatchResponseItem();
- item.getHeaders().put(RESTHeaders.RESOURCE_KEY, Arrays.asList(status.getResource()));
+ item.getHeaders().put(RESTHeaders.RESOURCE_KEY, Arrays.asList(status.getResource()));
- item.setStatus(status.getStatus().getHttpStatus());
+ item.setStatus(status.getStatus().getHttpStatus());
- if (status.getFailureReason() != null) {
- item.getHeaders().put(RESTHeaders.ERROR_INFO, Arrays.asList(status.getFailureReason()));
- }
+ if (status.getFailureReason() != null) {
+ item.getHeaders().put(RESTHeaders.ERROR_INFO, Arrays.asList(status.getFailureReason()));
+ }
- if (getPreference() == Preference.RETURN_NO_CONTENT) {
- item.getHeaders().put(
- RESTHeaders.PREFERENCE_APPLIED,
- Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
- } else {
- item.setContent(POJOHelper.serialize(updated.getEntity()));
- }
+ if (getPreference() == Preference.RETURN_NO_CONTENT) {
+ item.getHeaders().put(
+ RESTHeaders.PREFERENCE_APPLIED,
+ Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
+ } else {
+ item.setContent(POJOHelper.serialize(updated.getEntity()));
+ }
- return item;
- }).collect(Collectors.toList());
+ return item;
+ }).collect(Collectors.toList());
}
String boundary = "deassociate_" + SecureRandomUtils.generateRandomUUID().toString();
return Response.ok(BatchPayloadGenerator.generate(
- batchResponseItems, SyncopeConstants.DOUBLE_DASH + boundary)).
- type(RESTHeaders.multipartMixedWith(boundary)).
- build();
+ batchResponseItems, SyncopeConstants.DOUBLE_DASH + boundary)).
+ type(RESTHeaders.multipartMixedWith(boundary)).
+ build();
}
@Override
@@ -297,26 +297,26 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
case LINK:
updated = new ProvisioningResult<>();
updated.setEntity(getAnyLogic().link(
- patch.getKey(),
- patch.getResources()));
+ patch.getKey(),
+ patch.getResources()));
break;
case ASSIGN:
updated = getAnyLogic().assign(
- patch.getKey(),
- patch.getResources(),
- patch.getValue() != null,
- patch.getValue(),
- isNullPriorityAsync());
+ patch.getKey(),
+ patch.getResources(),
+ patch.getValue() != null,
+ patch.getValue(),
+ isNullPriorityAsync());
break;
case PROVISION:
updated = getAnyLogic().provision(
- patch.getKey(),
- patch.getResources(),
- patch.getValue() != null,
- patch.getValue(),
- isNullPriorityAsync());
+ patch.getKey(),
+ patch.getResources(),
+ patch.getValue() != null,
+ patch.getValue(),
+ isNullPriorityAsync());
break;
default:
@@ -331,13 +331,13 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
item.getHeaders().put(RESTHeaders.RESOURCE_KEY, Arrays.asList(resource));
item.setStatus(updated.getEntity().getResources().contains(resource)
- ? Response.Status.OK.getStatusCode()
- : Response.Status.BAD_REQUEST.getStatusCode());
+ ? Response.Status.OK.getStatusCode()
+ : Response.Status.BAD_REQUEST.getStatusCode());
if (getPreference() == Preference.RETURN_NO_CONTENT) {
item.getHeaders().put(
- RESTHeaders.PREFERENCE_APPLIED,
- Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
+ RESTHeaders.PREFERENCE_APPLIED,
+ Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
} else {
item.setContent(POJOHelper.serialize(updated.getEntity()));
}
@@ -346,33 +346,33 @@ public abstract class AbstractAnyService<TO extends AnyTO, P extends AnyPatch>
}).collect(Collectors.toList());
} else {
batchResponseItems = updated.getPropagationStatuses().stream().
- map(status -> {
- BatchResponseItem item = new BatchResponseItem();
+ map(status -> {
+ BatchResponseItem item = new BatchResponseItem();
- item.getHeaders().put(RESTHeaders.RESOURCE_KEY, Arrays.asList(status.getResource()));
+ item.getHeaders().put(RESTHeaders.RESOURCE_KEY, Arrays.asList(status.getResource()));
- item.setStatus(status.getStatus().getHttpStatus());
+ item.setStatus(status.getStatus().getHttpStatus());
- if (status.getFailureReason() != null) {
- item.getHeaders().put(RESTHeaders.ERROR_INFO, Arrays.asList(status.getFailureReason()));
- }
+ if (status.getFailureReason() != null) {
+ item.getHeaders().put(RESTHeaders.ERROR_INFO, Arrays.asList(status.getFailureReason()));
+ }
- if (getPreference() == Preference.RETURN_NO_CONTENT) {
- item.getHeaders().put(
- RESTHeaders.PREFERENCE_APPLIED,
- Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
- } else {
- item.setContent(POJOHelper.serialize(updated.getEntity()));
- }
+ if (getPreference() == Preference.RETURN_NO_CONTENT) {
+ item.getHeaders().put(
+ RESTHeaders.PREFERENCE_APPLIED,
+ Arrays.asList(Preference.RETURN_NO_CONTENT.toString()));
+ } else {
+ item.setContent(POJOHelper.serialize(updated.getEntity()));
+ }
- return item;
- }).collect(Collectors.toList());
+ return item;
+ }).collect(Collectors.toList());
}
String boundary = "associate_" + SecureRandomUtils.generateRandomUUID().toString();
return Response.ok(BatchPayloadGenerator.generate(
- batchResponseItems, SyncopeConstants.DOUBLE_DASH + boundary)).
- type(RESTHeaders.multipartMixedWith(boundary)).
- build();
+ batchResponseItems, SyncopeConstants.DOUBLE_DASH + boundary)).
+ type(RESTHeaders.multipartMixedWith(boundary)).
+ build();
}
}
diff --git a/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuditServiceImpl.java b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuditServiceImpl.java
new file mode 100644
index 0000000..e13827a
--- /dev/null
+++ b/core/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuditServiceImpl.java
@@ -0,0 +1,48 @@
+/*
+ * 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.core.rest.cxf.service;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.syncope.common.lib.to.AuditEntryTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.rest.api.beans.AuditQuery;
+import org.apache.syncope.common.rest.api.service.AuditService;
+import org.apache.syncope.core.logic.AuditLogic;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class AuditServiceImpl extends AbstractServiceImpl implements AuditService {
+ @Autowired
+ private AuditLogic logic;
+
+ @Override
+ public PagedResult<AuditEntryTO> search(final AuditQuery auditQuery) {
+ Pair<Integer, List<AuditEntryTO>> result = logic.search(
+ auditQuery.getKey(),
+ auditQuery.getPage(),
+ auditQuery.getSize(),
+ getOrderByClauses(auditQuery.getOrderBy())
+ );
+ return buildPagedResult(result.getRight(), auditQuery.getPage(), auditQuery.getSize(), result.getLeft());
+ }
+
+}
diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml
index d980a12..1249f21 100644
--- a/fit/core-reference/pom.xml
+++ b/fit/core-reference/pom.xml
@@ -338,7 +338,7 @@ under the License.
<deployable>
<location>${project.build.directory}/${project.build.finalName}</location>
<pingURL>http://localhost:${cargo.servlet.port}/syncope/cacheStats.jsp</pingURL>
- <pingTimeout>60000</pingTimeout>
+ <pingTimeout>180000</pingTimeout>
<properties>
<context>syncope</context>
</properties>
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
index be0dcee..b932689 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/AbstractITCase.java
@@ -77,6 +77,7 @@ import org.apache.syncope.common.rest.api.service.AnyObjectService;
import org.apache.syncope.common.rest.api.service.AnyTypeClassService;
import org.apache.syncope.common.rest.api.service.AnyTypeService;
import org.apache.syncope.common.rest.api.service.ApplicationService;
+import org.apache.syncope.common.rest.api.service.AuditService;
import org.apache.syncope.common.rest.api.service.CamelRouteService;
import org.apache.syncope.common.rest.api.service.ConfigurationService;
import org.apache.syncope.common.rest.api.service.ConnectorHistoryService;
@@ -281,6 +282,8 @@ public abstract class AbstractITCase {
protected static SCIMConfService scimConfService;
+ protected static AuditService auditService;
+
@BeforeAll
public static void securitySetup() {
try (InputStream propStream = Encryptor.class.getResourceAsStream("/security.properties")) {
@@ -353,6 +356,7 @@ public abstract class AbstractITCase {
oidcClientService = adminClient.getService(OIDCClientService.class);
oidcProviderService = adminClient.getService(OIDCProviderService.class);
scimConfService = adminClient.getService(SCIMConfService.class);
+ auditService = adminClient.getService(AuditService.class);
}
protected static String getUUIDString() {
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java
new file mode 100644
index 0000000..dfcbebc
--- /dev/null
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/AuditITCase.java
@@ -0,0 +1,108 @@
+/*
+ * 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.fit.core;
+
+import org.apache.syncope.common.lib.SyncopeConstants;
+import org.apache.syncope.common.lib.to.AuditEntryTO;
+import org.apache.syncope.common.lib.to.GroupTO;
+import org.apache.syncope.common.lib.to.PagedResult;
+import org.apache.syncope.common.lib.to.ProvisioningResult;
+import org.apache.syncope.common.lib.to.UserTO;
+import org.apache.syncope.common.rest.api.beans.AuditQuery;
+import org.apache.syncope.fit.AbstractITCase;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class AuditITCase extends AbstractITCase {
+ private static final String USER_KEY = getUUIDString();
+ private static final String GROUP_KEY = getUUIDString();
+
+ private static GroupTO getSampleGroupTO(final String name) {
+ GroupTO groupTO = new GroupTO();
+ groupTO.setRealm(SyncopeConstants.ROOT_REALM);
+ groupTO.setName(name + getUUIDString());
+ groupTO.setKey(GROUP_KEY);
+ return groupTO;
+ }
+
+ private static UserTO getSampleUserTO(final String email) {
+ UserTO userTO = new UserTO();
+ userTO.setRealm(SyncopeConstants.ROOT_REALM);
+ userTO.setPassword("password123");
+ userTO.setUsername(email);
+ userTO.setKey(USER_KEY);
+ userTO.getPlainAttrs().add(attrTO("fullname", "Apache Syncope"));
+ userTO.getPlainAttrs().add(attrTO("firstname", "Apache"));
+ userTO.getPlainAttrs().add(attrTO("surname", "Syncope"));
+ userTO.getPlainAttrs().add(attrTO("userId", email));
+ userTO.getPlainAttrs().add(attrTO("email", email));
+ return userTO;
+ }
+
+ @Test
+ public void findByUser() {
+ UserTO userTO = getSampleUserTO("example@syncope.org");
+ userTO.getResources().add(RESOURCE_NAME_TESTDB);
+ ProvisioningResult<UserTO> result = createUser(userTO);
+ assertNotNull(result);
+ userTO = result.getEntity();
+ userService.read(userTO.getKey());
+
+ PagedResult<AuditEntryTO> auditResult = auditService.search(
+ new AuditQuery.Builder()
+ .key(USER_KEY)
+ .orderBy("event_date desc")
+ .page(1)
+ .size(1)
+ .build());
+ assertNotNull(auditResult);
+ List<AuditEntryTO> results = auditResult.getResult();
+ assertFalse(results.isEmpty());
+ assertEquals(1, results.size());
+ assertTrue(results.stream().allMatch(entry -> entry.getKey().equalsIgnoreCase(USER_KEY)));
+ userService.delete(userTO.getKey());
+ }
+
+ @Test
+ public void findByGroup() {
+ GroupTO groupTO = getSampleGroupTO("AuditGroup");
+ ProvisioningResult<GroupTO> groupResult = createGroup(groupTO);
+ assertNotNull(groupResult);
+ groupTO = groupResult.getEntity();
+ groupService.read(groupTO.getKey());
+
+ PagedResult<AuditEntryTO> result = auditService.search(
+ new AuditQuery.Builder()
+ .key(GROUP_KEY)
+ .orderBy("event_date asc")
+ .page(1)
+ .size(1)
+ .build());
+ assertNotNull(result);
+ List<AuditEntryTO> results = result.getResult();
+ assertFalse(results.isEmpty());
+ assertEquals(1, results.size());
+ assertTrue(results.stream().allMatch(entry -> entry.getKey().equalsIgnoreCase(GROUP_KEY)));
+ groupService.delete(groupTO.getKey());
+ }
+}