You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by an...@apache.org on 2017/07/13 12:24:01 UTC
syncope git commit: [SYNCOPE-1144] configurable audit appenders with
message rewrite option
Repository: syncope
Updated Branches:
refs/heads/2_0_X bfd47df70 -> dc4b83ef1
[SYNCOPE-1144] configurable audit appenders with message rewrite option
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo
Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/dc4b83ef
Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/dc4b83ef
Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/dc4b83ef
Branch: refs/heads/2_0_X
Commit: dc4b83ef1cbf76240bf24c9b8a7e8cc9ae0a9994
Parents: bfd47df
Author: Andrea Patricelli <an...@apache.org>
Authored: Tue Jul 4 12:09:45 2017 +0200
Committer: Andrea Patricelli <an...@apache.org>
Committed: Thu Jul 13 14:21:14 2017 +0200
----------------------------------------------------------------------
.../core/logic/AbstractAuditAppender.java | 71 +++++++++++++++
.../syncope/core/logic/AuditAppender.java | 51 +++++++++++
.../core/logic/DefaultAuditAppender.java | 54 ++++++++++++
.../core/logic/DefaultRewriteAuditAppender.java | 64 ++++++++++++++
.../apache/syncope/core/logic/LoggerLogic.java | 46 +++++++++-
.../core/logic/PassThroughRewritePolicy.java | 41 +++++++++
.../init/ClassPathScanImplementationLookup.java | 15 ++++
.../syncope/core/logic/init/LoggerAccessor.java | 12 ++-
.../syncope/core/logic/init/LoggerLoader.java | 79 +++++++++++++++++
.../persistence/api/ImplementationLookup.java | 5 +-
.../jpa/DummyImplementationLookup.java | 5 ++
.../provisioning/java/AuditManagerImpl.java | 13 ++-
.../AbstractPropagationTaskExecutor.java | 2 +-
.../java/DummyImplementationLookup.java | 5 ++
.../core/reference/ITImplementationLookup.java | 13 +++
.../reference/SyslogRewriteAuditAppender.java | 80 +++++++++++++++++
.../core/reference/TestFileAuditAppender.java | 86 ++++++++++++++++++
.../reference/TestFileRewriteAuditAppender.java | 84 ++++++++++++++++++
.../fit/core/reference/TestRewritePolicy.java | 46 ++++++++++
.../apache/syncope/fit/core/LoggerITCase.java | 93 ++++++++++++++++++++
.../src/test/resources/core-test.properties | 1 +
.../reference-guide/concepts/audit.adoc | 50 ++++++++++-
.../workingwithapachesyncope/customization.adoc | 6 ++
23 files changed, 910 insertions(+), 12 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAuditAppender.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAuditAppender.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAuditAppender.java
new file mode 100644
index 0000000..5383a81
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AbstractAuditAppender.java
@@ -0,0 +1,71 @@
+/*
+ * 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.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
+
+public abstract class AbstractAuditAppender implements AuditAppender {
+
+ protected String domainName;
+
+ protected Appender targetAppender;
+
+ protected RewriteAppender rewriteAppender;
+
+ @Override
+ public abstract void init();
+
+ public abstract void initTargetAppender();
+
+ public abstract void initRewriteAppender();
+
+ @Override
+ public abstract RewritePolicy getRewritePolicy();
+
+ @Override
+ public String getDomainName() {
+ return domainName;
+ }
+
+ @Override
+ public void setDomainName(final String domainName) {
+ this.domainName = domainName;
+ }
+
+ @Override
+ public abstract String getTargetAppenderName();
+
+ @Override
+ public boolean isRewriteEnabled() {
+ return rewriteAppender != null;
+ }
+
+ @Override
+ public RewriteAppender getRewriteAppender() {
+ return rewriteAppender;
+ }
+
+ @Override
+ public Appender getTargetAppender() {
+ return targetAppender;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/logic/src/main/java/org/apache/syncope/core/logic/AuditAppender.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/AuditAppender.java b/core/logic/src/main/java/org/apache/syncope/core/logic/AuditAppender.java
new file mode 100644
index 0000000..93dbfed
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/AuditAppender.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.core.logic;
+
+import java.util.Set;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+
+/**
+ * Basic interface to implement to define a custom audit appender
+ *
+ * @see org.apache.syncope.core.logic.DefaultRewriteAuditAppender or org.apache.syncope.core.logic.DefaultAuditAppender
+ */
+public interface AuditAppender {
+
+ void init();
+
+ Set<AuditLoggerName> getEvents();
+
+ Appender getTargetAppender();
+
+ RewritePolicy getRewritePolicy();
+
+ String getTargetAppenderName();
+
+ void setDomainName(String name);
+
+ String getDomainName();
+
+ boolean isRewriteEnabled();
+
+ RewriteAppender getRewriteAppender();
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/logic/src/main/java/org/apache/syncope/core/logic/DefaultAuditAppender.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/DefaultAuditAppender.java b/core/logic/src/main/java/org/apache/syncope/core/logic/DefaultAuditAppender.java
new file mode 100644
index 0000000..1fd9e5e
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/DefaultAuditAppender.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.core.logic;
+
+import java.util.Collections;
+import java.util.Set;
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+
+/**
+ * Default (abstract) implementation of custom audit appender.
+ * It is bound to an empty collection of events, i.e. it does not create any logger.
+ * This class has to be extended by non-rewrite appenders
+ *
+ * @see org.apache.syncope.fit.core.reference.TestFileAuditAppender
+ */
+public abstract class DefaultAuditAppender extends AbstractAuditAppender {
+
+ @Override
+ public void init() {
+ initTargetAppender();
+ }
+
+ @Override
+ public Set<AuditLoggerName> getEvents() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public void initRewriteAppender() {
+ }
+
+ @Override
+ public RewritePolicy getRewritePolicy() {
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/logic/src/main/java/org/apache/syncope/core/logic/DefaultRewriteAuditAppender.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/DefaultRewriteAuditAppender.java b/core/logic/src/main/java/org/apache/syncope/core/logic/DefaultRewriteAuditAppender.java
new file mode 100644
index 0000000..207502e
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/DefaultRewriteAuditAppender.java
@@ -0,0 +1,64 @@
+/*
+ * 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 java.util.Collections;
+import java.util.Set;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.rewrite.RewriteAppender;
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
+import org.apache.logging.log4j.core.config.AppenderRef;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+
+/**
+ * Default (abstract) implementation of custom rewriting audit appender; it provides rewrite appender definition and
+ * a default "pass-through" policy. It is bound to an empty collection of events, i.e. it does not create any logger.
+ * This class has to be extended by rewrite appenders.
+ *
+ * @see org.apache.syncope.fit.core.reference.TestFileRewriteAuditAppender
+ */
+public abstract class DefaultRewriteAuditAppender extends AbstractAuditAppender {
+
+ @Override
+ public void init() {
+ initTargetAppender();
+ initRewriteAppender();
+ }
+
+ @Override
+ public void initRewriteAppender() {
+ rewriteAppender = RewriteAppender.createAppender(getTargetAppenderName() + "_rewrite",
+ "true",
+ new AppenderRef[] { AppenderRef.createAppenderRef(getTargetAppenderName(), Level.DEBUG, null) },
+ ((LoggerContext) LogManager.getContext(false)).getConfiguration(), getRewritePolicy(), null);
+ }
+
+ @Override
+ public Set<AuditLoggerName> getEvents() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public RewritePolicy getRewritePolicy() {
+ return PassThroughRewritePolicy.createPolicy();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java b/core/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
index 5315ea9..667efc8 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/LoggerLogic.java
@@ -25,7 +25,9 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.IteratorUtils;
+import org.apache.commons.collections4.Predicate;
import org.apache.commons.collections4.PredicateUtils;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.TransformerUtils;
@@ -60,9 +62,11 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.api.entity.resource.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.Logger;
import org.apache.syncope.core.persistence.api.entity.task.SchedTask;
+import org.apache.syncope.core.provisioning.java.AuditManagerImpl;
import org.apache.syncope.core.spring.BeanUtils;
import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate;
+import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
@@ -216,10 +220,41 @@ public class LoggerLogic extends AbstractTransactionalLogic<LoggerTO> {
syncopeLogger.setLevel(LoggerLevel.fromLevel(level));
syncopeLogger = loggerDAO.save(syncopeLogger);
+ boolean isAudit = LoggerType.AUDIT.equals(syncopeLogger.getType());
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ String domainAuditLoggerName =
+ AuditManagerImpl.getDomainAuditEventLoggerName(AuthContextUtils.getDomain(), syncopeLogger.
+ getKey());
LoggerConfig logConf = SyncopeConstants.ROOT_LOGGER.equals(name)
? ctx.getConfiguration().getLoggerConfig(LogManager.ROOT_LOGGER_NAME)
- : ctx.getConfiguration().getLoggerConfig(name);
+ : isAudit
+ ? ctx.getConfiguration().getLoggerConfig(domainAuditLoggerName)
+ : ctx.getConfiguration().getLoggerConfig(name);
+
+ if (isAudit) {
+ // SYNCOPE-1144 For each custom audit appender class add related appenders to log4j logger
+ List<AuditAppender> auditAppenders = loggerLoader.auditAppenders(AuthContextUtils.getDomain());
+ boolean isRootLogConf = LogManager.ROOT_LOGGER_NAME.equals(logConf.getName());
+ final String loggerKey = syncopeLogger.getKey();
+ if (isRootLogConf) {
+ logConf = new LoggerConfig(domainAuditLoggerName, null, false);
+ }
+ for (AuditAppender auditAppender : auditAppenders) {
+
+ if (IterableUtils.matchesAny(auditAppender.getEvents(), new Predicate<AuditLoggerName>() {
+
+ @Override
+ public boolean evaluate(final AuditLoggerName auditLoggerName) {
+ return loggerKey.equalsIgnoreCase(auditLoggerName.toLoggerName());
+ }
+ })) {
+ loggerLoader.addAppenderToContext(ctx, auditAppender, logConf);
+ }
+ }
+ if (isRootLogConf) {
+ ctx.getConfiguration().addLogger(domainAuditLoggerName, logConf);
+ }
+ }
logConf.setLevel(level);
ctx.updateLoggers();
@@ -254,6 +289,7 @@ public class LoggerLogic extends AbstractTransactionalLogic<LoggerTO> {
if (expectedType != syncopeLogger.getType()) {
throwInvalidLogger(expectedType);
}
+ boolean isAudit = LoggerType.AUDIT.equals(syncopeLogger.getType());
LoggerTO loggerToDelete = new LoggerTO();
BeanUtils.copyProperties(syncopeLogger, loggerToDelete);
@@ -263,8 +299,14 @@ public class LoggerLogic extends AbstractTransactionalLogic<LoggerTO> {
// set log level to OFF in order to disable configured logger until next reboot
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ String domainAuditLoggerName =
+ AuditManagerImpl.getDomainAuditEventLoggerName(AuthContextUtils.getDomain(), syncopeLogger.
+ getKey());
org.apache.logging.log4j.core.Logger logger = SyncopeConstants.ROOT_LOGGER.equals(name)
- ? ctx.getLogger(LogManager.ROOT_LOGGER_NAME) : ctx.getLogger(name);
+ ? ctx.getLogger(LogManager.ROOT_LOGGER_NAME)
+ : isAudit
+ ? ctx.getLogger(domainAuditLoggerName)
+ : ctx.getLogger(name);
logger.setLevel(Level.OFF);
ctx.updateLoggers();
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/logic/src/main/java/org/apache/syncope/core/logic/PassThroughRewritePolicy.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/PassThroughRewritePolicy.java b/core/logic/src/main/java/org/apache/syncope/core/logic/PassThroughRewritePolicy.java
new file mode 100644
index 0000000..08c6c4e
--- /dev/null
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/PassThroughRewritePolicy.java
@@ -0,0 +1,41 @@
+/*
+ * 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.logging.log4j.core.Core;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+
+@Plugin(name = "PassThroughRewritePolicy", category = Core.CATEGORY_NAME, elementType = "rewritePolicy",
+ printObject = true)
+public class PassThroughRewritePolicy implements RewritePolicy {
+
+ @Override
+ public LogEvent rewrite(final LogEvent event) {
+ return event;
+ }
+
+ @PluginFactory
+ public static PassThroughRewritePolicy createPolicy() {
+ return new PassThroughRewritePolicy();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
index fd2f1fb..902fa4d 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/init/ClassPathScanImplementationLookup.java
@@ -28,6 +28,7 @@ import java.util.Set;
import org.apache.syncope.common.lib.policy.AccountRuleConf;
import org.apache.syncope.common.lib.policy.PasswordRuleConf;
import org.apache.syncope.common.lib.report.ReportletConf;
+import org.apache.syncope.core.logic.AuditAppender;
import org.apache.syncope.core.persistence.api.ImplementationLookup;
import org.apache.syncope.core.persistence.api.attrvalue.validation.Validator;
import org.apache.syncope.core.persistence.api.dao.AccountRule;
@@ -77,6 +78,8 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
private Map<Class<? extends PasswordRuleConf>, Class<? extends PasswordRule>> passwordRuleClasses;
+ private Set<Class<?>> auditAppenderClasses;
+
@Override
public Integer getPriority() {
return 400;
@@ -103,6 +106,7 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
reportletClasses = new HashMap<>();
accountRuleClasses = new HashMap<>();
passwordRuleClasses = new HashMap<>();
+ auditAppenderClasses = new HashSet<>();
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AssignableTypeFilter(JWTSSOProvider.class));
@@ -119,6 +123,7 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
scanner.addIncludeFilter(new AssignableTypeFilter(PullCorrelationRule.class));
scanner.addIncludeFilter(new AssignableTypeFilter(Validator.class));
scanner.addIncludeFilter(new AssignableTypeFilter(NotificationRecipientsProvider.class));
+ scanner.addIncludeFilter(new AssignableTypeFilter(AuditAppender.class));
for (BeanDefinition bd : scanner.findCandidateComponents(getBasePackage())) {
try {
@@ -207,6 +212,11 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
if (NotificationRecipientsProvider.class.isAssignableFrom(clazz) && !isAbstractClazz) {
classNames.get(Type.NOTIFICATION_RECIPIENTS_PROVIDER).add(bd.getBeanClassName());
}
+
+ if (AuditAppender.class.isAssignableFrom(clazz) && !isAbstractClazz) {
+ classNames.get(Type.AUDIT_APPENDER).add(clazz.getName());
+ auditAppenderClasses.add(clazz);
+ }
} catch (Throwable t) {
LOG.warn("Could not inspect class {}", bd.getBeanClassName(), t);
}
@@ -249,4 +259,9 @@ public class ClassPathScanImplementationLookup implements ImplementationLookup {
return passwordRuleClasses.get(passwordRuleConfClass);
}
+
+ @Override
+ public Set<Class<?>> getAuditAppenderClasses() {
+ return auditAppenderClasses;
+ }
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerAccessor.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerAccessor.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerAccessor.java
index 47fa990..675fd94 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerAccessor.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerAccessor.java
@@ -30,6 +30,7 @@ import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.persistence.api.dao.LoggerDAO;
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.api.entity.Logger;
+import org.apache.syncope.core.provisioning.java.AuditManagerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
@@ -57,7 +58,8 @@ public class LoggerAccessor {
}
}
for (Logger syncopeLogger : loggerDAO.findAll(LoggerType.AUDIT)) {
- syncopeLoggers.put(syncopeLogger.getKey(), syncopeLogger);
+ syncopeLoggers.put(AuditManagerImpl.getDomainAuditEventLoggerName(AuthContextUtils.getDomain(),
+ syncopeLogger.getKey()), syncopeLogger);
}
/*
@@ -71,7 +73,8 @@ public class LoggerAccessor {
if (syncopeLoggers.containsKey(loggerName)) {
logConf.setLevel(syncopeLoggers.get(loggerName).getLevel().getLevel());
syncopeLoggers.remove(loggerName);
- } else if (!loggerName.equals(LoggerType.AUDIT.getPrefix())) {
+ } else if (!loggerName.startsWith(LoggerType.AUDIT.getPrefix()) || !loggerName.startsWith(
+ AuthContextUtils.getDomain() + "." + LoggerType.AUDIT.getPrefix())) {
Logger syncopeLogger = entityFactory.newEntity(Logger.class);
syncopeLogger.setKey(loggerName);
syncopeLogger.setLevel(LoggerLevel.fromLevel(logConf.getLevel()));
@@ -84,8 +87,9 @@ public class LoggerAccessor {
/*
* Foreach SyncopeLogger not found in log4j create a new log4j logger with given name and level.
*/
- for (Logger syncopeLogger : syncopeLoggers.values()) {
- LoggerConfig logConf = ctx.getConfiguration().getLoggerConfig(syncopeLogger.getKey());
+ for (Map.Entry<String, Logger> entry : syncopeLoggers.entrySet()) {
+ Logger syncopeLogger = entry.getValue();
+ LoggerConfig logConf = ctx.getConfiguration().getLoggerConfig(entry.getKey());
logConf.setLevel(syncopeLogger.getLevel().getLevel());
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java
----------------------------------------------------------------------
diff --git a/core/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java b/core/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java
index 60d02eb..4a24d32 100644
--- a/core/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java
+++ b/core/logic/src/main/java/org/apache/syncope/core/logic/init/LoggerLoader.java
@@ -20,7 +20,9 @@ package org.apache.syncope.core.logic.init;
import java.sql.Connection;
import java.sql.SQLException;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.apache.logging.log4j.Level;
@@ -31,13 +33,20 @@ import org.apache.logging.log4j.core.appender.db.ColumnMapping;
import org.apache.logging.log4j.core.appender.db.jdbc.ColumnConfig;
import org.apache.logging.log4j.core.appender.db.jdbc.ConnectionSource;
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;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.core.logic.AuditAppender;
import org.apache.syncope.core.logic.MemoryAppender;
import org.apache.syncope.core.provisioning.java.AuditManagerImpl;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.persistence.api.DomainsHolder;
+import org.apache.syncope.core.persistence.api.ImplementationLookup;
import org.apache.syncope.core.persistence.api.SyncopeLoader;
+import org.apache.syncope.core.spring.ApplicationContextProvider;
+import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.stereotype.Component;
@@ -50,6 +59,9 @@ public class LoggerLoader implements SyncopeLoader {
@Autowired
private LoggerAccessor loggerAccessor;
+ @Autowired
+ private ImplementationLookup implementationLookup;
+
private final Map<String, MemoryAppender> memoryAppenders = new HashMap<>();
@Override
@@ -81,6 +93,7 @@ public class LoggerLoader implements SyncopeLoader {
setConfiguration(ctx.getConfiguration()).setName("THROWABLE").setPattern("%ex{full}").build()
};
ColumnMapping[] columnMappings = new ColumnMapping[0];
+
for (Map.Entry<String, DataSource> entry : domainsHolder.getDomains().entrySet()) {
Appender appender = ctx.getConfiguration().getAppender("audit_for_" + entry.getKey());
if (appender == null) {
@@ -103,6 +116,9 @@ public class LoggerLoader implements SyncopeLoader {
logConf.setLevel(Level.DEBUG);
ctx.getConfiguration().addLogger(AuditManagerImpl.getDomainAuditLoggerName(entry.getKey()), logConf);
+ // SYNCOPE-1144 For each custom audit appender class add related appenders to log4j logger
+ configureCustomAppenders(entry.getKey(), ctx);
+
AuthContextUtils.execWithAuthContext(entry.getKey(), new AuthContextUtils.Executable<Void>() {
@Override
@@ -120,6 +136,69 @@ public class LoggerLoader implements SyncopeLoader {
return memoryAppenders;
}
+ public void configureCustomAppenders(final String domainName, final LoggerContext ctx) {
+ List<AuditAppender> auditAppenders = auditAppenders(domainName);
+ for (AuditAppender auditAppender : auditAppenders) {
+ for (AuditLoggerName event : auditAppender.getEvents()) {
+ String domainAuditLoggerName =
+ AuditManagerImpl.getDomainAuditEventLoggerName(domainName, event.toLoggerName());
+ LoggerConfig eventLogConf = ctx.getConfiguration().getLoggerConfig(domainAuditLoggerName);
+ boolean isRootLogConf = LogManager.ROOT_LOGGER_NAME.equals(eventLogConf.getName());
+
+ if (isRootLogConf) {
+ eventLogConf = new LoggerConfig(domainAuditLoggerName, null, false);
+ }
+ addAppenderToContext(ctx, auditAppender, eventLogConf);
+ eventLogConf.setLevel(Level.DEBUG);
+ if (isRootLogConf) {
+ ctx.getConfiguration().addLogger(domainAuditLoggerName, eventLogConf);
+ }
+ }
+ }
+ }
+
+ public List<AuditAppender> auditAppenders(final String domainName) throws BeansException {
+ List<AuditAppender> auditAppenders = new ArrayList<>();
+ for (Class<?> clazz : implementationLookup.getAuditAppenderClasses()) {
+ AuditAppender auditAppender;
+ if (ApplicationContextProvider.getBeanFactory().containsSingleton(clazz.getName())) {
+ auditAppender = (AuditAppender) ApplicationContextProvider.getBeanFactory().
+ getSingleton(clazz.getName());
+ } else {
+ auditAppender = (AuditAppender) ApplicationContextProvider.getBeanFactory().
+ createBean(clazz, AbstractBeanDefinition.AUTOWIRE_BY_TYPE, true);
+ auditAppender.setDomainName(domainName);
+ auditAppender.init();
+ }
+ auditAppenders.add(auditAppender);
+ }
+ return auditAppenders;
+ }
+
+ public void addAppenderToContext(
+ final LoggerContext ctx,
+ final AuditAppender auditAppender,
+ final LoggerConfig eventLogConf) {
+ Appender targetAppender = ctx.getConfiguration().getAppender(auditAppender.getTargetAppenderName());
+ if (targetAppender == null) {
+ targetAppender = auditAppender.getTargetAppender();
+ }
+ targetAppender.start();
+ ctx.getConfiguration().addAppender(targetAppender);
+ if (auditAppender.isRewriteEnabled()) {
+ RewriteAppender rewriteAppender = ctx.getConfiguration().getAppender(auditAppender.
+ getTargetAppenderName() + "_rewrite");
+ if (rewriteAppender == null) {
+ rewriteAppender = auditAppender.getRewriteAppender();
+ }
+ rewriteAppender.start();
+ ctx.getConfiguration().addAppender(rewriteAppender);
+ eventLogConf.addAppender(rewriteAppender, Level.DEBUG, null);
+ } else {
+ eventLogConf.addAppender(targetAppender, Level.DEBUG, null);
+ }
+ }
+
private static class DataSourceConnectionSource implements ConnectionSource {
private final DataSource dataSource;
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
index 2d3438f..c510677 100644
--- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
+++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/ImplementationLookup.java
@@ -43,7 +43,8 @@ public interface ImplementationLookup extends SyncopeLoader {
PUSH_ACTIONS,
PULL_CORRELATION_RULE,
VALIDATOR,
- NOTIFICATION_RECIPIENTS_PROVIDER;
+ NOTIFICATION_RECIPIENTS_PROVIDER,
+ AUDIT_APPENDER;
}
@@ -56,4 +57,6 @@ public interface ImplementationLookup extends SyncopeLoader {
Class<? extends AccountRule> getAccountRuleClass(Class<? extends AccountRuleConf> accountRuleConfClass);
Class<? extends PasswordRule> getPasswordRuleClass(Class<? extends PasswordRuleConf> passwordRuleConfClass);
+
+ Set<Class<?>> getAuditAppenderClasses();
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
index 4a785ff..1875fb1 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/DummyImplementationLookup.java
@@ -75,4 +75,9 @@ public class DummyImplementationLookup implements ImplementationLookup {
return DefaultPasswordRule.class;
}
+ @Override
+ public Set<Class<?>> getAuditAppenderClasses() {
+ return Collections.emptySet();
+ }
+
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditManagerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditManagerImpl.java
index 14180ec..760a9fa 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditManagerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/AuditManagerImpl.java
@@ -45,6 +45,10 @@ public class AuditManagerImpl implements AuditManager {
return LoggerType.AUDIT.getPrefix() + "." + domain;
}
+ public static String getDomainAuditEventLoggerName(final String domain, final String loggerName) {
+ return domain + "." + loggerName;
+ }
+
@Override
public boolean auditRequested(
final AuditElements.EventCategoryType type,
@@ -118,10 +122,15 @@ public class AuditManagerImpl implements AuditManager {
loggerDAO.find(auditEntry.getLogger().toLoggerName());
if (syncopeLogger != null && syncopeLogger.getLevel() == LoggerLevel.DEBUG) {
Logger logger = LoggerFactory.getLogger(getDomainAuditLoggerName(AuthContextUtils.getDomain()));
+ Logger eventLogger = LoggerFactory.getLogger(getDomainAuditEventLoggerName(AuthContextUtils.getDomain(),
+ syncopeLogger.getKey()));
+ String serializedAuditEntry = POJOHelper.serialize(auditEntry);
if (throwable == null) {
- logger.debug(POJOHelper.serialize(auditEntry));
+ logger.debug(serializedAuditEntry);
+ eventLogger.debug(POJOHelper.serialize(auditEntry));
} else {
- logger.debug(POJOHelper.serialize(auditEntry), throwable);
+ logger.debug(serializedAuditEntry, throwable);
+ eventLogger.debug(serializedAuditEntry, throwable);
}
}
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
index ff91c41..a14ba2f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/AbstractPropagationTaskExecutor.java
@@ -508,7 +508,7 @@ public abstract class AbstractPropagationTaskExecutor implements PropagationTask
for (PropagationActions action : actions) {
action.after(task, execution, afterObj);
}
-
+ // SYNCOPE-1136
String anyTypeKind = task.getAnyTypeKind() == null ? "realm" : task.getAnyTypeKind().name().toLowerCase();
String operation = task.getOperation().name().toLowerCase();
boolean notificationsAvailable = notificationManager.notificationsAvailable(
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java
----------------------------------------------------------------------
diff --git a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java
index 011364c..cac936d 100644
--- a/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java
+++ b/core/provisioning-java/src/test/java/org/apache/syncope/core/provisioning/java/DummyImplementationLookup.java
@@ -75,4 +75,9 @@ public class DummyImplementationLookup implements ImplementationLookup {
return DefaultPasswordRule.class;
}
+ @Override
+ public Set<Class<?>> getAuditAppenderClasses() {
+ return Collections.emptySet();
+ }
+
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
index 3ea2715..81f94b9 100644
--- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/ITImplementationLookup.java
@@ -146,6 +146,11 @@ public class ITImplementationLookup implements ImplementationLookup {
classNames = new HashSet<>();
classNames.add(TestNotificationRecipientsProvider.class.getName());
put(Type.NOTIFICATION_RECIPIENTS_PROVIDER, classNames);
+
+ classNames = new HashSet<>();
+ classNames.add(TestFileRewriteAuditAppender.class.getName());
+ classNames.add(TestFileAuditAppender.class.getName());
+ put(Type.AUDIT_APPENDER, classNames);
}
};
@@ -256,4 +261,12 @@ public class ITImplementationLookup implements ImplementationLookup {
return PASSWORD_RULE_CLASSES.get(passwordRuleConfClass);
}
+
+ @Override
+ public Set<Class<?>> getAuditAppenderClasses() {
+ Set<Class<?>> classes = new HashSet<>();
+ classes.add(TestFileRewriteAuditAppender.class);
+ classes.add(TestFileAuditAppender.class);
+ return classes;
+ }
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SyslogRewriteAuditAppender.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SyslogRewriteAuditAppender.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SyslogRewriteAuditAppender.java
new file mode 100644
index 0000000..9a192df
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/SyslogRewriteAuditAppender.java
@@ -0,0 +1,80 @@
+/*
+ * 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.reference;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.logging.log4j.core.appender.SyslogAppender;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.logging.log4j.core.net.Facility;
+import org.apache.logging.log4j.core.net.Protocol;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.core.logic.ConnectorLogic;
+import org.apache.syncope.core.logic.DefaultRewriteAuditAppender;
+import org.apache.syncope.core.logic.ResourceLogic;
+
+public class SyslogRewriteAuditAppender extends DefaultRewriteAuditAppender {
+
+ @Override
+ public Set<AuditLoggerName> getEvents() {
+ Set<AuditLoggerName> events = new HashSet<>();
+ events.add(
+ new AuditLoggerName(
+ AuditElements.EventCategoryType.LOGIC,
+ ResourceLogic.class.getSimpleName(),
+ null,
+ "update",
+ AuditElements.Result.SUCCESS));
+ events.add(
+ new AuditLoggerName(
+ AuditElements.EventCategoryType.LOGIC,
+ ConnectorLogic.class.getSimpleName(),
+ null,
+ "update",
+ AuditElements.Result.SUCCESS));
+ events.add(
+ new AuditLoggerName(
+ AuditElements.EventCategoryType.LOGIC,
+ ResourceLogic.class.getSimpleName(),
+ null,
+ "delete",
+ AuditElements.Result.SUCCESS));
+ return events;
+ }
+
+ @Override
+ public void initTargetAppender() {
+ targetAppender = SyslogAppender.newSyslogAppenderBuilder()
+ .withName(getTargetAppenderName())
+ .withHost("localhost")
+ .withPort(514)
+ .withProtocol(Protocol.UDP)
+ .withLayout(
+ PatternLayout.newBuilder().withPattern("%d{ISO8601} %-5level %logger - %msg%n").build())
+ .setFacility(Facility.LOCAL1)
+ .build();
+ }
+
+ @Override
+ public String getTargetAppenderName() {
+ return "audit_for_" + domainName + "_syslog";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileAuditAppender.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileAuditAppender.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileAuditAppender.java
new file mode 100644
index 0000000..cffc8f7
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileAuditAppender.java
@@ -0,0 +1,86 @@
+/*
+ * 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.reference;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.core.logic.ConnectorLogic;
+import org.apache.syncope.core.logic.DefaultAuditAppender;
+import org.apache.syncope.core.logic.ResourceLogic;
+
+public class TestFileAuditAppender extends DefaultAuditAppender {
+
+ @Override
+ public Set<AuditLoggerName> getEvents() {
+ Set<AuditLoggerName> events = new HashSet<>();
+ events.add(
+ new AuditLoggerName(
+ AuditElements.EventCategoryType.LOGIC,
+ ResourceLogic.class.getSimpleName(),
+ null,
+ "create",
+ AuditElements.Result.SUCCESS));
+ events.add(
+ new AuditLoggerName(
+ AuditElements.EventCategoryType.LOGIC,
+ ConnectorLogic.class.getSimpleName(),
+ null,
+ "update",
+ AuditElements.Result.SUCCESS));
+ return events;
+ }
+
+ @Override
+ public void initTargetAppender() {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ // get log file path from existing file appender
+ RollingRandomAccessFileAppender mainFile =
+ (RollingRandomAccessFileAppender) ctx.getConfiguration().getAppender("mainFile");
+
+ String pathPrefix = mainFile == null
+ ? System.getProperty("user.dir") + StringUtils.replace("/target/log", "/", File.separator)
+ + File.separator
+ : StringUtils.replace(mainFile.getFileName(), "core.log", StringUtils.EMPTY);
+
+ targetAppender = FileAppender.newBuilder()
+ .withName(getTargetAppenderName())
+ .withAppend(true)
+ .withFileName(pathPrefix + getTargetAppenderName() + ".log")
+ .withLayout(
+ PatternLayout.newBuilder()
+ .withPattern("%d{HH:mm:ss.SSS} %-5level %logger - %msg%n")
+ .build()).
+ build();
+ }
+
+ @Override
+ public String getTargetAppenderName() {
+ return "audit_for_" + domainName + "_norewrite_file";
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileRewriteAuditAppender.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileRewriteAuditAppender.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileRewriteAuditAppender.java
new file mode 100644
index 0000000..932ce56
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileRewriteAuditAppender.java
@@ -0,0 +1,84 @@
+/*
+ * 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.reference;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LoggerContext;
+import org.apache.logging.log4j.core.appender.FileAppender;
+import org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender;
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.apache.syncope.common.lib.types.AuditElements;
+import org.apache.syncope.common.lib.types.AuditLoggerName;
+import org.apache.syncope.core.logic.DefaultRewriteAuditAppender;
+import org.apache.syncope.core.logic.ResourceLogic;
+
+public class TestFileRewriteAuditAppender extends DefaultRewriteAuditAppender {
+
+ @Override
+ public Set<AuditLoggerName> getEvents() {
+ Set<AuditLoggerName> events = new HashSet<>();
+ events.add(
+ new AuditLoggerName(
+ AuditElements.EventCategoryType.LOGIC,
+ ResourceLogic.class.getSimpleName(),
+ null,
+ "update",
+ AuditElements.Result.SUCCESS));
+ return events;
+ }
+
+ @Override
+ public void initTargetAppender() {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ // get log file path from existing file appender
+ RollingRandomAccessFileAppender mainFile =
+ (RollingRandomAccessFileAppender) ctx.getConfiguration().getAppender("mainFile");
+
+ String pathPrefix = mainFile == null
+ ? System.getProperty("user.dir") + StringUtils.replace("/target/log", "/", File.separator)
+ + File.separator
+ : StringUtils.replace(mainFile.getFileName(), "core.log", StringUtils.EMPTY);
+
+ targetAppender = FileAppender.newBuilder()
+ .withName(getTargetAppenderName())
+ .withAppend(true)
+ .withFileName(pathPrefix + getTargetAppenderName() + ".log")
+ .withLayout(
+ PatternLayout.newBuilder()
+ .withPattern("%d{HH:mm:ss.SSS} %-5level %logger - %msg%n")
+ .build())
+ .build();
+ }
+
+ @Override
+ public String getTargetAppenderName() {
+ return "audit_for_" + domainName + "_file";
+ }
+
+ @Override
+ public RewritePolicy getRewritePolicy() {
+ return TestRewritePolicy.createPolicy();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestRewritePolicy.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestRewritePolicy.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestRewritePolicy.java
new file mode 100644
index 0000000..641f203
--- /dev/null
+++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestRewritePolicy.java
@@ -0,0 +1,46 @@
+/*
+ * 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.reference;
+
+import org.apache.logging.log4j.core.Core;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
+import org.apache.logging.log4j.core.config.plugins.Plugin;
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
+import org.apache.logging.log4j.message.SimpleMessage;
+import org.apache.logging.log4j.status.StatusLogger;
+
+@Plugin(name = "TestRewritePolicy", category = Core.CATEGORY_NAME, elementType = "rewritePolicy",
+ printObject = true)
+public class TestRewritePolicy implements RewritePolicy {
+
+ protected static final StatusLogger LOGGER = StatusLogger.getLogger();
+
+ @Override
+ public LogEvent rewrite(final LogEvent event) {
+ return new Log4jLogEvent.Builder(event).setMessage(new SimpleMessage("This is a static test message")).build();
+ }
+
+ @PluginFactory
+ public static TestRewritePolicy createPolicy() {
+ return new TestRewritePolicy();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java
index 5720785..9bc6ed6 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/LoggerITCase.java
@@ -24,17 +24,27 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
import java.text.ParseException;
import java.util.List;
+import java.util.Properties;
import javax.ws.rs.core.Response;
import javax.xml.ws.WebServiceException;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.Predicate;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.log.EventCategoryTO;
import org.apache.syncope.common.lib.log.LogAppender;
import org.apache.syncope.common.lib.log.LogStatementTO;
import org.apache.syncope.common.lib.log.LoggerTO;
+import org.apache.syncope.common.lib.to.ConnInstanceTO;
+import org.apache.syncope.common.lib.to.ConnPoolConfTO;
+import org.apache.syncope.common.lib.to.ResourceTO;
import org.apache.syncope.common.lib.types.AnyTypeKind;
import org.apache.syncope.common.lib.types.AuditElements;
import org.apache.syncope.common.lib.types.AuditElements.EventCategoryType;
@@ -43,11 +53,13 @@ import org.apache.syncope.common.lib.types.LoggerLevel;
import org.apache.syncope.common.lib.types.LoggerType;
import org.apache.syncope.common.lib.types.ResourceOperation;
import org.apache.syncope.common.rest.api.LoggerWrapper;
+import org.apache.syncope.core.logic.ConnectorLogic;
import org.apache.syncope.core.logic.ReportLogic;
import org.apache.syncope.core.logic.ResourceLogic;
import org.apache.syncope.core.logic.GroupLogic;
import org.apache.syncope.core.logic.UserLogic;
import org.apache.syncope.fit.AbstractITCase;
+import org.junit.Assert;
import org.junit.Test;
public class LoggerITCase extends AbstractITCase {
@@ -270,4 +282,85 @@ public class LoggerITCase extends AbstractITCase {
assertNotNull(userLogic);
assertEquals(1, IterableUtils.frequency(userLogic.getEvents(), "create"));
}
+
+ @Test
+ public void testCustomAuditAppender() throws IOException, InterruptedException {
+ InputStream propStream = null;
+ try {
+ Properties props = new Properties();
+ propStream = getClass().getResourceAsStream("/core-test.properties");
+ props.load(propStream);
+
+ final String auditFilePath = props.getProperty("test.log.dir") + File.separator
+ + "audit_for_Master_file.log";
+ final String auditNoRewriteFilePath = props.getProperty("test.log.dir") + File.separator
+ + "audit_for_Master_norewrite_file.log";
+ // 1. Enable audit for resource update -> catched by FileRewriteAuditAppender
+ AuditLoggerName auditLoggerResUpd = new AuditLoggerName(
+ EventCategoryType.LOGIC,
+ ResourceLogic.class.getSimpleName(),
+ null,
+ "update",
+ AuditElements.Result.SUCCESS);
+
+ LoggerTO loggerTOUpd = new LoggerTO();
+ loggerTOUpd.setKey(auditLoggerResUpd.toLoggerName());
+ loggerTOUpd.setLevel(LoggerLevel.DEBUG);
+ loggerService.update(LoggerType.AUDIT, loggerTOUpd);
+ // 2. Enable audit for connector update -> NOT catched by FileRewriteAuditAppender
+ AuditLoggerName auditLoggerConnUpd = new AuditLoggerName(
+ EventCategoryType.LOGIC,
+ ConnectorLogic.class.getSimpleName(),
+ null,
+ "update",
+ AuditElements.Result.SUCCESS);
+
+ LoggerTO loggerTOConnUpd = new LoggerTO();
+ loggerTOConnUpd.setKey(auditLoggerConnUpd.toLoggerName());
+ loggerTOConnUpd.setLevel(LoggerLevel.DEBUG);
+ loggerService.update(LoggerType.AUDIT, loggerTOConnUpd);
+
+ // 3. check that resource update is transformed and logged onto an audit file.
+ ResourceTO resource = resourceService.read(RESOURCE_NAME_CSV);
+ assertNotNull(resource);
+ resource.setPropagationPriority(100);
+ resourceService.update(resource);
+
+ ConnInstanceTO connector = connectorService.readByResource(RESOURCE_NAME_CSV, null);
+ assertNotNull(connector);
+ connector.setPoolConf(new ConnPoolConfTO());
+ connectorService.update(connector);
+
+ File auditTempFile = new File(auditFilePath);
+ // check audit_for_Master_file.log, it should contain only a static message
+ String auditLog = FileUtils.readFileToString(auditTempFile, "UTF-8");
+
+ Assert.assertTrue(StringUtils.contains(auditLog,
+ "DEBUG Master.syncope.audit.[LOGIC]:[ResourceLogic]:[]:[update]:[SUCCESS]"
+ + " - This is a static test message"));
+ File auditNoRewriteTempFile = new File(auditNoRewriteFilePath);
+ // check audit_for_Master_file.log, it should contain only a static message
+ String auditLogNoRewrite = FileUtils.readFileToString(auditNoRewriteTempFile, "UTF-8");
+
+ Assert.assertFalse(StringUtils.contains(auditLogNoRewrite,
+ "DEBUG Master.syncope.audit.[LOGIC]:[ResourceLogic]:[]:[update]:[SUCCESS]"
+ + " - This is a static test message"));
+
+ // clean audit_for_Master_file.log
+ FileUtils.writeStringToFile(auditTempFile, StringUtils.EMPTY, "UTF-8");
+ loggerService.delete(LoggerType.AUDIT, "syncope.audit.[LOGIC]:[ResourceLogic]:[]:[update]:[SUCCESS]");
+
+ resource = resourceService.read(RESOURCE_NAME_CSV);
+ assertNotNull(resource);
+ resource.setPropagationPriority(200);
+ resourceService.update(resource);
+
+ // check that nothing has been written to audit_for_Master_file.log
+ assertTrue(StringUtils.isEmpty(FileUtils.readFileToString(auditTempFile, "UTF-8")));
+ } catch (IOException e) {
+ fail("Unable to read/write log files" + e.getMessage());
+ } finally {
+ IOUtils.closeQuietly(propStream);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/fit/core-reference/src/test/resources/core-test.properties
----------------------------------------------------------------------
diff --git a/fit/core-reference/src/test/resources/core-test.properties b/fit/core-reference/src/test/resources/core-test.properties
index f4e4d32..2f523e9 100644
--- a/fit/core-reference/src/test/resources/core-test.properties
+++ b/fit/core-reference/src/test/resources/core-test.properties
@@ -16,3 +16,4 @@
# under the License.
test.csv.src=${project.build.directory}/test-classes/test.csv
test.csv.dst=${test.csvdir.path}/test.csv
+test.log.dir=${log.directory}
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/src/main/asciidoc/reference-guide/concepts/audit.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/concepts/audit.adoc b/src/main/asciidoc/reference-guide/concepts/audit.adoc
index 3b2b5d8..78c38d3 100644
--- a/src/main/asciidoc/reference-guide/concepts/audit.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/audit.adoc
@@ -19,9 +19,10 @@
=== Audit
The audit feature allows to capture <<audit-events,events>> occurring within the <<core>> and to log relevant information
-about them as entries into the `SYNCOPEAUDIT` table of the internal storage.
+about them. By default events are logged as entries into the `SYNCOPEAUDIT` table of the internal storage,
+but can also be logged on some additional Log4j2 appenders defined through simple customization mechanisms.
-Once events are reported in the table above, they can be used as input for external tools.
+Once events are reported, they can be used as input for external tools.
[TIP]
====
@@ -32,3 +33,48 @@ An example of how audit entries can be extracted for reporting is shown by the <
The information provided for <<notification-events,notification events>> is also valid for audit events, including examples -
except for the admin console <<console-configuration-audit,tooling>>, which is naturally distinct.
+
+==== Audit Customization
+
+As mentioned above, events are, basically, logged in a database table, but this behavior can be extended through
+`AuditAppender` interface implementation which allows an user to define additional logging supports that we address
+as appenders.
+
+Appender is a Log4j entity that allows to write whatever log message on different destinations (file, queues, syslog,
+other appenders, etc.). Moreover it provides the ability to edit (rewrite) the message, that is flowing through appenders,
+in order to customize information.
+Audit customization relies on Log4j appenders. To implement a custom audit appender an user just needs to extend to basic
+classes:
+
+. DefaultAuditAppender
+. DefaultRewriteAuditAppender
+
+The first is intended to add custom appender without any rewrtining of the message, the second allows message rewriting.
+
+What is needed to implement a well formed appender:
+
+. Events: a set of events to which the appender is bound. Appender will log only if one of those events occurs.
+. Target Appender: the Log4j appender that writes message somewhere. See
+ https://github.com/andrea-patricelli/syncope/blob/2_0_X/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileRewriteAuditAppender.java[TestFileRewriteAuditAppender^] or
+ https://github.com/andrea-patricelli/syncope/blob/2_0_X/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestFileAuditAppender.java[TestFileAuditAppender^].
+. Rewrite policy (if needed): in case of rewrite enabled a rewrite policy should be defined, by implementing Log4j
+ `RewritePolicy` interface. Some examples are
+ https://github.com/andrea-patricelli/syncope/blob/2_0_X/core/logic/src/main/java/org/apache/syncope/core/logic/PassThroughRewritePolicy.java[PassThroughRewritePolicy^]
+ and https://github.com/andrea-patricelli/syncope/blob/2_0_X/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/TestRewritePolicy.java[TestRewritePolicy^].
+ If no rewrite policy is specified `PassThroughRewritePolicy` will be used.
+
+[TIP]
+====
+Be careful while assigning names to the appenders. The name of the target appender should be unique and should depend on
+the domain.
+A best practice is to assign different names to the appenders in order to avoid names collisions and strange behavior of
+the logging framework.
+====
+
+===== How custom appenders work
+
+An appender is bound to specific events. While enabling audit on some event, if that event is "catched" also by the custom
+appender, it automatically activates. Once the audit is enabled the same audit message will be logged by the
+default audit appender and all the extensions bound to those events. While disabling audit all audit extensions are
+disabled.
+To enable an audit extension an user just needs to implement his custom `AuditAppender` in the sources, build application and deploy.
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/syncope/blob/dc4b83ef/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
----------------------------------------------------------------------
diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
index f73e360..da85daa 100644
--- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
+++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/customization.adoc
@@ -694,3 +694,9 @@ Moreover, `defaultValues` do not overwrite any existing value.
For example, the http://www.chorevolution.eu/[CHOReVOLUTION^] IdM - based on Apache Syncope - provides
https://gitlab.ow2.org/chorevolution/syncope/tree/master/ext/choreography[an extension^]
for managing via the <<core>> and visualizing via the <<admin-console-component>> the running choreography instances.
+
+[[audit-customization]]
+==== Audit Extensions
+
+<<audit>> by default, if enabled, logs on a specific database table, though this functionality could be extended to log
+also on different supports (file, queue, syslog, etc.).