[SYNCOPE-1180] Allowing more flexible SMTP configuration, fixing mail debug option, improving docs with examples; side effect: fixing properties file lookup for Encryptor
Project: http://git-wip-us.apache.org/repos/asf/syncope/repo Commit: http://git-wip-us.apache.org/repos/asf/syncope/commit/7ad5d19b Tree: http://git-wip-us.apache.org/repos/asf/syncope/tree/7ad5d19b Diff: http://git-wip-us.apache.org/repos/asf/syncope/diff/7ad5d19b Branch: refs/heads/master Commit: 7ad5d19b91e01544f076b980c1b26deae0184a79 Parents: aa85d99 Author: Francesco Chicchiriccò <ilgro...@apache.org> Authored: Mon Jul 31 16:48:59 2017 +0200 Committer: Francesco Chicchiriccò <ilgro...@apache.org> Committed: Mon Jul 31 16:49:10 2017 +0200 ---------------------------------------------------------------------- .../console/SyncopeConsoleApplication.java | 21 +-- .../enduser/SyncopeEnduserApplication.java | 17 +-- .../syncope/common/lib/LogOutputStream.java | 141 +++++++++++++++++++ .../syncope/common/lib/PropertyUtils.java | 64 +++++++++ .../notification/NotificationJobDelegate.java | 50 +++++-- .../src/main/resources/mail.properties | 10 +- .../src/main/resources/provisioningContext.xml | 7 - .../syncope/core/spring/security/Encryptor.java | 14 +- .../src/main/resources/security.properties | 2 + .../ext/saml2lsp/agent/SAML2SPAgentSetup.java | 19 +-- .../syncope/core/logic/init/SAML2SPLoader.java | 26 +--- .../src/main/resources/log4j2.xml | 10 ++ .../src/main/resources/mail.properties | 10 +- .../fit/core/NotificationTaskITCase.java | 4 +- .../emailconfiguration.adoc | 60 +++++++- 15 files changed, 343 insertions(+), 112 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java ---------------------------------------------------------------------- diff --git a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java index 4c44802..427e364 100644 --- a/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java +++ b/client/console/src/main/java/org/apache/syncope/client/console/SyncopeConsoleApplication.java @@ -22,8 +22,6 @@ import de.agilecoders.wicket.core.Bootstrap; import de.agilecoders.wicket.core.settings.BootstrapSettings; import de.agilecoders.wicket.core.settings.IBootstrapSettings; import de.agilecoders.wicket.core.settings.SingleThemeProvider; -import java.io.File; -import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -35,7 +33,6 @@ import java.util.Map; import java.util.Properties; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; @@ -52,12 +49,12 @@ import org.apache.syncope.client.console.themes.AdminLTE; import org.apache.syncope.client.lib.AnonymousAuthenticationHandler; import org.apache.syncope.client.lib.SyncopeClientFactoryBean; import org.apache.syncope.common.lib.EntityTOUtils; +import org.apache.syncope.common.lib.PropertyUtils; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.DomainTO; import org.apache.syncope.common.lib.types.StandardEntitlement; import org.apache.syncope.common.rest.api.service.DomainService; import org.apache.wicket.Page; -import org.apache.wicket.WicketRuntimeException; import org.apache.wicket.authroles.authentication.AbstractAuthenticatedWebSession; import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication; import org.apache.wicket.authroles.authentication.AuthenticatedWebSession; @@ -141,20 +138,8 @@ public class SyncopeConsoleApplication extends AuthenticatedWebApplication { super.init(); // read console.properties - Properties props = new Properties(); - try (InputStream is = getClass().getResourceAsStream("/" + CONSOLE_PROPERTIES)) { - props.load(is); - File consoleDir = new File(props.getProperty("console.directory")); - if (consoleDir.exists() && consoleDir.canRead() && consoleDir.isDirectory()) { - File consoleDirProps = FileUtils.getFile(consoleDir, CONSOLE_PROPERTIES); - if (consoleDirProps.exists() && consoleDirProps.canRead() && consoleDirProps.isFile()) { - props.clear(); - props.load(FileUtils.openInputStream(consoleDirProps)); - } - } - } catch (Exception e) { - throw new WicketRuntimeException("Could not read " + CONSOLE_PROPERTIES, e); - } + Properties props = PropertyUtils.read(getClass(), CONSOLE_PROPERTIES, "console.directory").getLeft(); + version = props.getProperty("version"); Args.notNull(version, "<version>"); site = props.getProperty("site"); http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java ---------------------------------------------------------------------- diff --git a/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java b/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java index fc0d730..5e42000 100644 --- a/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java +++ b/client/enduser/src/main/java/org/apache/syncope/client/enduser/SyncopeEnduserApplication.java @@ -42,6 +42,7 @@ import org.apache.syncope.client.enduser.init.EnduserInitializer; import org.apache.syncope.client.enduser.model.CustomAttributesInfo; import org.apache.syncope.client.enduser.resources.CaptchaResource; import org.apache.syncope.client.lib.SyncopeClientFactoryBean; +import org.apache.syncope.common.lib.PropertyUtils; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.wicket.Page; import org.apache.wicket.Session; @@ -99,20 +100,8 @@ public class SyncopeEnduserApplication extends WebApplication implements Seriali super.init(); // read enduser.properties - Properties props = new Properties(); - try (InputStream is = getClass().getResourceAsStream("/" + ENDUSER_PROPERTIES)) { - props.load(is); - File enduserDir = new File(props.getProperty("enduser.directory")); - if (enduserDir.exists() && enduserDir.canRead() && enduserDir.isDirectory()) { - File enduserDirProps = FileUtils.getFile(enduserDir, ENDUSER_PROPERTIES); - if (enduserDirProps.exists() && enduserDirProps.canRead() && enduserDirProps.isFile()) { - props.clear(); - props.load(FileUtils.openInputStream(enduserDirProps)); - } - } - } catch (Exception e) { - throw new WicketRuntimeException("Could not read " + ENDUSER_PROPERTIES, e); - } + Properties props = PropertyUtils.read(getClass(), ENDUSER_PROPERTIES, "enduser.directory").getLeft(); + version = props.getProperty("version"); Args.notNull(version, "<version>"); site = props.getProperty("site"); http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/common/lib/src/main/java/org/apache/syncope/common/lib/LogOutputStream.java ---------------------------------------------------------------------- diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/LogOutputStream.java b/common/lib/src/main/java/org/apache/syncope/common/lib/LogOutputStream.java new file mode 100644 index 0000000..5087f95 --- /dev/null +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/LogOutputStream.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.common.lib; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import org.slf4j.Logger; + +/** + * Delegates output stream writing onto an SLF4J logger. + * Inspired by {@code}org.apache.commons.exec.LogOutputStream{@code} + */ +public class LogOutputStream extends OutputStream { + + /** Initial buffer size. */ + private static final int INTIAL_SIZE = 132; + + /** Carriage return. */ + private static final int CR = 0x0d; + + /** Linefeed. */ + private static final int LF = 0x0a; + + /** The internal buffer. */ + private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(INTIAL_SIZE); + + /** + * The delegate logger. + */ + private final Logger logger; + + private boolean skip = false; + + public LogOutputStream(final Logger logger) { + this.logger = logger; + } + + /** + * Write the data to the buffer and flush the buffer, if a line separator is + * detected. + * + * @param cc data to log (byte). + * @see java.io.OutputStream#write(int) + */ + @Override + public void write(final int cc) { + final byte c = (byte) cc; + if (c == '\n' || c == '\r') { + if (!skip) { + processBuffer(); + } + } else { + buffer.write(cc); + } + skip = c == '\r'; + } + + /** + * Flush this log stream. + * + * @see java.io.OutputStream#flush() + */ + @Override + public void flush() { + if (buffer.size() > 0) { + processBuffer(); + } + } + + /** + * Writes all remaining data from the buffer. + * + * @throws java.io.IOException + * @see java.io.OutputStream#close() + */ + @Override + public void close() throws IOException { + if (buffer.size() > 0) { + processBuffer(); + } + super.close(); + } + + /** + * Write a block of characters to the output stream + * + * @param b the array containing the data + * @param off the offset into the array where data starts + * @param len the length of block + * @see java.io.OutputStream#write(byte[], int, int) + */ + @Override + public void write(final byte[] b, final int off, final int len) { + // find the line breaks and pass other chars through in blocks + int offset = off; + int blockStartOffset = offset; + int remaining = len; + while (remaining > 0) { + while (remaining > 0 && b[offset] != LF && b[offset] != CR) { + offset++; + remaining--; + } + // either end of buffer or a line separator char + final int blockLength = offset - blockStartOffset; + if (blockLength > 0) { + buffer.write(b, blockStartOffset, blockLength); + } + while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) { + write(b[offset]); + offset++; + remaining--; + } + blockStartOffset = offset; + } + } + + /** + * Converts the buffer to a string and sends it to internal logger. + */ + private void processBuffer() { + logger.debug(buffer.toString()); + buffer.reset(); + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/common/lib/src/main/java/org/apache/syncope/common/lib/PropertyUtils.java ---------------------------------------------------------------------- diff --git a/common/lib/src/main/java/org/apache/syncope/common/lib/PropertyUtils.java b/common/lib/src/main/java/org/apache/syncope/common/lib/PropertyUtils.java new file mode 100644 index 0000000..1dd9066 --- /dev/null +++ b/common/lib/src/main/java/org/apache/syncope/common/lib/PropertyUtils.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.common.lib; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.Properties; +import org.apache.commons.lang3.tuple.Pair; + +/** + * Utility class for manipulating properties files. + */ +public final class PropertyUtils { + + public static Pair<Properties, String> read( + final Class<?> clazz, final String propertiesFileName, final String confDirProp) { + + Properties props = new Properties(); + String confDirName = null; + + try (InputStream is = clazz.getResourceAsStream("/" + propertiesFileName)) { + props.load(is); + + confDirName = props.getProperty(confDirProp); + if (confDirName != null) { + File confDir = new File(confDirName); + if (confDir.exists() && confDir.canRead() && confDir.isDirectory()) { + File confDirProps = new File(confDir, propertiesFileName); + if (confDirProps.exists() && confDirProps.canRead() && confDirProps.isFile()) { + props.clear(); + props.load(new FileInputStream(confDirProps)); + } + } + } + } catch (Exception e) { + throw new RuntimeException("Could not read " + propertiesFileName, e); + } + + return Pair.of(props, confDirName); + } + + /** + * Private default constructor, for static-only classes. + */ + private PropertyUtils() { + } +} http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java index 3ef734b..c019639 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/NotificationJobDelegate.java @@ -18,10 +18,16 @@ */ package org.apache.syncope.core.provisioning.java.job.notification; +import java.io.PrintStream; import java.util.Date; +import java.util.Enumeration; import java.util.Properties; +import javax.mail.Session; import javax.mail.internet.MimeMessage; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.LogOutputStream; +import org.apache.syncope.common.lib.PropertyUtils; import org.apache.syncope.common.lib.types.AuditElements; import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.lib.types.TraceLevel; @@ -32,9 +38,11 @@ import org.apache.syncope.core.persistence.api.entity.task.NotificationTask; import org.apache.syncope.core.persistence.api.entity.task.TaskExec; import org.apache.syncope.core.provisioning.api.AuditManager; import org.apache.syncope.core.provisioning.api.notification.NotificationManager; +import org.apache.syncope.core.spring.security.Encryptor; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; @@ -43,7 +51,7 @@ import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @Component -public class NotificationJobDelegate { +public class NotificationJobDelegate implements InitializingBean { private static final Logger LOG = LoggerFactory.getLogger(NotificationJobDelegate.class); @@ -62,24 +70,38 @@ public class NotificationJobDelegate { @Autowired private NotificationManager notificationManager; - private long maxRetries; + @Override + public void afterPropertiesSet() throws Exception { + if (mailSender instanceof JavaMailSenderImpl) { + JavaMailSenderImpl javaMailSender = (JavaMailSenderImpl) mailSender; - private void init() { - maxRetries = notificationManager.getMaxRetries(); + Properties javaMailProperties = javaMailSender.getJavaMailProperties(); - if (mailSender instanceof JavaMailSenderImpl - && StringUtils.isNotBlank(((JavaMailSenderImpl) mailSender).getUsername())) { + Properties props = PropertyUtils.read(Encryptor.class, "mail.properties", "conf.directory").getLeft(); + for (Enumeration<?> e = props.propertyNames(); e.hasMoreElements();) { + String prop = (String) e.nextElement(); + if (prop.startsWith("mail.smtp.")) { + javaMailProperties.setProperty(prop, props.getProperty(prop)); + } + } + + if (StringUtils.isNotBlank(javaMailSender.getUsername())) { + javaMailProperties.setProperty("mail.smtp.auth", "true"); + } - Properties javaMailProperties = ((JavaMailSenderImpl) mailSender).getJavaMailProperties(); - javaMailProperties.setProperty("mail.smtp.auth", "true"); - ((JavaMailSenderImpl) mailSender).setJavaMailProperties(javaMailProperties); + javaMailSender.setJavaMailProperties(javaMailProperties); + + String mailDebug = props.getProperty("mail.debug", "false"); + if (BooleanUtils.toBoolean(mailDebug)) { + Session session = javaMailSender.getSession(); + session.setDebug(true); + session.setDebugOut(new PrintStream(new LogOutputStream(LOG))); + } } } @Transactional public TaskExec executeSingle(final NotificationTask task) { - init(); - TaskExec execution = entityFactory.newEntity(TaskExec.class); execution.setTask(task); execution.setStart(new Date()); @@ -216,16 +238,16 @@ public class NotificationJobDelegate { } private void handleRetries(final TaskExec execution) { - if (maxRetries <= 0) { + if (notificationManager.getMaxRetries() <= 0) { return; } long failedExecutionsCount = notificationManager.countExecutionsWithStatus( execution.getTask().getKey(), NotificationJob.Status.NOT_SENT.name()); - if (failedExecutionsCount <= maxRetries) { + if (failedExecutionsCount <= notificationManager.getMaxRetries()) { LOG.debug("Execution of notification task {} will be retried [{}/{}]", - execution.getTask(), failedExecutionsCount, maxRetries); + execution.getTask(), failedExecutionsCount, notificationManager.getMaxRetries()); notificationManager.setTaskExecuted(execution.getTask().getKey(), false); auditManager.audit( http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/core/provisioning-java/src/main/resources/mail.properties ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/resources/mail.properties b/core/provisioning-java/src/main/resources/mail.properties index 7a6653e..e0cb1b0 100644 --- a/core/provisioning-java/src/main/resources/mail.properties +++ b/core/provisioning-java/src/main/resources/mail.properties @@ -14,11 +14,17 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +conf.directory=${conf.directory} + smtpHost=none.syncope.apache.org smtpPort=25 smtpUser= smtpPassword= smtpProtocol=smtp smtpEncoding=UTF-8 -smtpConnectionTimeout=3000 -mailDebug=false +mail.debug=false + +# Add more properties starting with mail.smtp.* from +# https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties +mail.smtp.connectiontimeout=3000 +mail.smtp.starttls.enable=false http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/core/provisioning-java/src/main/resources/provisioningContext.xml ---------------------------------------------------------------------- diff --git a/core/provisioning-java/src/main/resources/provisioningContext.xml b/core/provisioning-java/src/main/resources/provisioningContext.xml index fab7099..023d234 100644 --- a/core/provisioning-java/src/main/resources/provisioningContext.xml +++ b/core/provisioning-java/src/main/resources/provisioningContext.xml @@ -104,13 +104,6 @@ under the License. <property name="username" value="${smtpUser}"/> <property name="password" value="${smtpPassword}"/> <property name="protocol" value="${smtpProtocol}"/> - - <property name="javaMailProperties"> - <props> - <prop key="mail.smtp.connectiontimeout">${smtpConnectionTimeout}</prop> - <prop key="mail.debug">${mailDebug}</prop> - </props> - </property> </bean> <bean class="org.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl"/> http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/core/spring/src/main/java/org/apache/syncope/core/spring/security/Encryptor.java ---------------------------------------------------------------------- diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/Encryptor.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/Encryptor.java index a97094a..91cf40a 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/Encryptor.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/Encryptor.java @@ -18,7 +18,6 @@ */ package org.apache.syncope.core.spring.security; -import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; @@ -31,9 +30,9 @@ import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.spec.SecretKeySpec; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.PropertyUtils; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.jasypt.commons.CommonUtils; import org.jasypt.digest.StandardStringDigester; @@ -88,11 +87,8 @@ public final class Encryptor { private static Boolean ULSSC; static { - InputStream propStream = null; try { - propStream = Encryptor.class.getResourceAsStream("/security.properties"); - Properties props = new Properties(); - props.load(propStream); + Properties props = PropertyUtils.read(Encryptor.class, "security.properties", "conf.directory").getLeft(); SECRET_KEY = props.getProperty("secretKey"); SALT_ITERATIONS = Integer.valueOf(props.getProperty("digester.saltIterations")); @@ -102,8 +98,6 @@ public final class Encryptor { ULSSC = Boolean.valueOf(props.getProperty("digester.useLenientSaltSizeCheck")); } catch (Exception e) { LOG.error("Could not read security parameters", e); - } finally { - IOUtils.closeQuietly(propStream); } if (SECRET_KEY == null) { @@ -160,8 +154,8 @@ public final class Encryptor { actualKeyPadding.append(randomChars); actualKey = actualKeyPadding.toString(); LOG.warn("The secret key is too short (< 16), adding some random characters. " - + "Passwords encrypted with AES and this key will not be recoverable " - + "as a result if the container is restarted."); + + "Passwords encrypted with AES and this key will not be recoverable " + + "as a result if the container is restarted."); } try { http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/core/spring/src/main/resources/security.properties ---------------------------------------------------------------------- diff --git a/core/spring/src/main/resources/security.properties b/core/spring/src/main/resources/security.properties index 3f72ad0..870d853 100644 --- a/core/spring/src/main/resources/security.properties +++ b/core/spring/src/main/resources/security.properties @@ -14,6 +14,8 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +conf.directory=${conf.directory} + adminUser=${adminUser} adminPassword=${adminPassword} adminPasswordAlgorithm=SSHA256 http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2SPAgentSetup.java ---------------------------------------------------------------------- diff --git a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2SPAgentSetup.java b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2SPAgentSetup.java index e42d438..870f4b6 100644 --- a/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2SPAgentSetup.java +++ b/ext/saml2sp/agent/src/main/java/org/apache/syncope/ext/saml2lsp/agent/SAML2SPAgentSetup.java @@ -18,16 +18,14 @@ */ package org.apache.syncope.ext.saml2lsp.agent; -import java.io.File; -import java.io.InputStream; import java.util.Properties; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.syncope.client.lib.AnonymousAuthenticationHandler; import org.apache.syncope.client.lib.SyncopeClientFactoryBean; +import org.apache.syncope.common.lib.PropertyUtils; @WebListener public class SAML2SPAgentSetup implements ServletContextListener { @@ -44,20 +42,7 @@ public class SAML2SPAgentSetup implements ServletContextListener { @Override public void contextInitialized(final ServletContextEvent sce) { // read saml2spagent.properties - Properties props = new Properties(); - try (InputStream is = getClass().getResourceAsStream("/" + SAML2SP_AGENT_PROPERTIES)) { - props.load(is); - File confDir = new File(props.getProperty("conf.directory")); - if (confDir.exists() && confDir.canRead() && confDir.isDirectory()) { - File consoleDirProps = FileUtils.getFile(confDir, SAML2SP_AGENT_PROPERTIES); - if (consoleDirProps.exists() && consoleDirProps.canRead() && consoleDirProps.isFile()) { - props.clear(); - props.load(FileUtils.openInputStream(consoleDirProps)); - } - } - } catch (Exception e) { - throw new RuntimeException("Could not read " + SAML2SP_AGENT_PROPERTIES, e); - } + Properties props = PropertyUtils.read(getClass(), SAML2SP_AGENT_PROPERTIES, "conf.directory").getLeft(); String anonymousUser = props.getProperty("anonymousUser"); assertNotNull(anonymousUser, "<anonymousUser>"); http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SPLoader.java ---------------------------------------------------------------------- diff --git a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SPLoader.java b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SPLoader.java index f9a5eec..a4230b2 100644 --- a/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SPLoader.java +++ b/ext/saml2sp/logic/src/main/java/org/apache/syncope/core/logic/init/SAML2SPLoader.java @@ -18,15 +18,15 @@ */ package org.apache.syncope.core.logic.init; -import java.io.File; import java.io.InputStream; import java.security.KeyStore; import java.util.HashMap; import java.util.Map; import java.util.Properties; import net.shibboleth.utilities.java.support.resolver.CriteriaSet; -import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.PropertyUtils; import org.apache.syncope.core.persistence.api.SyncopeLoader; import org.apache.syncope.core.provisioning.api.EntitlementsHolder; import org.apache.syncope.common.lib.types.SAML2SPEntitlement; @@ -80,25 +80,9 @@ public class SAML2SPLoader implements SyncopeLoader { public void load() { EntitlementsHolder.getInstance().init(SAML2SPEntitlement.values()); - String confDirectory = null; - - Properties props = new Properties(); - try (InputStream is = getClass().getResourceAsStream("/" + SAML2SP_LOGIC_PROPERTIES)) { - props.load(is); - confDirectory = props.getProperty("conf.directory"); - - File confDir = new File(confDirectory); - if (confDir.exists() && confDir.canRead() && confDir.isDirectory()) { - File confDirProps = FileUtils.getFile(confDir, SAML2SP_LOGIC_PROPERTIES); - if (confDirProps.exists() && confDirProps.canRead() && confDirProps.isFile()) { - props.clear(); - props.load(FileUtils.openInputStream(confDirProps)); - confDirectory = props.getProperty("conf.directory"); - } - } - } catch (Exception e) { - throw new RuntimeException("Could not read " + SAML2SP_LOGIC_PROPERTIES, e); - } + Pair<Properties, String> init = PropertyUtils.read(getClass(), SAML2SP_LOGIC_PROPERTIES, "conf.directory"); + Properties props = init.getLeft(); + String confDirectory = init.getRight(); assertNotNull(confDirectory, "<conf.directory>"); http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/fit/core-reference/src/main/resources/log4j2.xml ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/main/resources/log4j2.xml b/fit/core-reference/src/main/resources/log4j2.xml index 4c9684f..5dde70c 100644 --- a/fit/core-reference/src/main/resources/log4j2.xml +++ b/fit/core-reference/src/main/resources/log4j2.xml @@ -189,6 +189,16 @@ under the License. <appender-ref ref="main"/> </asyncLogger> + <!-- To enable when setting 'mail.debug=true' in mail.properties --> + <!--<asyncLogger name="org.apache.syncope.core.provisioning.java.job.notification" additivity="false" level="DEBUG"> + <appender-ref ref="mainFile"/> + <appender-ref ref="main"/> + </asyncLogger> + <asyncLogger name="javax.mail" additivity="false" level="DEBUG"> + <appender-ref ref="mainFile"/> + <appender-ref ref="main"/> + </asyncLogger>--> + <root level="INFO"> <appender-ref ref="mainFile"/> <appender-ref ref="main"/> http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/fit/core-reference/src/main/resources/mail.properties ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/main/resources/mail.properties b/fit/core-reference/src/main/resources/mail.properties index e8b9a5f..f8e1c05 100644 --- a/fit/core-reference/src/main/resources/mail.properties +++ b/fit/core-reference/src/main/resources/mail.properties @@ -14,11 +14,17 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. +conf.directory=${conf.directory} + smtpHost=localhost smtpPort=2525 smtpUser= smtpPassword= smtpProtocol=smtp smtpEncoding=UTF-8 -smtpConnectionTimeout=3000 -mailDebug=false +mail.debug=false + +# Add more properties starting with mail.smtp.* from +# https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties +mail.smtp.connectiontimeout=3000 +mail.smtp.starttls.enable=false http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java ---------------------------------------------------------------------- diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java index 2f568b5..a4ca1f4 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java @@ -103,7 +103,7 @@ public class NotificationTaskITCase extends AbstractNotificationTaskITCase { NotificationTaskTO taskTO = findNotificationTask(created.getLeft(), 50); assertNotNull(taskTO); assertNotNull(taskTO.getNotification()); - assertTrue(taskTO.getExecutions().isEmpty()); + int preExecs = taskTO.getExecutions().size(); // 4. verify notification could not be delivered execTask(taskService, taskTO.getKey(), NotificationJob.Status.NOT_SENT.name(), 5, false); @@ -111,7 +111,7 @@ public class NotificationTaskITCase extends AbstractNotificationTaskITCase { taskTO = taskService.read(taskTO.getKey(), true); assertNotNull(taskTO); assertFalse(taskTO.isExecuted()); - assertFalse(taskTO.getExecutions().isEmpty()); + assertTrue(preExecs <= taskTO.getExecutions().size()); for (ExecTO exec : taskTO.getExecutions()) { assertEquals(NotificationJob.Status.NOT_SENT.name(), exec.getStatus()); } http://git-wip-us.apache.org/repos/asf/syncope/blob/7ad5d19b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/emailconfiguration.adoc ---------------------------------------------------------------------- diff --git a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/emailconfiguration.adoc b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/emailconfiguration.adoc index 6863f6d..9787dc3 100644 --- a/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/emailconfiguration.adoc +++ b/src/main/asciidoc/reference-guide/workingwithapachesyncope/systemadministration/emailconfiguration.adoc @@ -18,8 +18,8 @@ // ==== E-mail Configuration -The `mail.properties` holds the configuration options to enable the effective delivery of <<notifications,notification>> -e-mails: +The `mail.properties` file holds the configuration options to enable the effective delivery of +<<notifications,notification>> e-mails: * `smtpHost` - the mail server host, typically an SMTP host; * `smtpPort` - the mail server port; @@ -27,9 +27,59 @@ e-mails: * `smtpPassword` - (optional) the password for the account at the mail host; * `smtpProtocol` - the mail protocol; * `smtpEncoding` - the default encoding to use for MIME messages; -* `smtpConnectionTimeout` - the connection timeout value in milliseconds, to the mail host; -* `mailDebug` - when `true`, enable the debugging of email, including the handshake, authentication, delivery and -disconnection. +* `mail.smtp.starttls.enable` - when `true`, enable the use of the `STARTTLS` command to switch the connection to a +TLS-protected connection before issuing any login commands; +* `mail.smtp.connectiontimeout` - the connection timeout value in milliseconds, to the mail host; +* `mail.debug` - when `true`, enable the debugging of email processing including the handshake, authentication, delivery + and disconnection; in order for this setting to be effective, it is also required to add the following elements to the +`log4j2.xml` configuration file: +[source,xml] +<asyncLogger name="org.apache.syncope.core.provisioning.java.job.notification" + additivity="false" level="DEBUG"> + <appender-ref ref="mainFile"/> + <appender-ref ref="main"/> +</asyncLogger> +<asyncLogger name="javax.mail" additivity="false" level="DEBUG"> + <appender-ref ref="mainFile"/> + <appender-ref ref="main"/> +</asyncLogger> + +All the https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties[JavaMail(TM) properties^] +are available for usage. + +.Basic configuration, no authentication +==== +.... +conf.directory=${conf.directory} + +smtpHost=your.local.smtp.server +smtpPort=25 +smtpUser= +smtpPassword= +smtpProtocol=smtp +smtpEncoding=UTF-8 +mail.debug=false +mail.smtp.connectiontimeout=3000 +mail.smtp.starttls.enable=false +.... +==== + +.STARTTLS configuration, with authentication +==== +.... +conf.directory=${conf.directory} + +smtpHost=smtp.gmail.com +smtpPort=587 +smtpUser=your_usern...@gmail.com +smtpPassword=your_password +smtpProtocol=smtp +smtpEncoding=UTF-8 +mail.debug=false +mail.smtp.connectiontimeout=3000 +mail.smtp.starttls.enable=true +.... +==== [NOTE] In order to make the changes to `mail.properties` effective, the Java EE container needs to be restarted.