This is an automated email from the ASF dual-hosted git repository. ldywicki pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/karaf.git
The following commit(s) were added to refs/heads/master by this push: new d9d15d7 [KARAF-5418] Add LDAPPubkeyLoginModule JAAS module d9d15d7 is described below commit d9d15d7e40f7aa8a2ccf2e90464faba685df852b Author: Ciprian Ciubotariu <cheepe...@gmx.net> AuthorDate: Mon Oct 9 20:41:53 2017 +0300 [KARAF-5418] Add LDAPPubkeyLoginModule JAAS module This commit contains two test public/private key pairs that are used to exercise the LDAPPubkeyLoginModule --- jaas/modules/pom.xml | 5 + .../apache/karaf/jaas/modules/ldap/LDAPCache.java | 42 ++++++ .../karaf/jaas/modules/ldap/LDAPLoginModule.java | 30 +--- .../karaf/jaas/modules/ldap/LDAPOptions.java | 5 + .../jaas/modules/ldap/LDAPPubkeyLoginModule.java | 158 ++++++++++++++++++++ .../org/apache/karaf/jaas/modules/ldap/Util.java | 48 ++++++ .../modules/publickey/PublickeyLoginModule.java | 6 +- .../jaas/modules/NamePubkeyCallbackHandler.java | 63 ++++++++ .../modules/ldap/LDAPPubkeyLoginModuleTest.java | 161 +++++++++++++++++++++ .../jaas/modules/ldap/example.com_pubkey.ldif | 68 +++++++++ .../karaf/jaas/modules/ldap/ldap_pubkey.properties | 37 +++++ .../karaf/jaas/modules/ldap/ldaptest.admin.id_rsa | 15 ++ .../jaas/modules/ldap/ldaptest.admin.id_rsa.pub | 1 + .../karaf/jaas/modules/ldap/ldaptest.cheese.id_rsa | 15 ++ .../jaas/modules/ldap/ldaptest.cheese.id_rsa.pub | 1 + 15 files changed, 623 insertions(+), 32 deletions(-) diff --git a/jaas/modules/pom.xml b/jaas/modules/pom.xml index 8038102..a709599 100644 --- a/jaas/modules/pom.xml +++ b/jaas/modules/pom.xml @@ -116,6 +116,11 @@ <scope>test</scope> </dependency> <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-all</artifactId> <version>1.3</version> diff --git a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPCache.java b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPCache.java index 7566165..ed04fdc 100644 --- a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPCache.java +++ b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPCache.java @@ -69,6 +69,7 @@ public class LDAPCache implements Closeable, NamespaceChangeListener, ObjectChan private final Map<String, String[]> userDnAndNamespace; private final Map<String, String[]> userRoles; + private final Map<String, String[]> userPubkeys; private final LDAPOptions options; private DirContext context; @@ -76,6 +77,7 @@ public class LDAPCache implements Closeable, NamespaceChangeListener, ObjectChan this.options = options; userDnAndNamespace = new HashMap<>(); userRoles = new HashMap<>(); + userPubkeys = new HashMap<>(); } @Override @@ -212,6 +214,18 @@ public class LDAPCache implements Closeable, NamespaceChangeListener, ObjectChan return result; } + public synchronized String[] getUserPubkeys(String userDn) throws NamingException { + String[] result = userPubkeys.get(userDn); + if (result == null) { + result = doGetUserPubkeys(userDn); + if (!options.getDisableCache()) { + userPubkeys.put(userDn, result); + } + } + return result; + } + + protected Set<String> tryMappingRole(String role) { Set<String> roles = new HashSet<>(); if (options.getRoleMapping().isEmpty()) { @@ -292,6 +306,33 @@ public class LDAPCache implements Closeable, NamespaceChangeListener, ObjectChan } } + private String[] doGetUserPubkeys(String userDn) throws NamingException { + DirContext context = open(); + + String userPubkeyAttribute = options.getUserPubkeyAttribute(); + if (userPubkeyAttribute != null) { + LOGGER.debug("Looking for public keys of user {} in attribute {}", userDn, userPubkeyAttribute); + + Attributes attributes = context.getAttributes(userDn, new String[]{userPubkeyAttribute}); + Attribute pubkeyAttribute = attributes.get(userPubkeyAttribute); + + List<String> pubkeyList = new ArrayList<>(); + if (pubkeyAttribute != null) { + for (int i = 0; i < pubkeyAttribute.size(); i++) { + String pk = (String) pubkeyAttribute.get(i); + if (pk != null) { + pubkeyList.add(pk); + } + } + } + return pubkeyList.toArray(new String[pubkeyList.size()]); + } else { + LOGGER.debug("The user public key attribute is null so no keys were retrieved"); + return new String[] {}; + } + } + + @Override public void objectAdded(NamingEvent evt) { clearCache(); @@ -320,5 +361,6 @@ public class LDAPCache implements Closeable, NamespaceChangeListener, ObjectChan protected synchronized void clearCache() { userDnAndNamespace.clear(); userRoles.clear(); + userPubkeys.clear(); } } diff --git a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPLoginModule.java b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPLoginModule.java index 1838f58..eab81e8 100644 --- a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPLoginModule.java +++ b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPLoginModule.java @@ -72,7 +72,7 @@ public class LDAPLoginModule extends AbstractKarafLoginModule { throw new LoginException(unsupportedCallbackException.getMessage() + " not available to obtain information from user."); } - user = doRFC2254Encoding(((NameCallback) callbacks[0]).getName()); + user = Util.doRFC2254Encoding(((NameCallback) callbacks[0]).getName()); char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword(); @@ -160,34 +160,6 @@ public class LDAPLoginModule extends AbstractKarafLoginModule { return true; } - protected String doRFC2254Encoding(String inputString) { - StringBuffer buf = new StringBuffer(inputString.length()); - for (int i = 0; i < inputString.length(); i++) { - char c = inputString.charAt(i); - switch (c) { - case '\\': - buf.append("\\5c"); - break; - case '*': - buf.append("\\2a"); - break; - case '(': - buf.append("\\28"); - break; - case ')': - buf.append("\\29"); - break; - case '\0': - buf.append("\\00"); - break; - default: - buf.append(c); - break; - } - } - return buf.toString(); - } - public boolean abort() throws LoginException { return true; } diff --git a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPOptions.java b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPOptions.java index 38b4d44..6328276 100644 --- a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPOptions.java +++ b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPOptions.java @@ -38,6 +38,7 @@ public class LDAPOptions { public static final String USER_BASE_DN = "user.base.dn"; public static final String USER_FILTER = "user.filter"; public static final String USER_SEARCH_SUBTREE = "user.search.subtree"; + public static final String USER_PUBKEY_ATTRIBUTE = "user.pubkey.attribute"; public static final String ROLE_BASE_DN = "role.base.dn"; public static final String ROLE_FILTER = "role.filter"; public static final String ROLE_NAME_ATTRIBUTE = "role.name.attribute"; @@ -99,6 +100,10 @@ public class LDAPOptions { return Boolean.parseBoolean((String) options.get(USER_SEARCH_SUBTREE)); } + public String getUserPubkeyAttribute() { + return (String) options.get(USER_PUBKEY_ATTRIBUTE); + } + public String getRoleFilter() { return (String) options.get(ROLE_FILTER); } diff --git a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPPubkeyLoginModule.java b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPPubkeyLoginModule.java new file mode 100644 index 0000000..50e8729 --- /dev/null +++ b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/LDAPPubkeyLoginModule.java @@ -0,0 +1,158 @@ +/* + * Licensed 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. + * under the License. + */ +package org.apache.karaf.jaas.modules.ldap; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.LoginException; +import java.io.IOException; +import java.security.PublicKey; +import java.util.HashSet; +import java.util.Map; +import javax.naming.NamingException; +import javax.security.auth.login.FailedLoginException; + +import org.apache.karaf.jaas.boot.principal.RolePrincipal; +import org.apache.karaf.jaas.boot.principal.UserPrincipal; +import org.apache.karaf.jaas.modules.AbstractKarafLoginModule; +import org.apache.karaf.jaas.modules.publickey.PublickeyCallback; +import org.apache.karaf.jaas.modules.publickey.PublickeyLoginModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Karaf JAAS login module which uses a LDAP backend. + */ +public class LDAPPubkeyLoginModule extends AbstractKarafLoginModule { + + private static Logger logger = LoggerFactory.getLogger(LDAPPubkeyLoginModule.class); + + public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) { + super.initialize(subject, callbackHandler, options); + LDAPCache.clear(); + } + + public boolean login() throws LoginException { + ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + try { + return doLogin(); + } finally { + ManagedSSLSocketFactory.setSocketFactory(null); + Thread.currentThread().setContextClassLoader(tccl); + } + } + + protected boolean doLogin() throws LoginException { + Callback[] callbacks = new Callback[2]; + callbacks[0] = new NameCallback("Username: "); + callbacks[1] = new PublickeyCallback(); + + try { + callbackHandler.handle(callbacks); + } catch (IOException ioException) { + throw new LoginException(ioException.getMessage()); + } catch (UnsupportedCallbackException unsupportedCallbackException) { + throw new LoginException(unsupportedCallbackException.getMessage() + " not available to obtain information from user."); + } + + user = Util.doRFC2254Encoding(((NameCallback) callbacks[0]).getName()); + + PublicKey remotePubkey = ((PublickeyCallback) callbacks[1]).getPublicKey(); + + LDAPOptions options = new LDAPOptions(this.options); + if (options.isUsernameTrim()) { + if (user != null) { + user = user.trim(); + } + } + + principals = new HashSet<>(); + LDAPCache cache = LDAPCache.getCache(options); + + // step 1: get the user DN + final String[] userDnAndNamespace; + try { + logger.debug("Get the user DN."); + userDnAndNamespace = cache.getUserDnAndNamespace(user); + if (userDnAndNamespace == null) { + return false; + } + } catch (Exception e) { + logger.warn("Can't connect to the LDAP server: {}", e.getMessage(), e); + throw new LoginException("Can't connect to the LDAP server: " + e.getMessage()); + } + + String userFullDn = userDnAndNamespace[0] + "," + options.getUserBaseDn(); + + // step 2: pubkey authentication + try { + authenticatePubkey(userFullDn, remotePubkey, cache); + } catch (NamingException e) { + logger.warn("Can't connect to the LDAP server: {}", e.getMessage(), e); + throw new LoginException("Can't connect to the LDAP server: " + e.getMessage()); + } catch (FailedLoginException e) { + if (!this.detailedLoginExcepion) { + throw new LoginException("Authentication failed"); + } else { + logger.warn("Public key authentication failed for user {}: {}", user, e.getMessage(), e); + throw new LoginException("Public key authentication failed for user " + user + ": " + e.getMessage()); + } + + } + + principals.add(new UserPrincipal(user)); + + // step 3: retrieving user roles + try { + String[] roles = cache.getUserRoles(user, userDnAndNamespace[0], userDnAndNamespace[1]); + for (String role : roles) { + principals.add(new RolePrincipal(role)); + } + } catch (Exception e) { + throw new LoginException("Can't get user " + user + " roles: " + e.getMessage()); + } + + return true; + } + + private void authenticatePubkey(String userDn, PublicKey key, LDAPCache cache) throws FailedLoginException, NamingException { + if (key == null) + throw new FailedLoginException("no public key supplied by the client"); + String[] storedKeys = cache.getUserPubkeys(userDn); + if (storedKeys.length > 0) { + String keyString = PublickeyLoginModule.getString(key); + for (String storedKey : storedKeys) { + if (keyString.equals(storedKey)) { + return; + } + } + } + throw new FailedLoginException("no matching public key found"); + } + + public boolean abort() throws LoginException { + return true; + } + + public boolean logout() throws LoginException { + subject.getPrincipals().removeAll(principals); + principals.clear(); + return true; + } + +} diff --git a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/Util.java b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/Util.java new file mode 100644 index 0000000..7ad2dd5 --- /dev/null +++ b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/ldap/Util.java @@ -0,0 +1,48 @@ +/* + * + * Licensed 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. + * under the License. + */ +package org.apache.karaf.jaas.modules.ldap; + +class Util { + + static String doRFC2254Encoding(String inputString) { + StringBuilder buf = new StringBuilder(inputString.length()); + for (int i = 0; i < inputString.length(); i++) { + char c = inputString.charAt(i); + switch (c) { + case '\\': + buf.append("\\5c"); + break; + case '*': + buf.append("\\2a"); + break; + case '(': + buf.append("\\28"); + break; + case ')': + buf.append("\\29"); + break; + case '\u0000': + buf.append("\\00"); + break; + default: + buf.append(c); + break; + } + } + return buf.toString(); + } + +} diff --git a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/publickey/PublickeyLoginModule.java b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/publickey/PublickeyLoginModule.java index d04f0ab..246592b 100644 --- a/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/publickey/PublickeyLoginModule.java +++ b/jaas/modules/src/main/java/org/apache/karaf/jaas/modules/publickey/PublickeyLoginModule.java @@ -145,7 +145,7 @@ public class PublickeyLoginModule extends AbstractKarafLoginModule { return true; } - private String getString(PublicKey key) throws FailedLoginException { + public static String getString(PublicKey key) throws FailedLoginException { try { if (key instanceof DSAPublicKey) { DSAPublicKey dsa = (DSAPublicKey) key; @@ -175,13 +175,13 @@ public class PublickeyLoginModule extends AbstractKarafLoginModule { } } - private void write(DataOutputStream dos, BigInteger integer) throws IOException { + private static void write(DataOutputStream dos, BigInteger integer) throws IOException { byte[] data = integer.toByteArray(); dos.writeInt(data.length); dos.write(data, 0, data.length); } - private void write(DataOutputStream dos, String str) throws IOException { + private static void write(DataOutputStream dos, String str) throws IOException { byte[] data = str.getBytes(); dos.writeInt(data.length); dos.write(data); diff --git a/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/NamePubkeyCallbackHandler.java b/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/NamePubkeyCallbackHandler.java new file mode 100644 index 0000000..f85b271 --- /dev/null +++ b/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/NamePubkeyCallbackHandler.java @@ -0,0 +1,63 @@ +/* + * Licensed 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. + * under the License. + */ +package org.apache.karaf.jaas.modules; + +import java.io.IOException; +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.PublicKey; +import java.util.Objects; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import org.apache.karaf.jaas.modules.publickey.PublickeyCallback; +import org.apache.sshd.common.keyprovider.FileKeyPairProvider; + +/** + * {@link CallbackHandler} implementation handling a name and public key. + */ +public class NamePubkeyCallbackHandler implements CallbackHandler { + + private final String name; + private final PublicKey publicKey; + + public NamePubkeyCallbackHandler(String name, PublicKey publicKey) { + this.name = Objects.requireNonNull(name); + this.publicKey = Objects.requireNonNull(publicKey); + } + + public NamePubkeyCallbackHandler(String name, Path publicKeyFile) throws IOException { + this.name = Objects.requireNonNull(name); + + FileKeyPairProvider provider = new FileKeyPairProvider(publicKeyFile); + Iterable<KeyPair> keys = provider.loadKeys(); + if (!keys.iterator().hasNext()) { + throw new IOException("no public keys loaded"); + } + this.publicKey = keys.iterator().next().getPublic(); + } + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback cb : callbacks) { + if (cb instanceof NameCallback) { + ((NameCallback) cb).setName(name); + } else if (cb instanceof PublickeyCallback) { + ((PublickeyCallback) cb).setPublicKey(publicKey); + } + } + } +} diff --git a/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/LDAPPubkeyLoginModuleTest.java b/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/LDAPPubkeyLoginModuleTest.java new file mode 100644 index 0000000..b5a1d40 --- /dev/null +++ b/jaas/modules/src/test/java/org/apache/karaf/jaas/modules/ldap/LDAPPubkeyLoginModuleTest.java @@ -0,0 +1,161 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.apache.karaf.jaas.modules.ldap; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import javax.security.auth.Subject; +import javax.security.auth.login.LoginException; +import org.apache.directory.server.annotations.CreateLdapServer; +import org.apache.directory.server.annotations.CreateTransport; +import org.apache.directory.server.core.annotations.ApplyLdifFiles; +import org.apache.directory.server.core.annotations.CreateDS; +import org.apache.directory.server.core.annotations.CreatePartition; +import org.apache.directory.server.core.integ.AbstractLdapTestUnit; +import static org.apache.directory.server.core.integ.AbstractLdapTestUnit.getLdapServer; +import org.apache.directory.server.core.integ.FrameworkRunner; +import org.apache.felix.utils.properties.Properties; +import org.apache.karaf.jaas.boot.principal.RolePrincipal; +import org.apache.karaf.jaas.boot.principal.UserPrincipal; +import org.apache.karaf.jaas.modules.NamePubkeyCallbackHandler; +import static org.apache.karaf.jaas.modules.PrincipalHelper.names; +import static org.apache.karaf.jaas.modules.ldap.LdapPropsUpdater.ldapProps; +import org.apache.log4j.Level; +import org.hamcrest.Matchers; +import static org.hamcrest.Matchers.containsInAnyOrder; +import org.junit.After; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(FrameworkRunner.class) +@CreateLdapServer(transports = { + @CreateTransport(protocol = "LDAP")}) +@CreateDS(name = "LdapPubkeyLoginModuleTest-class", + partitions = { + @CreatePartition(name = "example", suffix = "dc=example,dc=com")}) +@ApplyLdifFiles( + "org/apache/karaf/jaas/modules/ldap/example.com_pubkey.ldif" +) +public class LDAPPubkeyLoginModuleTest extends AbstractLdapTestUnit { + + private static final String LDAP_PROPERTIES_FILE = "org/apache/karaf/jaas/modules/ldap/ldap_pubkey.properties"; + + @Before + public void updatePort() throws Exception { + ldapProps(LDAP_PROPERTIES_FILE, LdapLoginModuleTest::replacePort); + } + + public static String replacePort(String line) { + return line.replaceAll("portno", "" + getLdapServer().getPort()); + } + + @After + public void tearDown() { + LDAPCache.clear(); + } + + @Test + public void testAdminLogin() throws Exception { + Properties options = ldapLoginModuleOptions(); + LDAPPubkeyLoginModule module = new LDAPPubkeyLoginModule(); + Subject subject = new Subject(); + Path pubkeyFile = srcTestResourcePath("org/apache/karaf/jaas/modules/ldap/ldaptest.admin.id_rsa"); + module.initialize(subject, new NamePubkeyCallbackHandler("admin", pubkeyFile), null, options); + + assertEquals("Precondition", 0, subject.getPrincipals().size()); + assertTrue(module.login()); + assertTrue(module.commit()); + + assertEquals(2, subject.getPrincipals().size()); + assertThat(names(subject.getPrincipals(UserPrincipal.class)), containsInAnyOrder("admin")); + assertThat(names(subject.getPrincipals(RolePrincipal.class)), containsInAnyOrder("admin")); + + assertTrue(module.logout()); + assertEquals("Principals should be gone as the user has logged out", 0, subject.getPrincipals().size()); + } + + @Test + public void testNonAdminLogin() throws Exception { + Properties options = ldapLoginModuleOptions(); + LDAPPubkeyLoginModule module = new LDAPPubkeyLoginModule(); + Subject subject = new Subject(); + Path pubkeyFile = srcTestResourcePath("org/apache/karaf/jaas/modules/ldap/ldaptest.cheese.id_rsa"); + module.initialize(subject, new NamePubkeyCallbackHandler("cheese", pubkeyFile), null, options); + + assertEquals("Precondition", 0, subject.getPrincipals().size()); + assertTrue(module.login()); + assertTrue(module.commit()); + + assertEquals(1, subject.getPrincipals().size()); + assertThat(names(subject.getPrincipals(UserPrincipal.class)), containsInAnyOrder("cheese")); + assertThat(names(subject.getPrincipals(RolePrincipal.class)), Matchers.empty()); + + assertTrue(module.logout()); + assertEquals("Principals should be gone as the user has logged out", 0, subject.getPrincipals().size()); + } + + @Test + public void testBadPrivateKey() throws Exception { + Properties options = ldapLoginModuleOptions(); + LDAPPubkeyLoginModule module = new LDAPPubkeyLoginModule(); + Subject subject = new Subject(); + Path pubkeyFile = srcTestResourcePath("org/apache/karaf/jaas/modules/ldap/ldaptest.cheese.id_rsa"); + module.initialize(subject, new NamePubkeyCallbackHandler("admin", pubkeyFile), null, options); + + assertEquals("Precondition", 0, subject.getPrincipals().size()); + org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(LDAPLoginModule.class); + Level oldLevel = logger.getLevel(); + logger.setLevel(Level.OFF); + try { + module.login(); + fail("Should have thrown LoginException"); + } catch (LoginException e) { + assertTrue(e.getMessage().startsWith("Authentication failed")); + } finally { + logger.setLevel(oldLevel); + } + + } + + @Test + public void testUserNotFound() throws Exception { + Properties options = ldapLoginModuleOptions(); + LDAPPubkeyLoginModule module = new LDAPPubkeyLoginModule(); + Subject subject = new Subject(); + Path pubkeyFile = srcTestResourcePath("org/apache/karaf/jaas/modules/ldap/ldaptest.admin.id_rsa"); + module.initialize(subject, new NamePubkeyCallbackHandler("imnothere", pubkeyFile), null, options); + + assertEquals("Precondition", 0, subject.getPrincipals().size()); + assertFalse(module.login()); + } + + private Path srcTestResourcePath(String relativePath) throws IOException { + String basedir = System.getProperty("basedir"); + if (basedir == null) { + basedir = new File(".").getCanonicalPath(); + } + Path pubkeyFile = Paths.get(basedir, "/src/test/resources/", relativePath); + return pubkeyFile; + } + + protected Properties ldapLoginModuleOptions() throws IOException { + String basedir = System.getProperty("basedir"); + if (basedir == null) { + basedir = new File(".").getCanonicalPath(); + } + File file = new File(basedir + "/target/test-classes/" + LDAP_PROPERTIES_FILE); + return new Properties(file); + } + +} diff --git a/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/example.com_pubkey.ldif b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/example.com_pubkey.ldif new file mode 100644 index 0000000..0cb87f5 --- /dev/null +++ b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/example.com_pubkey.ldif @@ -0,0 +1,68 @@ +## --------------------------------------------------------------------------- +## 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. +## --------------------------------------------------------------------------- + +dn: dc=example,dc=com +objectclass: top +objectclass: domain +dc: example + +dn: ou=people,dc=example,dc=com +objectClass: top +objectClass: organizationalUnit +ou: people + +dn: ou=groups,dc=example,dc=com +objectClass: top +objectClass: organizationalUnit +ou: groups + +dn: cn=admin,ou=groups,dc=example,dc=com +objectClass: top +objectClass: groupOfNames +cn: admin +description: cn=admin,ou=groups,dc=example,dc=com +member: cn=admin,ou=people,dc=example,dc=com + +dn: cn=admin,ou=people,dc=example,dc=com +objectClass: top +objectClass: inetOrgPerson +objectClass: person +objectClass: organizationalPerson +objectClass: extensibleObject +cn: admin +sn: admin +uid: admin +userPassword: admin123 +publicKey:: QUFBQUIzTnphQzF5YzJFQUFBQURBUUFCQUFBQWdRQzdRVi9MRFJSd29IL0xuV1NY + eDkxa0RZMEdvTW5uYUNpR3BGOTh6Y0JUSDY5dGxNdlBFRDNDYzd0eGRXVlp2UjFKSTIyeERwYmU + 1QTA1MzFaOFd0MTArSFZBZWorc2QwU0haVmJNYThrQ0NlYzBVQWlBZERuakd6NWNWTG5USCtwVl + FPVWk1cFUxalMva3N5dkdGcG44M29hVlgzRC9FLzg2TkN1aXRTRDkzUT09 + +dn: cn=cheese,ou=people,dc=example,dc=com +objectClass: top +objectClass: inetOrgPerson +objectClass: person +objectClass: organizationalPerson +objectClass: extensibleObject +cn: cheese +sn: cheese +uid: cheese +userPassword: foodie +publicKey:: QUFBQUIzTnphQzF5YzJFQUFBQURBUUFCQUFBQWdRQ3pFbjhETUF3ZzJodXdhSDc3 + RVExZGdaMlQ5b2ltU0p5Lzc3Y1E0OFRkOVh1UGtTV3BUN0RZNWhJTDYreDVrVnRHSWhjdGxQZlc + xQTRmM3A4M216bjl0d1hkVFplSXMvUFk2UXdwWTQ3WDdzRDZHRElZZ21kMXh0ZjNwbms3Tkpna3 + poallZTXhQVEdVWTFDQTVLMWhMMVlQMmMyS2JPTnVkTjFhZmhuSW01dz09 diff --git a/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldap_pubkey.properties b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldap_pubkey.properties new file mode 100644 index 0000000..5e1a707 --- /dev/null +++ b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldap_pubkey.properties @@ -0,0 +1,37 @@ +################################################################################ +# +# 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. +# +################################################################################ + +debug=true +connection.url=ldap://127.0.0.1:portno +connection.username=uid=admin,ou=system +connection.password=secret +connection.protocol= +authentication=simple + +user.base.dn=ou=people,dc=example,dc=com +user.filter=(uid=%u) +user.search.subtree=true +user.pubkey.attribute=publicKey + +role.base.dn=ou=groups,dc=example,dc=com +role.name.attribute=cn +role.filter=(member=%fqdn) +role.search.subtree=true + +initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory diff --git a/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.admin.id_rsa b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.admin.id_rsa new file mode 100644 index 0000000..dc79845 --- /dev/null +++ b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.admin.id_rsa @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXAIBAAKBgQC7QV/LDRRwoH/LnWSXx91kDY0GoMnnaCiGpF98zcBTH69tlMvP +ED3Cc7txdWVZvR1JI22xDpbe5A0531Z8Wt10+HVAej+sd0SHZVbMa8kCCec0UAiA +dDnjGz5cVLnTH+pVQOUi5pU1jS/ksyvGFpn83oaVX3D/E/86NCuitSD93QIDAQAB +AoGBAKye3G7z1NbqrkSHCJd/ENFOSKZGjTn84/cTCk+j4NsAB5lOJP/yKezbAX3b +Sh4K3zdwKIujNmOs+aBTCYhDv4eiZxnIV5Vej1d9zbzP7pVCUmd6qiHcW90sZsLt +zVKkkv0OdlKEOTA5r9Ar5xx+Y61Vf/fNxxABACrCzfgsuyZFAkEA8b9+09GYKJp8 +6XY4wuguDiICODYPbFd7gqYm6p83zBTJXy/OfuX2LNfUyUja/XumsdPhPAHbLdc5 +H8vGHpnAfwJBAMZLdzxp+pGnYkgGEyaF0CLocEnoxSprhw6tY8o7SIrqyjPsltTu +8iR9u53ntp7cjG7nzycULpkduslCjDcOE6MCQEi2UG9lm16bGPcfl/MH4tJdaE1/ +9SOhLIUfdJUdTqsTlX4L4xBIGsNiJ55jS3rytjDGifiClmozUfs+T1jk5gECQAe0 +2R508/MDMVOhQM9Hdg5VQD/vFvKOGUKdxHoQkcIsW81mzXnbC3gVltwNLFDCO4b3 +5Vocc68ps5+swWxGVMcCQCLukMpOgK8HGnMTs3F5+R+7AnhtnX6EAKuEzxJoXZYM ++QegJZvWii7GwJDuSvU0IQKqKZhdfp/TaxztDPEmMMg= +-----END RSA PRIVATE KEY----- diff --git a/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.admin.id_rsa.pub b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.admin.id_rsa.pub new file mode 100644 index 0000000..be1665e --- /dev/null +++ b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.admin.id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC7QV/LDRRwoH/LnWSXx91kDY0GoMnnaCiGpF98zcBTH69tlMvPED3Cc7txdWVZvR1JI22xDpbe5A0531Z8Wt10+HVAej+sd0SHZVbMa8kCCec0UAiAdDnjGz5cVLnTH+pVQOUi5pU1jS/ksyvGFpn83oaVX3D/E/86NCuitSD93Q== LdapUnitTestKey diff --git a/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.cheese.id_rsa b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.cheese.id_rsa new file mode 100644 index 0000000..1650c37 --- /dev/null +++ b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.cheese.id_rsa @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQCzEn8DMAwg2huwaH77EQ1dgZ2T9oimSJy/77cQ48Td9XuPkSWp +T7DY5hIL6+x5kVtGIhctlPfW1A4f3p83mzn9twXdTZeIs/PY6QwpY47X7sD6GDIY +gmd1xtf3pnk7NJgkzhjYYMxPTGUY1CA5K1hL1YP2c2KbONudN1afhnIm5wIDAQAB +AoGAC7xPlJ7mfJSusd33TG7uqE0hTZwfkn45v55vKe0zbrRy15LUnAb7+QsC7cMV +aVYsXClJyZP0tiCJmG8XkiZbI3h1qGkD4Z8h4M9GoxVZvrYV6UnEX8C7oeaPe0ao +xe1hk7RzGNFREfM9yHt++Zq0geAZDSwN6XrZDL+qpWLap7kCQQDXjZdleBiNxB6q +dq7dJEgYUjc6fjs2x7BhaeCBgcRZWIpnqe4VtNGmT3wJHOKpxCj3w8AppRck4Ju6 +LS69b0c1AkEA1Kx+fZe9sYLg+l69lJy+slXDx32Ite9dUNgETRPNCO9Pp6btXjs8 +x+VZe7ZSxSnEiUvJ+x4K+gVmp3UTPIWNKwJBALiWPgHcuFoeiow7mj8x5LM/JKBo +nNiqZHbnLiR5NeW1FsDzGjloYOhkxLkhDVGH8/VIonSHNayU04a5Tn9WnckCQQCc +4YGNc9nikAEVr715Wwbw1oNNLTUjwCa99Bt4IBsndCD2MxT2Zgw4CN8xexUji+QG +w3mDXG4McN+At8Qw6PNxAkBapr5ojSjeoaWS4+nmMLfxlLuCpo4WbjCr9aFVSCy1 +PpQtHVREUgoq4Vo61BNleQ/kSrQXBIgZwQtwGXifUWNM +-----END RSA PRIVATE KEY----- diff --git a/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.cheese.id_rsa.pub b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.cheese.id_rsa.pub new file mode 100644 index 0000000..17ae7b7 --- /dev/null +++ b/jaas/modules/src/test/resources/org/apache/karaf/jaas/modules/ldap/ldaptest.cheese.id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCzEn8DMAwg2huwaH77EQ1dgZ2T9oimSJy/77cQ48Td9XuPkSWpT7DY5hIL6+x5kVtGIhctlPfW1A4f3p83mzn9twXdTZeIs/PY6QwpY47X7sD6GDIYgmd1xtf3pnk7NJgkzhjYYMxPTGUY1CA5K1hL1YP2c2KbONudN1afhnIm5w== LdapUnitTestKey -- To stop receiving notification emails like this one, please contact ['"commits@karaf.apache.org" <commits@karaf.apache.org>'].