This is an automated email from the ASF dual-hosted git repository. ilgrosso pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/syncope.git
commit c1502d5db7c8d3d4bca05380627108986738dc32 Author: Francesco Chicchiriccò <[email protected]> AuthorDate: Fri Nov 29 14:47:15 2019 +0100 [SYNCOPE-1511] Enhancements for PostgreSQL JSONB and MySQL JSON --- archetype/pom.xml | 5 +- .../apache/syncope/common/lib/to/AuditEntryTO.java | 15 +- .../syncope/common/lib/types/AuditLoggerName.java | 2 +- .../syncope/common/rest/api/beans/AuditQuery.java | 155 ++++++++++------ .../syncope/common/rest/api/beans/TaskQuery.java | 2 - .../common/rest/api/service/AuditService.java | 4 +- .../org/apache/syncope/core/logic/AuditLogic.java | 14 +- .../syncope/core/logic/init/LoggerLoader.java | 27 ++- .../core/rest/cxf/service/AuditServiceImpl.java | 7 +- .../syncope/core/persistence/api/dao/AuditDAO.java | 20 ++- .../core/persistence/api/entity/AuditEntry.java | 5 +- .../jpa/dao/AbstractJPAJSONAuditDAO.java | 125 +++++++++++++ .../persistence/jpa/dao/MyJPAJSONAuditDAO.java | 53 ++++++ .../persistence/jpa/dao/PGJPAJSONAuditDAO.java | 59 +++++++ .../src/main/resources/audit/audit_myjson.sql} | 6 +- .../src/main/resources/audit/audit_pgjsonb.sql} | 5 +- .../resources/myjson/domains/Master.properties | 2 +- .../main/resources/myjson/persistence.properties | 1 + .../resources/pgjsonb/domains/Master.properties | 2 +- .../main/resources/pgjsonb/persistence.properties | 1 + .../core/persistence/jpa/PersistenceContext.java | 46 +++-- .../core/persistence/jpa/dao/JPAAuditDAO.java | 194 +++++++++++---------- .../src/main/resources/audit/audit.sql | 2 - .../src/main/resources/audit/audit_sqlserver.sql | 9 +- .../src/main/resources/persistence.properties | 1 + .../core/provisioning/api/AuditEntryImpl.java | 12 -- .../provisioning/api/data/AuditDataBinder.java | 2 +- .../java/data/AuditDataBinderImpl.java | 28 +-- .../java/job/report/AuditReportlet.java | 4 +- .../src/main/resources/persistence.properties.all | 1 + .../main/resources/persistence.properties.myjson | 1 + .../main/resources/persistence.properties.pgjsonb | 1 + .../src/main/resources/persistence.properties | 1 + fit/core-reference/pom.xml | 7 +- .../resources/elasticsearch/persistence.properties | 1 + .../resources/myjson/domains/Master.properties | 2 +- .../src/main/resources/oracle/Dockerfile | 4 +- .../resources/pgjsonb/domains/Master.properties | 2 +- .../org/apache/syncope/fit/core/AuditITCase.java | 15 +- .../systemadministration/dbms.adoc | 25 ++- 40 files changed, 600 insertions(+), 268 deletions(-) diff --git a/archetype/pom.xml b/archetype/pom.xml index d8564e1..54a749a 100644 --- a/archetype/pom.xml +++ b/archetype/pom.xml @@ -42,7 +42,7 @@ under the License. <extension> <groupId>org.apache.maven.archetype</groupId> <artifactId>archetype-packaging</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> </extension> </extensions> @@ -51,7 +51,7 @@ under the License. <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-archetype-plugin</artifactId> - <version>3.1.1</version> + <version>3.1.2</version> <extensions>true</extensions> </plugin> </plugins> @@ -151,6 +151,7 @@ under the License. <directory>../core/persistence-jpa-json/src/main/resources/</directory> <includes> <include>META-INF/*</include> + <include>audit/*</include> </includes> <targetPath>${project.build.outputDirectory}/archetype-resources/core/src/main/resources</targetPath> </resource> diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AuditEntryTO.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AuditEntryTO.java index fbaf0ca..9d183e6 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AuditEntryTO.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/to/AuditEntryTO.java @@ -18,21 +18,18 @@ */ 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; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import org.apache.syncope.common.lib.BaseBean; @XmlRootElement(name = "audit") @XmlType -public class AuditEntryTO extends BaseBean implements EntityTO { - private static final long serialVersionUID = 1215115961911228005L; +public class AuditEntryTO extends BaseBean implements EntityTO { - private final List<String> inputs = new ArrayList<>(); + private static final long serialVersionUID = 1215115961911228005L; private String who; @@ -44,6 +41,8 @@ public class AuditEntryTO extends BaseBean implements EntityTO { private String before; + private final List<String> inputs = new ArrayList<>(); + private String output; private Date date; diff --git a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/AuditLoggerName.java b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/AuditLoggerName.java index 7b36554..53a7675 100644 --- a/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/AuditLoggerName.java +++ b/common/idrepo/lib/src/main/java/org/apache/syncope/common/lib/types/AuditLoggerName.java @@ -45,7 +45,7 @@ public class AuditLoggerName implements Serializable { return domain + '.' + loggerName; } - private final AuditElements.EventCategoryType type; + private final EventCategoryType type; private final String category; diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AuditQuery.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AuditQuery.java index d5dcd1a..5992024 100644 --- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AuditQuery.java +++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/AuditQuery.java @@ -18,68 +18,49 @@ */ package org.apache.syncope.common.rest.api.beans; -import org.apache.syncope.common.lib.types.AuditElements; - -import javax.ws.rs.QueryParam; - +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.ArrayList; import java.util.List; +import javax.validation.constraints.NotNull; +import javax.ws.rs.QueryParam; +import org.apache.syncope.common.lib.types.AuditElements; +import org.apache.syncope.common.rest.api.service.JAXRSService; public class AuditQuery extends AbstractQuery { private static final long serialVersionUID = -2863334226169614417L; - private String key; - - private final List<AuditElements.Result> results = new ArrayList<>(); - - private final List<String> events = new ArrayList<>(); - - public String getKey() { - return key; - } - - @QueryParam("key") - public void setKey(final String key) { - this.key = key; - } - - public List<AuditElements.Result> getResults() { - return results; - } + public static class Builder extends AbstractQuery.Builder<AuditQuery, Builder> { - @QueryParam("results") - public void setResults(final List<AuditElements.Result> results) { - if (results != null) { - this.results.addAll(results); + public Builder(final String entityKey) { + super(); + getInstance().setEntityKey(entityKey); } - } - public List<String> getEvents() { - return events; - } - - @QueryParam("events") - public void setEvents(final List<String> events) { - if (events != null) { - this.events.addAll(events); + @Override + protected AuditQuery newInstance() { + return new AuditQuery(); } - } - public static class Builder extends AbstractQuery.Builder<AuditQuery, Builder> { + public Builder type(final AuditElements.EventCategoryType type) { + getInstance().setType(type); + return this; + } - public Builder key(final String keyword) { - getInstance().setKey(keyword); + public Builder category(final String category) { + getInstance().setCategory(category); return this; } - public Builder results(final List<AuditElements.Result> results) { - getInstance().setResults(results); + public Builder subcategory(final String subcategory) { + getInstance().setSubcategory(subcategory); return this; } - public Builder result(final AuditElements.Result result) { - getInstance().getResults().add(result); + public Builder event(final String event) { + getInstance().getEvents().add(event); return this; } @@ -88,15 +69,91 @@ public class AuditQuery extends AbstractQuery { return this; } - public Builder event(final String event) { - getInstance().getEvents().add(event); + public Builder result(final AuditElements.Result result) { + getInstance().setResult(result); return this; } + } - @Override - protected AuditQuery newInstance() { - return new AuditQuery(); - } + private String entityKey; + + private AuditElements.EventCategoryType type; + + private String category; + + private String subcategory; + + private final List<String> events = new ArrayList<>(); + + private AuditElements.Result result; + + @Parameter(name = JAXRSService.PARAM_ENTITY_KEY, description = "audit entity key to match", schema = + @Schema(implementation = String.class, example = "50592942-73ec-44c4-a377-e859524245e4")) + public String getEntityKey() { + return entityKey; + } + + @NotNull + @QueryParam(JAXRSService.PARAM_ENTITY_KEY) + public void setEntityKey(final String entityKey) { + this.entityKey = entityKey; + } + + @Parameter(name = "type", description = "audit type to match", schema = + @Schema(implementation = AuditElements.EventCategoryType.class)) + public AuditElements.EventCategoryType getType() { + return type; } + @QueryParam("type") + public void setType(final AuditElements.EventCategoryType type) { + this.type = type; + } + + @Parameter(name = "category", description = "audit category to match", schema = + @Schema(implementation = String.class)) + public String getCategory() { + return category; + } + + @QueryParam("category") + public void setCategory(final String category) { + this.category = category; + } + + @Parameter(name = "subcategory", description = "audit subcategory to match", schema = + @Schema(implementation = String.class)) + public String getSubcategory() { + return subcategory; + } + + @QueryParam("subcategory") + public void setSubcategory(final String subcategory) { + this.subcategory = subcategory; + } + + @Parameter(name = "result", description = "audit result to match", schema = + @Schema(implementation = AuditElements.Result.class)) + public AuditElements.Result getResult() { + return result; + } + + @QueryParam("result") + public void setResult(final AuditElements.Result result) { + this.result = result; + } + + @Parameter(name = "events", description = "audit events(s) to match", array = + @ArraySchema(uniqueItems = true, schema = + @Schema(implementation = String.class))) + public List<String> getEvents() { + return events; + } + + @QueryParam("events") + public void setEvents(final List<String> events) { + if (events != null) { + this.events.addAll(events); + } + } } diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/TaskQuery.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/TaskQuery.java index 22e9e97..6a5ac61 100644 --- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/TaskQuery.java +++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/beans/TaskQuery.java @@ -133,8 +133,6 @@ public class TaskQuery extends AbstractQuery { return entityKey; } - @Parameter(name = JAXRSService.PARAM_RESOURCE, description = " key", schema = - @Schema(implementation = String.class, example = "resource-ldap")) @QueryParam(JAXRSService.PARAM_ENTITY_KEY) public void setEntityKey(final String entityKey) { this.entityKey = entityKey; diff --git a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuditService.java b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuditService.java index 897131e..5035494 100644 --- a/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuditService.java +++ b/common/idrepo/rest-api/src/main/java/org/apache/syncope/common/rest/api/service/AuditService.java @@ -38,7 +38,7 @@ import javax.ws.rs.core.MediaType; @Tag(name = "Audits") @SecurityRequirements({ @SecurityRequirement(name = "BasicAuthentication"), - @SecurityRequirement(name = "Bearer")}) + @SecurityRequirement(name = "Bearer") }) @Path("audits") public interface AuditService { @@ -49,6 +49,6 @@ public interface AuditService { * @return paged list of objects matching the given query */ @GET - @Produces({MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML}) + @Produces({ MediaType.APPLICATION_JSON, RESTHeaders.APPLICATION_YAML, MediaType.APPLICATION_XML }) PagedResult<AuditEntryTO> search(@BeanParam AuditQuery auditQuery); } diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java index f730de2..f57eda9 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AuditLogic.java @@ -46,17 +46,21 @@ public class AuditLogic extends AbstractTransactionalLogic<AuditEntryTO> { @PreAuthorize("hasRole('" + IdRepoEntitlement.AUDIT_SEARCH + "')") @Transactional(readOnly = true) public Pair<Integer, List<AuditEntryTO>> search( - final String key, + final String entityKey, final int page, final int size, - final List<AuditElements.Result> results, + final AuditElements.EventCategoryType type, + final String category, + final String subcategory, final List<String> events, + final AuditElements.Result result, final List<OrderByClause> orderByClauses) { - Integer count = auditDAO.count(key); - List<AuditEntry> matching = auditDAO.findByEntityKey(key, page, size, results, events, orderByClauses); + Integer count = auditDAO.count(entityKey); + List<AuditEntry> matching = auditDAO.findByEntityKey( + entityKey, page, size, type, category, subcategory, events, result, orderByClauses); List<AuditEntryTO> searchResults = matching.stream(). - map(binder::getAuditTO). + map(auditEntry -> binder.getAuditTO(entityKey, auditEntry)). collect(Collectors.toList()); return Pair.of(count, searchResults); } diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java index 17e357c..282d726 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java @@ -20,6 +20,7 @@ package org.apache.syncope.core.logic.init; import java.sql.Connection; import java.sql.SQLException; +import java.sql.Timestamp; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,7 +32,6 @@ import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.db.ColumnMapping; import org.apache.logging.log4j.core.appender.db.jdbc.AbstractConnectionSource; -import org.apache.logging.log4j.core.appender.db.jdbc.ColumnConfig; import org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender; import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender; import org.apache.logging.log4j.core.config.LoggerConfig; @@ -65,21 +65,19 @@ public class LoggerLoader implements SyncopeCoreLoader { return 300; } - private static ColumnConfig[] buildColumnConfigs(final LoggerContext ctx) { - ColumnConfig[] columnConfigs = { - ColumnConfig.newBuilder(). - setConfiguration(ctx.getConfiguration()).setName("EVENT_DATE").setEventTimestamp(true).build(), - ColumnConfig.newBuilder().setUnicode(false). + private static ColumnMapping[] buildColumnMappings(final LoggerContext ctx) { + return new ColumnMapping[] { + ColumnMapping.newBuilder(). + setConfiguration(ctx.getConfiguration()).setName("EVENT_DATE").setType(Timestamp.class).build(), + ColumnMapping.newBuilder(). setConfiguration(ctx.getConfiguration()).setName("LOGGER_LEVEL").setPattern("%level").build(), - ColumnConfig.newBuilder().setUnicode(false). + ColumnMapping.newBuilder(). setConfiguration(ctx.getConfiguration()).setName("LOGGER").setPattern("%logger").build(), - ColumnConfig.newBuilder().setUnicode(false). - setConfiguration(ctx.getConfiguration()).setName("MESSAGE").setPattern("%message").build(), - ColumnConfig.newBuilder().setUnicode(false). + ColumnMapping.newBuilder(). + setConfiguration(ctx.getConfiguration()).setName(AuditDAO.MESSAGE_COLUMN).setPattern("%message").build(), + ColumnMapping.newBuilder(). setConfiguration(ctx.getConfiguration()).setName("THROWABLE").setPattern("%ex{full}").build() }; - - return columnConfigs; } @Override @@ -98,9 +96,8 @@ public class LoggerLoader implements SyncopeCoreLoader { setIgnoreExceptions(false). setConnectionSource(new DataSourceConnectionSource(domain, datasource)). setBufferSize(0). - setTableName(AuditDAO.TABLE_NAME). - setColumnConfigs(buildColumnConfigs(ctx)). - setColumnMappings(new ColumnMapping[0]). + setTableName(AuditDAO.TABLE). + setColumnMappings(buildColumnMappings(ctx)). build(); appender.start(); ctx.getConfiguration().addAppender(appender); diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuditServiceImpl.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuditServiceImpl.java index 67126f7..b646f91 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuditServiceImpl.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/service/AuditServiceImpl.java @@ -38,11 +38,14 @@ public class AuditServiceImpl extends AbstractServiceImpl implements AuditServic @Override public PagedResult<AuditEntryTO> search(final AuditQuery auditQuery) { Pair<Integer, List<AuditEntryTO>> result = logic.search( - auditQuery.getKey(), + auditQuery.getEntityKey(), auditQuery.getPage(), auditQuery.getSize(), - auditQuery.getResults(), + auditQuery.getType(), + auditQuery.getCategory(), + auditQuery.getSubcategory(), auditQuery.getEvents(), + auditQuery.getResult(), getOrderByClauses(auditQuery.getOrderBy())); return buildPagedResult(result.getRight(), auditQuery.getPage(), auditQuery.getSize(), result.getLeft()); } 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 index 3f1994c..5a78787 100644 --- 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 @@ -26,12 +26,20 @@ import org.apache.syncope.core.persistence.api.entity.AuditEntry; public interface AuditDAO { - String TABLE_NAME = "SYNCOPEAUDIT"; + String TABLE = "SYNCOPEAUDIT"; - List<AuditEntry> findByEntityKey(String key, int page, int size, - List<AuditElements.Result> results, - List<String> events, - List<OrderByClause> orderByClauses); + String MESSAGE_COLUMN = "MESSAGE"; - Integer count(String key); + List<AuditEntry> findByEntityKey( + String entityKey, + int page, + int size, + AuditElements.EventCategoryType type, + String category, + String subcategory, + List<String> events, + AuditElements.Result result, + List<OrderByClause> orderByClauses); + + int 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 index 7bcb134..2b10e43 100644 --- 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 @@ -18,10 +18,9 @@ */ package org.apache.syncope.core.persistence.api.entity; -import org.apache.syncope.common.lib.types.AuditLoggerName; - import java.io.Serializable; import java.util.Date; +import org.apache.syncope.common.lib.types.AuditLoggerName; public interface AuditEntry extends Serializable { @@ -38,6 +37,4 @@ public interface AuditEntry extends Serializable { String getThrowable(); Date getDate(); - - String getKey(); } diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAuditDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAuditDAO.java new file mode 100644 index 0000000..a921db1 --- /dev/null +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAJSONAuditDAO.java @@ -0,0 +1,125 @@ +/* + * 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 com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.types.AuditElements; +import org.springframework.util.CollectionUtils; + +public abstract class AbstractJPAJSONAuditDAO extends JPAAuditDAO { + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + protected abstract static class JSONMessageCriteriaBuilder extends MessageCriteriaBuilder { + + protected String entityKey; + + private AuditElements.EventCategoryType type; + + private String category; + + private String subcategory; + + private List<String> events; + + private AuditElements.Result result; + + @Override + protected MessageCriteriaBuilder entityKey(final String entityKey) { + this.entityKey = entityKey; + return this; + } + + @Override + public MessageCriteriaBuilder type(final AuditElements.EventCategoryType type) { + this.type = type; + return this; + } + + @Override + public MessageCriteriaBuilder category(final String category) { + this.category = category; + return this; + } + + @Override + public MessageCriteriaBuilder subcategory(final String subcategory) { + this.subcategory = subcategory; + return this; + } + + @Override + public MessageCriteriaBuilder events(final List<String> events) { + this.events = events; + return this; + } + + @Override + public MessageCriteriaBuilder result(final AuditElements.Result result) { + this.result = result; + return this; + } + + private Optional<ObjectNode> buildContainer() { + ObjectNode logger = MAPPER.createObjectNode(); + if (type != null) { + logger.put("type", type.name()); + } + if (StringUtils.isNotBlank(category)) { + logger.put("category", category); + } + if (StringUtils.isNotBlank(subcategory)) { + logger.put("subcategory", subcategory); + } + if (result != null) { + logger.put("result", result.name()); + } + + if (!logger.isEmpty()) { + ObjectNode container = MAPPER.createObjectNode(); + container.set("logger", logger); + return Optional.of(container); + } + + return Optional.empty(); + } + + protected abstract String doBuild(List<ObjectNode> containers); + + @Override + public String build() { + List<ObjectNode> containers = new ArrayList<>(); + if (CollectionUtils.isEmpty(events)) { + buildContainer().ifPresent(containers::add); + } else { + events.forEach(event -> buildContainer().ifPresent(container -> { + ((ObjectNode) container.get("logger")).put("event", event); + containers.add(container); + })); + } + + return doBuild(containers); + } + } +} diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAuditDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAuditDAO.java new file mode 100644 index 0000000..020069b --- /dev/null +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MyJPAJSONAuditDAO.java @@ -0,0 +1,53 @@ +/* + * 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 com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; + +public class MyJPAJSONAuditDAO extends AbstractJPAJSONAuditDAO { + + private static class MyMessageCriteriaBuilder extends JSONMessageCriteriaBuilder { + + @Override + protected String doBuild(final List<ObjectNode> containers) { + query.append('(').append(MESSAGE_COLUMN).append(" -> '$.before' LIKE '%").append(entityKey). + append("%' OR ").append(MESSAGE_COLUMN).append(" -> '$.input' LIKE '%").append(entityKey). + append("%' OR ").append(MESSAGE_COLUMN).append(" -> '$.output' LIKE '%").append(entityKey). + append("%')"); + + if (!containers.isEmpty()) { + query.append(" AND ("). + append(containers.stream().map(container -> "JSON_CONTAINS(" + MESSAGE_COLUMN + ", '" + + POJOHelper.serialize(container).replace("'", "''") + + "')").collect(Collectors.joining(" OR "))). + append(')'); + } + + return query.toString(); + } + } + + @Override + protected MessageCriteriaBuilder messageCriteriaBuilder(final String entityKey) { + return new MyMessageCriteriaBuilder().entityKey(entityKey); + } +} diff --git a/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAuditDAO.java b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAuditDAO.java new file mode 100644 index 0000000..54ea828 --- /dev/null +++ b/core/persistence-jpa-json/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAJSONAuditDAO.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.core.persistence.jpa.dao; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.syncope.core.persistence.jpa.dao.AbstractJPAJSONAuditDAO.JSONMessageCriteriaBuilder; +import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; + +public class PGJPAJSONAuditDAO extends AbstractJPAJSONAuditDAO { + + private static class PGMessageCriteriaBuilder extends JSONMessageCriteriaBuilder { + + @Override + protected String doBuild(final List<ObjectNode> containers) { + query.append('(').append(MESSAGE_COLUMN).append(" ->> 'before' LIKE '%").append(entityKey). + append("%' OR ").append(MESSAGE_COLUMN).append(" ->> 'input' LIKE '%").append(entityKey). + append("%' OR ").append(MESSAGE_COLUMN).append(" ->> 'output' LIKE '%").append(entityKey). + append("%')"); + + if (!containers.isEmpty()) { + query.append(" AND ("). + append(containers.stream().map(container -> MESSAGE_COLUMN + " @> '" + + POJOHelper.serialize(container).replace("'", "''") + + "'::jsonb").collect(Collectors.joining(" OR "))). + append(')'); + } + + return query.toString(); + } + } + + @Override + protected String select() { + return MESSAGE_COLUMN + "::text"; + } + + @Override + protected MessageCriteriaBuilder messageCriteriaBuilder(final String entityKey) { + return new PGMessageCriteriaBuilder().entityKey(entityKey); + } +} diff --git a/core/persistence-jpa/src/main/resources/audit/audit.sql b/core/persistence-jpa-json/src/main/resources/audit/audit_myjson.sql similarity index 95% copy from core/persistence-jpa/src/main/resources/audit/audit.sql copy to core/persistence-jpa-json/src/main/resources/audit/audit_myjson.sql index 44ffb17..af5d37b 100644 --- a/core/persistence-jpa/src/main/resources/audit/audit.sql +++ b/core/persistence-jpa-json/src/main/resources/audit/audit_myjson.sql @@ -19,8 +19,6 @@ CREATE TABLE IF NOT EXISTS SYNCOPEAUDIT ( EVENT_DATE TIMESTAMP, LOGGER_LEVEL VARCHAR(255) NOT NULL, LOGGER VARCHAR(255) NOT NULL, - MESSAGE TEXT NOT NULL, + MESSAGE JSON NOT NULL, THROWABLE TEXT -); - -COMMIT; +) ENGINE=InnoDB; diff --git a/core/persistence-jpa/src/main/resources/audit/audit.sql b/core/persistence-jpa-json/src/main/resources/audit/audit_pgjsonb.sql similarity index 89% copy from core/persistence-jpa/src/main/resources/audit/audit.sql copy to core/persistence-jpa-json/src/main/resources/audit/audit_pgjsonb.sql index 44ffb17..0edbdf8 100644 --- a/core/persistence-jpa/src/main/resources/audit/audit.sql +++ b/core/persistence-jpa-json/src/main/resources/audit/audit_pgjsonb.sql @@ -19,8 +19,7 @@ CREATE TABLE IF NOT EXISTS SYNCOPEAUDIT ( EVENT_DATE TIMESTAMP, LOGGER_LEVEL VARCHAR(255) NOT NULL, LOGGER VARCHAR(255) NOT NULL, - MESSAGE TEXT NOT NULL, + MESSAGE JSONB NOT NULL, THROWABLE TEXT ); - -COMMIT; +CREATE INDEX SYNCOPEAUDIT_idx ON SYNCOPEAUDIT USING gin ((MESSAGE) jsonb_path_ops) diff --git a/core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties b/core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties index 2197c87..6dfdf26 100644 --- a/core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties +++ b/core/persistence-jpa-json/src/main/resources/myjson/domains/Master.properties @@ -25,4 +25,4 @@ Master.orm=META-INF/spring-orm-myjson.xml Master.pool.maxActive=10 Master.pool.minIdle=2 -Master.audit.sql=audit_mysql_innodb.sql +Master.audit.sql=audit_myjson.sql diff --git a/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties b/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties index 438ba5d..a80d72e 100644 --- a/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties +++ b/core/persistence-jpa-json/src/main/resources/myjson/persistence.properties @@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO +audit.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditDAO openjpa.RemoteCommitProvider=sjvm diff --git a/core/persistence-jpa-json/src/main/resources/pgjsonb/domains/Master.properties b/core/persistence-jpa-json/src/main/resources/pgjsonb/domains/Master.properties index d9a648f..4ab67b0 100644 --- a/core/persistence-jpa-json/src/main/resources/pgjsonb/domains/Master.properties +++ b/core/persistence-jpa-json/src/main/resources/pgjsonb/domains/Master.properties @@ -25,4 +25,4 @@ Master.orm=META-INF/spring-orm-pgjsonb.xml Master.pool.maxActive=10 Master.pool.minIdle=2 -Master.audit.sql=audit.sql +Master.audit.sql=audit_pgjsonb.sql diff --git a/core/persistence-jpa-json/src/main/resources/pgjsonb/persistence.properties b/core/persistence-jpa-json/src/main/resources/pgjsonb/persistence.properties index 53172d9..0912595 100644 --- a/core/persistence-jpa-json/src/main/resources/pgjsonb/persistence.properties +++ b/core/persistence-jpa-json/src/main/resources/pgjsonb/persistence.properties @@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAnySearchDAO user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO +audit.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditDAO openjpa.RemoteCommitProvider=sjvm diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java index a26084f..4ce8ec6 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java @@ -25,6 +25,7 @@ import javax.persistence.ValidationMode; import javax.validation.Validator; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.AuditDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO; import org.apache.syncope.core.persistence.api.dao.PlainAttrValueDAO; @@ -80,13 +81,13 @@ public class PersistenceContext implements EnvironmentAware { if (OPENJPA_LOG.isDebugEnabled()) { jpaPropertyMap.put("openjpa.Log", "SQL=TRACE"); jpaPropertyMap.put("openjpa.ConnectionFactoryProperties", - "PrintParameters=true, PrettyPrint=true, PrettyPrintLineLength=120"); + "PrintParameters=true, PrettyPrint=true, PrettyPrintLineLength=120"); } jpaPropertyMap.put("openjpa.NontransactionalWrite", false); jpaPropertyMap.put("openjpa.jdbc.MappingDefaults", - "ForeignKeyDeleteAction=restrict, JoinForeignKeyDeleteAction=restrict," + "ForeignKeyDeleteAction=restrict, JoinForeignKeyDeleteAction=restrict," + "FieldStrategies='" + "java.util.Locale=org.apache.syncope.core.persistence.jpa.openjpa.LocaleValueHandler," + "java.lang.Boolean=org.apache.syncope.core.persistence.jpa.openjpa.BooleanValueHandler'"); @@ -102,8 +103,8 @@ public class PersistenceContext implements EnvironmentAware { @Bean public EntityFactory entityFactory() - throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, - IllegalArgumentException, InvocationTargetException { + throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException { return (EntityFactory) Class.forName(env.getProperty("entity.factory")).getConstructor().newInstance(); } @@ -111,8 +112,8 @@ public class PersistenceContext implements EnvironmentAware { @ConditionalOnMissingBean(name = "plainSchemaDAO") @Bean public PlainSchemaDAO plainSchemaDAO() - throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, - IllegalArgumentException, InvocationTargetException { + throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException { return (PlainSchemaDAO) Class.forName(env.getProperty("plainSchema.dao")).getConstructor().newInstance(); } @@ -120,8 +121,8 @@ public class PersistenceContext implements EnvironmentAware { @ConditionalOnMissingBean(name = "plainAttrDAO") @Bean public PlainAttrDAO plainAttrDAO() - throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, - IllegalArgumentException, InvocationTargetException { + throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException { return (PlainAttrDAO) Class.forName(env.getProperty("plainAttr.dao")).getConstructor().newInstance(); } @@ -129,8 +130,8 @@ public class PersistenceContext implements EnvironmentAware { @ConditionalOnMissingBean(name = "plainAttrValueDAO") @Bean public PlainAttrValueDAO plainAttrValueDAO() - throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, - IllegalArgumentException, InvocationTargetException { + throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException { return (PlainAttrValueDAO) Class.forName(env.getProperty("plainAttrValue.dao")).getConstructor().newInstance(); } @@ -138,8 +139,8 @@ public class PersistenceContext implements EnvironmentAware { @ConditionalOnMissingBean(name = "anySearchDAO") @Bean public AnySearchDAO anySearchDAO() - throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, - IllegalArgumentException, InvocationTargetException { + throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException { return (AnySearchDAO) Class.forName(env.getProperty("any.search.dao")).getConstructor().newInstance(); } @@ -147,8 +148,8 @@ public class PersistenceContext implements EnvironmentAware { @ConditionalOnMissingBean(name = "userDAO") @Bean public UserDAO userDAO() - throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, - IllegalArgumentException, InvocationTargetException { + throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException { return (UserDAO) Class.forName(env.getProperty("user.dao")).getConstructor().newInstance(); } @@ -156,8 +157,8 @@ public class PersistenceContext implements EnvironmentAware { @ConditionalOnMissingBean(name = "groupDAO") @Bean public GroupDAO groupDAO() - throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, - IllegalArgumentException, InvocationTargetException { + throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException { return (GroupDAO) Class.forName(env.getProperty("group.dao")).getConstructor().newInstance(); } @@ -165,12 +166,21 @@ public class PersistenceContext implements EnvironmentAware { @ConditionalOnMissingBean(name = "anyObjectDAO") @Bean public AnyObjectDAO anyObjectDAO() - throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, - IllegalArgumentException, InvocationTargetException { + throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException { return (AnyObjectDAO) Class.forName(env.getProperty("anyObject.dao")).getConstructor().newInstance(); } + @ConditionalOnMissingBean(name = "auditDAO") + @Bean + public AuditDAO auditDAO() + throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, + IllegalArgumentException, InvocationTargetException { + + return (AuditDAO) Class.forName(env.getProperty("audit.dao")).getConstructor().newInstance(); + } + @Bean public Validator localValidatorFactoryBean() { return new LocalValidatorFactoryBean(); 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 index 1fd173c..be47a81 100644 --- 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 @@ -18,14 +18,13 @@ */ package org.apache.syncope.core.persistence.jpa.dao; -import javax.sql.DataSource; -import java.sql.Timestamp; -import java.util.Collections; -import java.util.Date; +import java.sql.Clob; +import java.sql.SQLException; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; -import org.apache.syncope.core.persistence.api.DomainHolder; +import javax.persistence.Query; +import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.lib.types.AuditElements; import org.apache.syncope.core.persistence.api.dao.AuditDAO; import org.apache.syncope.core.persistence.api.dao.search.OrderByClause; @@ -33,113 +32,132 @@ import org.apache.syncope.core.persistence.api.entity.AuditEntry; import org.apache.syncope.core.persistence.jpa.entity.AbstractEntity; 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.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -@Transactional(rollbackFor = Throwable.class) -@Repository public class JPAAuditDAO extends AbstractDAO<AbstractEntity> implements AuditDAO { - @Autowired - private DomainHolder domainHolder; + protected static class MessageCriteriaBuilder { - 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<AuditElements.Result> results, - final List<String> events, - final List<OrderByClause> orderByClauses) { - - try { - String query = new MessageCriteriaBuilder(). - results(results). - events(events). - key(key). - build(); - String queryString = "SELECT * FROM " + TABLE_NAME + " WHERE " + query; - 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(); - } + protected final StringBuilder query = new StringBuilder(); - @Override - public Integer count(final String key) { - try { - String queryString = "SELECT COUNT(0) FROM " + AuditDAO.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); + protected MessageCriteriaBuilder entityKey(final String entityKey) { + query.append(' ').append(MESSAGE_COLUMN).append(" LIKE '%\"key\":\"").append(entityKey).append("\"%'"); + return this; } - return 0; - } - private JdbcTemplate getJdbcTemplate() { - String domain = AuthContextUtils.getDomain(); - DataSource datasource = domainHolder.getDomains().get(domain); - if (datasource == null) { - throw new IllegalArgumentException("Could not get to DataSource for domain " + domain); + public MessageCriteriaBuilder type(final AuditElements.EventCategoryType type) { + if (type != null) { + query.append(" AND " + MESSAGE_COLUMN + " LIKE '%\"type\":\"").append(type.name()).append("\"%'"); + } + return this; } - return new JdbcTemplate(datasource); - } - private static class MessageCriteriaBuilder { - - private final StringBuilder query = new StringBuilder(" 1=1 "); - - public MessageCriteriaBuilder key(final String key) { - query.append(" AND MESSAGE LIKE '%\"key\":\"").append(key).append("\"%' "); + public MessageCriteriaBuilder category(final String category) { + if (StringUtils.isNotBlank(category)) { + query.append(" AND " + MESSAGE_COLUMN + " LIKE '%\"category\":\"").append(category).append("\"%'"); + } return this; } - public MessageCriteriaBuilder results(final List<AuditElements.Result> results) { - buildCriteriaFor(results.stream().map(Enum::name).collect(Collectors.toList()), "result"); + public MessageCriteriaBuilder subcategory(final String subcategory) { + if (StringUtils.isNotBlank(subcategory)) { + query.append(" AND " + MESSAGE_COLUMN + " LIKE '%\"subcategory\":\""). + append(subcategory).append("\"%'"); + } return this; } - private void buildCriteriaFor(final List<String> items, final String field) { - if (!items.isEmpty()) { - query.append(" AND ( "); - query.append(items.stream().map(res -> "MESSAGE LIKE '%\"" + field + "\":\"" + res + "\"%'"). - collect(Collectors.joining(" OR "))); - query.append(" )"); + public MessageCriteriaBuilder events(final List<String> events) { + if (!events.isEmpty()) { + query.append(" AND ( "). + append(events.stream(). + map(event -> MESSAGE_COLUMN + " LIKE '%\"event\":\"" + event + "\"%'"). + collect(Collectors.joining(" OR "))). + append(" )"); } + return this; } - public MessageCriteriaBuilder events(final List<String> events) { - buildCriteriaFor(events, "event"); + public MessageCriteriaBuilder result(final AuditElements.Result result) { + if (result != null) { + query.append(" AND "). + append(MESSAGE_COLUMN).append(" LIKE '%\"result\":\"").append(result.name()).append("\"%' "); + } return this; } public String build() { - query.trimToSize(); return query.toString(); } } + + protected MessageCriteriaBuilder messageCriteriaBuilder(final String entityKey) { + return new MessageCriteriaBuilder().entityKey(entityKey); + } + + protected String select() { + return MESSAGE_COLUMN; + } + + @Transactional(readOnly = true) + @Override + public List<AuditEntry> findByEntityKey( + final String entityKey, + final int page, + final int itemsPerPage, + final AuditElements.EventCategoryType type, + final String category, + final String subcategory, + final List<String> events, + final AuditElements.Result result, + final List<OrderByClause> orderByClauses) { + + String queryString = "SELECT " + select() + + " FROM " + TABLE + + " WHERE " + messageCriteriaBuilder(entityKey). + type(type). + category(category). + subcategory(subcategory). + result(result). + events(events). + build(); + if (!orderByClauses.isEmpty()) { + queryString += " ORDER BY " + orderByClauses.stream(). + map(orderBy -> orderBy.getField() + ' ' + orderBy.getDirection().name()). + collect(Collectors.joining(",")); + } + + Query query = entityManager().createNativeQuery(queryString); + query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1)); + if (itemsPerPage >= 0) { + query.setMaxResults(itemsPerPage); + } + + @SuppressWarnings("unchecked") + List<Object> entries = query.getResultList(); + return entries.stream().map(row -> { + String value; + if (row instanceof Clob) { + Clob clob = (Clob) row; + try { + value = clob.getSubString(1, (int) clob.length()); + } catch (SQLException e) { + LOG.error("Unexpected error reading Audit Entry for entity key {}", entityKey, e); + return null; + } + } else { + value = row.toString(); + } + return POJOHelper.deserialize(value, AuditEntryImpl.class); + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + + @Override + public int count(final String key) { + String queryString = "SELECT COUNT(0) FROM " + TABLE + + " WHERE " + messageCriteriaBuilder(key).build(); + Query countQuery = entityManager().createNativeQuery(queryString); + + return ((Number) countQuery.getSingleResult()).intValue(); + } } diff --git a/core/persistence-jpa/src/main/resources/audit/audit.sql b/core/persistence-jpa/src/main/resources/audit/audit.sql index 44ffb17..1526b50 100644 --- a/core/persistence-jpa/src/main/resources/audit/audit.sql +++ b/core/persistence-jpa/src/main/resources/audit/audit.sql @@ -22,5 +22,3 @@ CREATE TABLE IF NOT EXISTS SYNCOPEAUDIT ( MESSAGE TEXT NOT NULL, THROWABLE TEXT ); - -COMMIT; diff --git a/core/persistence-jpa/src/main/resources/audit/audit_sqlserver.sql b/core/persistence-jpa/src/main/resources/audit/audit_sqlserver.sql index 191428a..4615af6 100644 --- a/core/persistence-jpa/src/main/resources/audit/audit_sqlserver.sql +++ b/core/persistence-jpa/src/main/resources/audit/audit_sqlserver.sql @@ -15,14 +15,13 @@ -- specific language governing permissions and limitations -- under the License. -IF NOT EXISTS -(SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[SYNCOPEAUDIT]') AND type in (N'U')) -BEGIN +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[SYNCOPEAUDIT]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) +DROP TABLE [dbo].[SYNCOPEAUDIT]; + CREATE TABLE SYNCOPEAUDIT ( EVENT_DATE DATETIME, LOGGER_LEVEL VARCHAR(255) NOT NULL, LOGGER VARCHAR(255) NOT NULL, MESSAGE TEXT NOT NULL, THROWABLE TEXT -) -END +) ON [PRIMARY]; diff --git a/core/persistence-jpa/src/main/resources/persistence.properties b/core/persistence-jpa/src/main/resources/persistence.properties index 0841aa0..448aaef 100644 --- a/core/persistence-jpa/src/main/resources/persistence.properties +++ b/core/persistence-jpa/src/main/resources/persistence.properties @@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO +audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditDAO openjpa.RemoteCommitProvider=sjvm diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java index 2d6d118..9681919 100644 --- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java +++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/AuditEntryImpl.java @@ -49,8 +49,6 @@ public class AuditEntryImpl implements AuditEntry { private Date date; - private String key; - @JsonCreator public AuditEntryImpl( @JsonProperty("who") final String who, @@ -145,15 +143,6 @@ public class AuditEntryImpl implements AuditEntry { 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(); } @@ -223,7 +212,6 @@ public class AuditEntryImpl implements AuditEntry { 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 index 2f79388..082f71f 100644 --- 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 @@ -23,5 +23,5 @@ import org.apache.syncope.core.persistence.api.entity.AuditEntry; public interface AuditDataBinder { - AuditEntryTO getAuditTO(AuditEntry auditEntry); + AuditEntryTO getAuditTO(String key, AuditEntry auditEntry); } 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 index 8d74e11..de49702 100644 --- 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 @@ -31,36 +31,36 @@ import org.springframework.stereotype.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()); + public AuditEntryTO getAuditTO(final String key, final AuditEntry auditEntry) { + AuditEntryTO auditEntryTO = new AuditEntryTO(); + auditEntryTO.setKey(key); + auditEntryTO.setWho(auditEntry.getWho()); + auditEntryTO.setDate(auditEntry.getDate()); + auditEntryTO.setThrowable(auditEntry.getThrowable()); + auditEntryTO.setLoggerName(auditEntry.getLogger().toLoggerName()); - auditTO.setSubCategory(auditEntry.getLogger().getSubcategory()); - auditTO.setEvent(auditEntry.getLogger().getEvent()); + auditEntryTO.setSubCategory(auditEntry.getLogger().getSubcategory()); + auditEntryTO.setEvent(auditEntry.getLogger().getEvent()); if (auditEntry.getLogger().getResult() != null) { - auditTO.setResult(auditEntry.getLogger().getResult().name()); + auditEntryTO.setResult(auditEntry.getLogger().getResult().name()); } if (auditEntry.getBefore() != null) { - auditTO.setBefore(POJOHelper.serialize(auditEntry.getBefore())); + auditEntryTO.setBefore(POJOHelper.serialize(auditEntry.getBefore())); } if (auditEntry.getInput() != null) { - auditTO.getInputs().addAll(Arrays.stream(auditEntry.getInput()). + auditEntryTO.getInputs().addAll(Arrays.stream(auditEntry.getInput()). filter(Objects::nonNull). map(POJOHelper::serialize). collect(Collectors.toList())); } if (auditEntry.getOutput() != null) { - auditTO.setOutput(POJOHelper.serialize(auditEntry.getOutput())); + auditEntryTO.setOutput(POJOHelper.serialize(auditEntry.getOutput())); } - return auditTO; + 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 7b9c555..a0632d4 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 @@ -49,12 +49,12 @@ public class AuditReportlet extends AbstractReportlet { private DataSource datasource; private void doExtractConf(final ContentHandler handler, final AtomicReference<String> status) throws SAXException { - status.set("Fetching " + conf.getSize() + " rows from the " + AuditDAO.TABLE_NAME + " table"); + status.set("Fetching " + conf.getSize() + " rows from the " + AuditDAO.TABLE + " table"); JdbcTemplate jdbcTemplate = new JdbcTemplate(datasource); jdbcTemplate.setMaxRows(conf.getSize()); List<Map<String, Object>> rows = jdbcTemplate. - queryForList("SELECT * FROM " + AuditDAO.TABLE_NAME + " ORDER BY EVENT_DATE DESC"); + queryForList("SELECT * FROM " + AuditDAO.TABLE + " ORDER BY EVENT_DATE DESC"); handler.startElement("", "", "events", null); AttributesImpl atts = new AttributesImpl(); diff --git a/docker/core/src/main/resources/persistence.properties.all b/docker/core/src/main/resources/persistence.properties.all index 2393a4e..42f7758 100644 --- a/docker/core/src/main/resources/persistence.properties.all +++ b/docker/core/src/main/resources/persistence.properties.all @@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnySearchDAO user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO +audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditDAO openjpa.RemoteCommitProvider=${OPENJPA_REMOTE_COMMIT} diff --git a/docker/core/src/main/resources/persistence.properties.myjson b/docker/core/src/main/resources/persistence.properties.myjson index 31a2dd8..cac21af 100644 --- a/docker/core/src/main/resources/persistence.properties.myjson +++ b/docker/core/src/main/resources/persistence.properties.myjson @@ -24,4 +24,5 @@ user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO conf.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONConfDAO +audit.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditDAO openjpa.RemoteCommitProvider=sjvm diff --git a/docker/core/src/main/resources/persistence.properties.pgjsonb b/docker/core/src/main/resources/persistence.properties.pgjsonb index d5a5128..b88b052 100644 --- a/docker/core/src/main/resources/persistence.properties.pgjsonb +++ b/docker/core/src/main/resources/persistence.properties.pgjsonb @@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAnySearchDAO user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO +audit.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditDAO openjpa.RemoteCommitProvider=${OPENJPA_REMOTE_COMMIT} diff --git a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties index 194c758..14b5143 100644 --- a/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties +++ b/ext/elasticsearch/persistence-jpa/src/main/resources/persistence.properties @@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearc user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO +audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditDAO openjpa.RemoteCommitProvider=sjvm diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml index 3dcb28a..a761142 100644 --- a/fit/core-reference/pom.xml +++ b/fit/core-reference/pom.xml @@ -1251,14 +1251,15 @@ under the License. </build> <run> <env> - <ORACLE_DISABLE_ASYNCH_IO>true</ORACLE_DISABLE_ASYNCH_IO> + <ORACLE_ALLOW_REMOTE>true</ORACLE_ALLOW_REMOTE> + <ORACLE_ENABLE_XDB>true</ORACLE_ENABLE_XDB> </env> <ports> <port>1521:1521</port> </ports> <wait> - <log>Disconnected from Oracle Database</log> - <time>300000</time> + <log>Disconnected from Oracle Database 11g Express Edition</log> + <time>30000</time> </wait> </run> </image> diff --git a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties index 194c758..14b5143 100644 --- a/fit/core-reference/src/main/resources/elasticsearch/persistence.properties +++ b/fit/core-reference/src/main/resources/elasticsearch/persistence.properties @@ -23,4 +23,5 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearc user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAUserDAO group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAGroupDAO anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAnyObjectDAO +audit.dao=org.apache.syncope.core.persistence.jpa.dao.JPAAuditDAO openjpa.RemoteCommitProvider=sjvm diff --git a/fit/core-reference/src/main/resources/myjson/domains/Master.properties b/fit/core-reference/src/main/resources/myjson/domains/Master.properties index 2197c87..6dfdf26 100644 --- a/fit/core-reference/src/main/resources/myjson/domains/Master.properties +++ b/fit/core-reference/src/main/resources/myjson/domains/Master.properties @@ -25,4 +25,4 @@ Master.orm=META-INF/spring-orm-myjson.xml Master.pool.maxActive=10 Master.pool.minIdle=2 -Master.audit.sql=audit_mysql_innodb.sql +Master.audit.sql=audit_myjson.sql diff --git a/fit/core-reference/src/main/resources/oracle/Dockerfile b/fit/core-reference/src/main/resources/oracle/Dockerfile index eec9268..ede2331 100644 --- a/fit/core-reference/src/main/resources/oracle/Dockerfile +++ b/fit/core-reference/src/main/resources/oracle/Dockerfile @@ -15,5 +15,7 @@ # specific language governing permissions and limitations # under the License. -FROM store/oracle/database-enterprise:12.2.0.1-slim +FROM oracleinanutshell/oracle-xe-11g + ADD init.sql /docker-entrypoint-initdb.d/ + diff --git a/fit/core-reference/src/main/resources/pgjsonb/domains/Master.properties b/fit/core-reference/src/main/resources/pgjsonb/domains/Master.properties index d9a648f..4ab67b0 100644 --- a/fit/core-reference/src/main/resources/pgjsonb/domains/Master.properties +++ b/fit/core-reference/src/main/resources/pgjsonb/domains/Master.properties @@ -25,4 +25,4 @@ Master.orm=META-INF/spring-orm-pgjsonb.xml Master.pool.maxActive=10 Master.pool.minIdle=2 -Master.audit.sql=audit.sql +Master.audit.sql=audit_pgjsonb.sql 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 index d8fc20a..7fa4904 100644 --- 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 @@ -47,7 +47,7 @@ public class AuditITCase extends AbstractITCase { i++; } while (results.isEmpty() && i < maxWaitSeconds); if (results.isEmpty()) { - fail("Timeout when executing query for key " + query.getKey()); + fail("Timeout when executing query for key " + query.getEntityKey()); } return results.get(0); @@ -58,8 +58,7 @@ public class AuditITCase extends AbstractITCase { UserTO userTO = createUser(UserITCase.getUniqueSample("[email protected]")).getEntity(); assertNotNull(userTO.getKey()); - AuditQuery query = new AuditQuery.Builder(). - key(userTO.getKey()).orderBy("event_date desc"). + AuditQuery query = new AuditQuery.Builder(userTO.getKey()).orderBy("event_date desc"). page(1).size(1).build(); AuditEntryTO entry = query(query, 50); assertEquals(userTO.getKey(), entry.getKey()); @@ -67,15 +66,16 @@ public class AuditITCase extends AbstractITCase { } @Test - public void findByUserAndByEventAndByResults() { + public void findByUserAndOther() { UserTO userTO = createUser(UserITCase.getUniqueSample("[email protected]")).getEntity(); assertNotNull(userTO.getKey()); - AuditQuery query = new AuditQuery.Builder(). - key(userTO.getKey()). + AuditQuery query = new AuditQuery.Builder(userTO.getKey()). orderBy("event_date desc"). page(1). size(1). + type(AuditElements.EventCategoryType.LOGIC). + category("UserLogic"). event("create"). result(AuditElements.Result.SUCCESS). build(); @@ -89,8 +89,7 @@ public class AuditITCase extends AbstractITCase { GroupTO groupTO = createGroup(GroupITCase.getBasicSample("AuditGroup")).getEntity(); assertNotNull(groupTO.getKey()); - AuditQuery query = new AuditQuery.Builder(). - key(groupTO.getKey()).orderBy("event_date desc"). + AuditQuery query = new AuditQuery.Builder(groupTO.getKey()).orderBy("event_date desc"). page(1).size(1).build(); AuditEntryTO entry = query(query, 50); assertEquals(groupTO.getKey(), entry.getKey()); diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/dbms.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/dbms.adoc index 865ad48..6048c2f 100644 --- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/dbms.adoc +++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/dbms.adoc @@ -90,6 +90,7 @@ any.search.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAnySearchDAO user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO +audit.dao=org.apache.syncope.core.persistence.jpa.dao.PGJPAJSONAuditDAO .... In `provisioning.properties`, replace as follows: @@ -109,6 +110,7 @@ Master.username=syncope Master.password=syncope Master.databasePlatform=org.apache.openjpa.jdbc.sql.PostgresDictionary Master.orm=META-INF/spring-orm-pgjsonb.xml +Master.audit.sql=audit_pgjsonb.sql .... [WARNING] @@ -209,6 +211,20 @@ Add the following dependency to `core/pom.xml`: </dependency> ---- +In `persistence.properties`, replace as follows: + +.... +entity.factory=org.apache.syncope.core.persistence.jpa.entity.MyJPAJSONEntityFactory +plainSchema.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainSchemaDAO +plainAttr.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrDAO +plainAttrValue.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONPlainAttrValueDAO +any.search.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAnySearchDAO +user.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONUserDAO +group.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONGroupDAO +anyObject.dao=org.apache.syncope.core.persistence.jpa.dao.JPAJSONAnyObjectDAO +audit.dao=org.apache.syncope.core.persistence.jpa.dao.MyJPAJSONAuditDAO +.... + In `provisioning.properties`, replace as follows: .... @@ -217,8 +233,7 @@ quartz.sql=tables_mysql_innodb.sql .... [WARNING] -This assumes that the InnoDB engine is enabled in your MySQL instance - if this is not the case, then change the value -for `quartz.sql` to `tables_mysql.sql`. +This assumes that the InnoDB engine is enabled in your MySQL instance. In `domains/Master.properties` (for the `Master` domain), replace as follows: @@ -230,17 +245,13 @@ Master.username=syncope Master.password=syncope Master.databasePlatform=org.apache.openjpa.jdbc.sql.MySQLDictionary(blobTypeName=LONGBLOB,dateFractionDigits=3) Master.orm=META-INF/spring-orm-myjson.xml -Master.audit.sql=audit_mysql_innodb.sql +Master.audit.sql=audit_myjson.sql .... [CAUTION] It is important to set the collation to `utf8_general_ci` after creation of `syncope` database. [WARNING] -This assumes that the InnoDB engine is enabled in your MySQL instance - if this is not the case, then change the value -for `Master.audit` to `audit.sql`. - -[WARNING] This assumes that you have a MySQL instance running on localhost, listening on its default port 3306 with a database `syncope` fully accessible by user `syncope` with password `syncope`.
