You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@eagle.apache.org by qi...@apache.org on 2016/11/17 05:18:34 UTC
incubator-eagle git commit: [MINOR] add settings for case: ldap
authentication over ssl
Repository: incubator-eagle
Updated Branches:
refs/heads/master 18f74d448 -> ce8e1c508
[MINOR] add settings for case: ldap authentication over ssl
To make it fit for ldap authentication over ssl protocol, add:
1. one config-attribute to indicate certificate absolute path.
2. checking code for verifying certificate's existence.
3. add unit test cases to cover the logic branches.
Author: anyway1021 <mw...@apache.org>
Closes #661 from anyway1021/ldap-auth-improve.
Project: http://git-wip-us.apache.org/repos/asf/incubator-eagle/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-eagle/commit/ce8e1c50
Tree: http://git-wip-us.apache.org/repos/asf/incubator-eagle/tree/ce8e1c50
Diff: http://git-wip-us.apache.org/repos/asf/incubator-eagle/diff/ce8e1c50
Branch: refs/heads/master
Commit: ce8e1c5080c0f9a8fae9010cbd29dc3c138f13d8
Parents: 18f74d4
Author: anyway1021 <mw...@apache.org>
Authored: Thu Nov 17 13:18:27 2016 +0800
Committer: Zhao, Qingwen <qi...@apache.org>
Committed: Thu Nov 17 13:18:27 2016 +0800
----------------------------------------------------------------------
eagle-server-assembly/src/main/conf/server.yml | 57 +++++++++-
.../authenticator/LdapBasicAuthenticator.java | 20 ++++
.../authentication/config/LdapSettings.java | 12 ++
.../src/main/resources/configuration.yml | 4 +
.../LdapBasicAuthenticatorTest.java | 110 ++++++++++++++++---
.../src/test/resources/configuration.yml | 4 +
6 files changed, 192 insertions(+), 15 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/ce8e1c50/eagle-server-assembly/src/main/conf/server.yml
----------------------------------------------------------------------
diff --git a/eagle-server-assembly/src/main/conf/server.yml b/eagle-server-assembly/src/main/conf/server.yml
index 5ea8b31..501d941 100644
--- a/eagle-server-assembly/src/main/conf/server.yml
+++ b/eagle-server-assembly/src/main/conf/server.yml
@@ -42,4 +42,59 @@ logging:
archive: true
archivedLogFilenamePattern: log/eagle-server-%d.log
archivedFileCount: 5
- timeZone: UTC
\ No newline at end of file
+ timeZone: UTC
+
+# ---------------------------------------------
+# Eagle Authentication Configuration
+# ---------------------------------------------
+auth:
+ # indicating if authentication is enabled, true for enabled, false for disabled
+ enabled: false
+
+ # indicating authentication mode, "simple" or "ldap"
+ mode: simple
+
+ # indicating whether to use cache: cache is usually used for authentications that may
+ # not handle high throughput (an RDBMS or LDAP server, for example)
+ caching: false
+
+ # indicating the cache policy, containing maximumSize and expireAfterWrite, e.g. maximumSize=10000, expireAfterWrite=10m
+ cachePolicy: maximumSize=10000, expireAfterWrite=1m
+
+ # indicating whether authorization is needed
+ authorization: false
+
+ # indicating whether @Auth annotation on parameters is needed
+ annotated: true
+
+ # for basic authentication, effective only when auth.mode=simple
+ simple:
+ # username for basic authentication, effective only when auth.mode=simple
+ username: admin
+ # password for basic authentication, effective only when auth.mode=simple
+ password: secret
+
+ # for ldap authentication, effective only when auth.mode=ldap
+ ldap:
+ # url providing ldap service. By convention, the port for typical ldap service is 389, and ldap service over ssl
+ # uses port 636 with protocol "ldaps", which requires certificates pre-installed.
+ providerUrl: ldap://server.address.or.domain:port
+
+ # template string containing ${USERNAME} placeholder. This is designed for some orgs who don't use plain usernames
+ # to authenticate, e.g. they may use its members' email address as the username: ${USERNAME}@some.org. When username
+ # is supposed to be recognized originally, just configure this parameter as ${USERNAME}
+ principalTemplate: ${USERNAME}@maybe.email.suffix
+
+ # string of strategy used by ldap service. "simple" is usually supported in most circumstances, we can use it by
+ # default or leave it a blank string.
+ strategy: simple
+
+ # the absolute path of ssl certificate file. This attribute is required conditional only when the auth -> mode is set
+ # as "ldap" and providerUrl starting with "ldaps://".
+ certificateAbsolutePath: /certificate/absolute/path
+
+ # timeout expression for connecting to ldap service endpoint
+ connectingTimeout: 500ms
+
+ # timeout expression for reading from ldap service
+ readingTimeout: 500ms
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/ce8e1c50/eagle-server/src/main/java/org/apache/eagle/server/authentication/authenticator/LdapBasicAuthenticator.java
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/java/org/apache/eagle/server/authentication/authenticator/LdapBasicAuthenticator.java b/eagle-server/src/main/java/org/apache/eagle/server/authentication/authenticator/LdapBasicAuthenticator.java
index 14652c3..c67dea8 100644
--- a/eagle-server/src/main/java/org/apache/eagle/server/authentication/authenticator/LdapBasicAuthenticator.java
+++ b/eagle-server/src/main/java/org/apache/eagle/server/authentication/authenticator/LdapBasicAuthenticator.java
@@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
import javax.naming.Context;
import javax.naming.directory.InitialDirContext;
+import java.io.File;
import java.util.Hashtable;
public class LdapBasicAuthenticator implements Authenticator<BasicCredentials, User> {
@@ -34,6 +35,10 @@ public class LdapBasicAuthenticator implements Authenticator<BasicCredentials, U
private static final String LDAP_LDAP_CTX_FACTORY_NAME = "com.sun.jndi.ldap.LdapCtxFactory";
private static final String LDAP_CONNECT_TIMEOUT_KEY = "com.sun.jndi.ldap.connect.timeout";
private static final String LDAP_READ_TIMEOUT_KEY = "com.sun.jndi.ldap.read.timeout";
+ private static final String SYS_PROP_SSL_KEY_STORE = "javax.net.ssl.keyStore";
+ private static final String SYS_PROP_SSL_TRUST_STORE = "javax.net.ssl.trustStore";
+ private static final String LDAPS_URL_PREFIX = "ldaps://";
+ private static final String SSL_PROTOCOL_VALUE = "ssl";
private LdapSettings settings = null;
public LdapBasicAuthenticator(LdapSettings settings) {
@@ -70,6 +75,21 @@ public class LdapBasicAuthenticator implements Authenticator<BasicCredentials, U
env.put(Context.SECURITY_AUTHENTICATION, strategy);
}
+ if (providerUrl.toLowerCase().startsWith(LDAPS_URL_PREFIX)) { // using ldap over ssl to authenticate
+ env.put(Context.SECURITY_PROTOCOL, SSL_PROTOCOL_VALUE);
+
+ String certificateAbsolutePath = settings.getCertificateAbsolutePath();
+ if (certificateAbsolutePath == null || "".equals(certificateAbsolutePath)) {
+ throw new RuntimeException("The attribute 'certificateAbsolutePath' must be set when using ldap over ssl to authenticate.");
+ }
+ if (!new File(certificateAbsolutePath).exists()) {
+ throw new RuntimeException(String.format("The file specified not existing: %s", certificateAbsolutePath));
+ }
+
+ System.setProperty(SYS_PROP_SSL_KEY_STORE, certificateAbsolutePath);
+ System.setProperty(SYS_PROP_SSL_TRUST_STORE, certificateAbsolutePath);
+ }
+
env.put(Context.SECURITY_PRINCIPAL, comprisePrincipal(sanitizedUsername));
env.put(Context.SECURITY_CREDENTIALS, password);
return env;
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/ce8e1c50/eagle-server/src/main/java/org/apache/eagle/server/authentication/config/LdapSettings.java
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/java/org/apache/eagle/server/authentication/config/LdapSettings.java b/eagle-server/src/main/java/org/apache/eagle/server/authentication/config/LdapSettings.java
index 6bb3303..9297e7e 100644
--- a/eagle-server/src/main/java/org/apache/eagle/server/authentication/config/LdapSettings.java
+++ b/eagle-server/src/main/java/org/apache/eagle/server/authentication/config/LdapSettings.java
@@ -24,6 +24,7 @@ public class LdapSettings {
private String providerUrl = "";
private String strategy = "";
private String principalTemplate = "";
+ private String certificateAbsolutePath = "";
private Duration connectingTimeout = Duration.parse("500ms");
private Duration readingTimeout = Duration.parse("500ms");
@@ -81,4 +82,15 @@ public class LdapSettings {
this.readingTimeout = readingTimeout;
return this;
}
+
+ @JsonProperty
+ public String getCertificateAbsolutePath() {
+ return certificateAbsolutePath;
+ }
+
+ @JsonProperty
+ public LdapSettings setCertificateAbsolutePath(String certificateAbsolutePath) {
+ this.certificateAbsolutePath = certificateAbsolutePath;
+ return this;
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/ce8e1c50/eagle-server/src/main/resources/configuration.yml
----------------------------------------------------------------------
diff --git a/eagle-server/src/main/resources/configuration.yml b/eagle-server/src/main/resources/configuration.yml
index 8d388b0..eabb8a1 100644
--- a/eagle-server/src/main/resources/configuration.yml
+++ b/eagle-server/src/main/resources/configuration.yml
@@ -66,6 +66,10 @@ auth:
# default or leave it a blank string.
strategy: simple
+ # the absolute path of ssl certificate file. This attribute is required conditional only when the auth -> mode is set
+ # as "ldap" and providerUrl starting with "ldaps://".
+ certificateAbsolutePath: /certificate/absolute/path
+
# timeout expression for connecting to ldap service endpoint
connectingTimeout: 500ms
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/ce8e1c50/eagle-server/src/test/java/org/apache/eagle/server/authentication/authenticator/LdapBasicAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/eagle-server/src/test/java/org/apache/eagle/server/authentication/authenticator/LdapBasicAuthenticatorTest.java b/eagle-server/src/test/java/org/apache/eagle/server/authentication/authenticator/LdapBasicAuthenticatorTest.java
index 492521f..8700a75 100644
--- a/eagle-server/src/test/java/org/apache/eagle/server/authentication/authenticator/LdapBasicAuthenticatorTest.java
+++ b/eagle-server/src/test/java/org/apache/eagle/server/authentication/authenticator/LdapBasicAuthenticatorTest.java
@@ -30,29 +30,38 @@ public class LdapBasicAuthenticatorTest {
private static final String USERNAME_SUFFIX = "@some.emailbox.suffix";
private static final String USERNAME_TEMPLATE = "${USERNAME}" + USERNAME_SUFFIX;
private static final String LDAP_SERVICE_PROVIDER_URL = "ldap://some.address:port";
+ private static final String LDAP_SERVICE_PROVIDER_SSL_URL = "ldaps://some.address:port";
private static final String STRATEGY_SIMPLE = "customized";
private static final String CONNECTING_TIMEOUT_VALUE = "500ms";
private static final String READING_TIMEOUT_VALUE = "800ms";
private static final String LDAP_CTX_FACTORY_NAME = "com.sun.jndi.ldap.LdapCtxFactory";
private static final String LDAP_CONNECT_TIMEOUT_KEY = "com.sun.jndi.ldap.connect.timeout";
private static final String LDAP_READ_TIMEOUT_KEY = "com.sun.jndi.ldap.read.timeout";
- private static final LdapBasicAuthenticator AUTHENTICATOR_FOR_UTIL_METHODS = new LdapBasicAuthenticator(
- new LdapSettings()
- .setProviderUrl(LDAP_SERVICE_PROVIDER_URL)
- .setPrincipalTemplate(USERNAME_TEMPLATE)
+ private static final String SYS_PROP_SSL_KEY_STORE = "javax.net.ssl.keyStore";
+ private static final String SYS_PROP_SSL_TRUST_STORE = "javax.net.ssl.trustStore";
+ private static final String EXISTING_MOCK_FILE_PATH = String.format("%s/pom.xml", System.getProperty("user.dir"));
+ private static final LdapBasicAuthenticator AUTHENTICATOR_FOR_UTIL_METHODS_WITHOUT_SSL = new LdapBasicAuthenticator(
+ getNonSSLPreSettings().setPrincipalTemplate(USERNAME_TEMPLATE)
.setStrategy(STRATEGY_SIMPLE)
.setConnectingTimeout(Duration.parse(CONNECTING_TIMEOUT_VALUE))
.setReadingTimeout(Duration.parse(READING_TIMEOUT_VALUE))
);
+ private static final LdapBasicAuthenticator AUTHENTICATOR_FOR_UTIL_METHODS_WITH_SSL = new LdapBasicAuthenticator(
+ getSSLPreSettings().setPrincipalTemplate(USERNAME_TEMPLATE)
+ .setStrategy(STRATEGY_SIMPLE)
+ .setCertificateAbsolutePath(EXISTING_MOCK_FILE_PATH)
+ .setConnectingTimeout(Duration.parse(CONNECTING_TIMEOUT_VALUE))
+ .setReadingTimeout(Duration.parse(READING_TIMEOUT_VALUE))
+ );
@Test
public void testSanitizeUsername() {
String correctUsername = "userNAME_123.45Z";
- String sanitized = AUTHENTICATOR_FOR_UTIL_METHODS.sanitizeUsername(correctUsername);
+ String sanitized = AUTHENTICATOR_FOR_UTIL_METHODS_WITHOUT_SSL.sanitizeUsername(correctUsername);
Assert.assertEquals(correctUsername, sanitized);
String incorrectUsername = "userNAME-~!@#$%^&777*()_+-=`[]\\{}|;':\",./<>?\u4f60";
- sanitized = AUTHENTICATOR_FOR_UTIL_METHODS.sanitizeUsername(incorrectUsername);
+ sanitized = AUTHENTICATOR_FOR_UTIL_METHODS_WITHOUT_SSL.sanitizeUsername(incorrectUsername);
System.out.println(sanitized);
Assert.assertEquals("userNAME777_.", sanitized);
}
@@ -60,27 +69,100 @@ public class LdapBasicAuthenticatorTest {
@Test
public void testComprisePrincipal() {
String username = "my.userNAME_123";
- String principal = AUTHENTICATOR_FOR_UTIL_METHODS.comprisePrincipal(username);
+ String principal = AUTHENTICATOR_FOR_UTIL_METHODS_WITHOUT_SSL.comprisePrincipal(username);
Assert.assertEquals(username+USERNAME_SUFFIX, principal);
}
@Test
- public void testGetContextEnvironment() {
+ public void testGetContextEnvironmentNormal() {
String username = "username";
- String secret_phrase = "secret-phrase";
- Hashtable<String, String> env = AUTHENTICATOR_FOR_UTIL_METHODS.getContextEnvironment(username, secret_phrase);
+ String secretPhrase = "secret-phrase";
+ Hashtable<String, String> env = AUTHENTICATOR_FOR_UTIL_METHODS_WITHOUT_SSL.getContextEnvironment(username, secretPhrase);
Assert.assertEquals("unexpected ldap context factory name", LDAP_CTX_FACTORY_NAME, env.get(Context.INITIAL_CONTEXT_FACTORY));
- Assert.assertEquals("unexpected ldap serivce provider url", LDAP_SERVICE_PROVIDER_URL, env.get(Context.PROVIDER_URL));
+ Assert.assertEquals("unexpected ldap service provider url", LDAP_SERVICE_PROVIDER_URL, env.get(Context.PROVIDER_URL));
Assert.assertEquals("unexpected connecting timeout value", String.valueOf(Duration.parse(CONNECTING_TIMEOUT_VALUE).toMilliseconds()), env.get(LDAP_CONNECT_TIMEOUT_KEY));
Assert.assertEquals("unexpected reading timeout value", String.valueOf(Duration.parse(READING_TIMEOUT_VALUE).toMilliseconds()), env.get(LDAP_READ_TIMEOUT_KEY));
Assert.assertEquals("unexpected username", username+USERNAME_SUFFIX, env.get(Context.SECURITY_PRINCIPAL));
- Assert.assertEquals("unexpected secret credentials", secret_phrase, env.get(Context.SECURITY_CREDENTIALS));
+ Assert.assertEquals("unexpected secret credentials", secretPhrase, env.get(Context.SECURITY_CREDENTIALS));
Assert.assertEquals("unexpected strategy", STRATEGY_SIMPLE, env.get(Context.SECURITY_AUTHENTICATION));
+ }
+
+ @Test
+ public void testGetContextEnvironmentBlankStrategy() {
+ String username = "username";
+ String secretPhrase = "secret-phrase";
// check strategy while it's configured as ""
- LdapBasicAuthenticator blankStrategyAuthenticator = new LdapBasicAuthenticator(new LdapSettings().setStrategy(""));
- String strategyMaybeBlank = blankStrategyAuthenticator.getContextEnvironment(username, secret_phrase).get(Context.SECURITY_AUTHENTICATION);
+ LdapBasicAuthenticator blankStrategyAuthenticator = new LdapBasicAuthenticator(getNonSSLPreSettings().setStrategy(""));
+ String strategyMaybeBlank = blankStrategyAuthenticator.getContextEnvironment(username, secretPhrase).get(Context.SECURITY_AUTHENTICATION);
Assert.assertNull("unexpected strategy", strategyMaybeBlank);
}
+
+ @Test
+ public void testGetContextEnvironmentNormalWithSSL() {
+ String username = "username";
+ String secretPhrase = "secret-phrase";
+ Hashtable<String, String> env = AUTHENTICATOR_FOR_UTIL_METHODS_WITH_SSL.getContextEnvironment(username, secretPhrase);
+
+ Assert.assertEquals("unexpected ldap context factory name", LDAP_CTX_FACTORY_NAME, env.get(Context.INITIAL_CONTEXT_FACTORY));
+ Assert.assertEquals("unexpected ldap service provider url", LDAP_SERVICE_PROVIDER_SSL_URL, env.get(Context.PROVIDER_URL));
+ Assert.assertEquals("unexpected connecting timeout value", String.valueOf(Duration.parse(CONNECTING_TIMEOUT_VALUE).toMilliseconds()), env.get(LDAP_CONNECT_TIMEOUT_KEY));
+ Assert.assertEquals("unexpected reading timeout value", String.valueOf(Duration.parse(READING_TIMEOUT_VALUE).toMilliseconds()), env.get(LDAP_READ_TIMEOUT_KEY));
+ Assert.assertEquals("unexpected username", username+USERNAME_SUFFIX, env.get(Context.SECURITY_PRINCIPAL));
+ Assert.assertEquals("unexpected secret credentials", secretPhrase, env.get(Context.SECURITY_CREDENTIALS));
+ Assert.assertEquals("unexpected strategy", STRATEGY_SIMPLE, env.get(Context.SECURITY_AUTHENTICATION));
+ Assert.assertEquals("unexpected key-store path", EXISTING_MOCK_FILE_PATH, System.getProperty(SYS_PROP_SSL_KEY_STORE));
+ Assert.assertEquals("unexpected trust-store path", EXISTING_MOCK_FILE_PATH, System.getProperty(SYS_PROP_SSL_TRUST_STORE));
+ }
+
+ @Test
+ public void testGetContextEnvironmentMeaninglessCAPathSSL() {
+ String username = "username";
+ String secretPhrase = "secret-phrase";
+
+ // check null certificateAbsolutePath
+ try {
+ LdapBasicAuthenticator blankStrategyAuthenticator = new LdapBasicAuthenticator(getSSLPreSettings().setCertificateAbsolutePath(null));
+ blankStrategyAuthenticator.getContextEnvironment(username, secretPhrase).get(Context.SECURITY_AUTHENTICATION);
+ }
+ catch (Exception e) {
+ Assert.assertEquals("unexpected exception thrown", RuntimeException.class, e.getClass());
+ Assert.assertEquals("unexpected exception message", "The attribute 'certificateAbsolutePath' must be set when using ldap over ssl to authenticate.", e.getMessage());
+ }
+
+ // check "" certificateAbsolutePath
+ try {
+ LdapBasicAuthenticator blankStrategyAuthenticator = new LdapBasicAuthenticator(getSSLPreSettings().setCertificateAbsolutePath(""));
+ blankStrategyAuthenticator.getContextEnvironment(username, secretPhrase).get(Context.SECURITY_AUTHENTICATION);
+ }
+ catch (Exception e) {
+ Assert.assertEquals("unexpected exception thrown", RuntimeException.class, e.getClass());
+ Assert.assertEquals("unexpected exception message", "The attribute 'certificateAbsolutePath' must be set when using ldap over ssl to authenticate.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testGetContextEnvironmentUnexistingCA_SSL() {
+ String username = "username";
+ String secretPhrase = "secret-phrase";
+ String wrongCAPath = String.format("%s/this/cannot/be/existing", System.getProperty("user.dir"));
+ try {
+ // check with not existing path indicated by certificateAbsolutePath
+ LdapBasicAuthenticator blankStrategyAuthenticator = new LdapBasicAuthenticator(getSSLPreSettings().setCertificateAbsolutePath(wrongCAPath));
+ blankStrategyAuthenticator.getContextEnvironment(username, secretPhrase).get(Context.SECURITY_AUTHENTICATION);
+ }
+ catch (Exception e) {
+ Assert.assertEquals("unexpected exception thrown", RuntimeException.class, e.getClass());
+ Assert.assertEquals("unexpected exception message", String.format("The file specified not existing: %s", wrongCAPath), e.getMessage());
+ }
+ }
+
+ private static LdapSettings getNonSSLPreSettings() {
+ return new LdapSettings().setProviderUrl(LDAP_SERVICE_PROVIDER_URL);
+ }
+
+ private static LdapSettings getSSLPreSettings() {
+ return new LdapSettings().setProviderUrl(LDAP_SERVICE_PROVIDER_SSL_URL);
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/ce8e1c50/eagle-server/src/test/resources/configuration.yml
----------------------------------------------------------------------
diff --git a/eagle-server/src/test/resources/configuration.yml b/eagle-server/src/test/resources/configuration.yml
index 8d388b0..eabb8a1 100644
--- a/eagle-server/src/test/resources/configuration.yml
+++ b/eagle-server/src/test/resources/configuration.yml
@@ -66,6 +66,10 @@ auth:
# default or leave it a blank string.
strategy: simple
+ # the absolute path of ssl certificate file. This attribute is required conditional only when the auth -> mode is set
+ # as "ldap" and providerUrl starting with "ldaps://".
+ certificateAbsolutePath: /certificate/absolute/path
+
# timeout expression for connecting to ldap service endpoint
connectingTimeout: 500ms