[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.

Reply via email to