You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ja...@apache.org on 2012/04/12 10:14:21 UTC
svn commit: r1325151 [2/2] - in /ace/trunk: ./ ace-authentication-api/
ace-authentication-api/src/ ace-authentication-api/src/main/
ace-authentication-api/src/main/java/
ace-authentication-api/src/main/java/org/
ace-authentication-api/src/main/java/org...
Added: ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessorTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessorTest.java?rev=1325151&view=auto
==============================================================================
--- ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessorTest.java (added)
+++ ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessorTest.java Thu Apr 12 08:14:17 2012
@@ -0,0 +1,415 @@
+/*
+ * 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.ace.authenticationprocessor.clientcert;
+
+import static org.apache.ace.authenticationprocessor.clientcert.ClientCertAuthenticationProcessor.ATTRIBUTE_CIPHER_SUITE;
+import static org.apache.ace.authenticationprocessor.clientcert.ClientCertAuthenticationProcessor.ATTRIBUTE_X509_CERTIFICATE;
+import static org.apache.ace.authenticationprocessor.clientcert.ClientCertAuthenticationProcessor.PROPERTY_KEY_PUBLICKEY;
+import static org.apache.ace.authenticationprocessor.clientcert.ClientCertAuthenticationProcessor.PROPERTY_KEY_USERNAME;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Properties;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Test cases for {@link ClientCertAuthenticationProcessor}.
+ */
+public class ClientCertAuthenticationProcessorTest {
+
+ private static MemoryKeyStore m_keystore;
+
+ private LogService m_log;
+ private UserAdmin m_userAdmin;
+ private HttpServletRequest m_servletRequest;
+
+ /**
+ * @return the day after tomorrow, never <code>null</code>.
+ */
+ private static Date dayAfterTomorrow() {
+ Calendar cal = getToday();
+ cal.add(Calendar.DAY_OF_MONTH, +2);
+ return cal.getTime();
+ }
+
+ /**
+ * @return the day before yesterday, never <code>null</code>.
+ */
+ private static Date dayBeforeYesterday() {
+ Calendar cal = getToday();
+ cal.add(Calendar.DAY_OF_MONTH, -2);
+ return cal.getTime();
+ }
+
+ /**
+ * @return today as date, without time component, never <code>null</code>.
+ */
+ private static Calendar getToday() {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.HOUR_OF_DAY, 12);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ return cal;
+ }
+
+ /**
+ * @return the date of tomorrow, never <code>null</code>.
+ */
+ private static Date tomorrow() {
+ Calendar cal = getToday();
+ cal.add(Calendar.DAY_OF_MONTH, +1);
+ return cal.getTime();
+ }
+
+ /**
+ * @return the date of yesterday, never <code>null</code>.
+ */
+ private static Date yesterday() {
+ Calendar cal = getToday();
+ cal.add(Calendar.DAY_OF_MONTH, -1);
+ return cal.getTime();
+ }
+
+ /**
+ * Creates an in-memory keystore for this test case.
+ */
+ @BeforeClass
+ public static void init() {
+ m_keystore = new MemoryKeyStore("cn=testCA", dayBeforeYesterday(), dayAfterTomorrow());
+ }
+
+ /**
+ * Set up for each individual test.
+ */
+ @Before
+ public void setUp() {
+ m_log = mock(LogService.class);
+
+ m_userAdmin = mock(UserAdmin.class);
+ m_servletRequest = mock(HttpServletRequest.class);
+
+ when(m_servletRequest.getAuthType()).thenReturn(HttpServletRequest.CLIENT_CERT_AUTH);
+ when(m_servletRequest.getAttribute(ATTRIBUTE_CIPHER_SUITE)).thenReturn("bogus-cipher-suite");
+ }
+
+ /**
+ * Tests that a null certificate chain will yield null.
+ */
+ @Test
+ public void testAuthenticateNoCertificateChainYieldsNull() {
+ User result = createAuthorizationProcessor().authenticate(m_userAdmin, m_servletRequest);
+ assertNull(result);
+ }
+
+ /**
+ * Tests that an empty certificate chain will yield null.
+ */
+ @Test
+ public void testAuthenticateEmptyCertificateChainYieldsNull() {
+ when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(new X509Certificate[0]);
+
+ User result = createAuthorizationProcessor().authenticate(m_userAdmin, m_servletRequest);
+ assertNull(result);
+ }
+
+ /**
+ * Tests that authenticating a known user with an invalid (expired) certificate will yield null.
+ */
+ @Test
+ public void testAuthenticateKnownUserWithExpiredCertificateYieldsNull() {
+ X509Certificate[] certificateChain = createExpiredCertificateChain("bob");
+ PublicKey publickey = certificateChain[0].getPublicKey();
+
+ when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(certificateChain);
+
+ User user = mock(User.class);
+ when(user.getName()).thenReturn("bob");
+ when(user.hasCredential(eq("publickey"), eq(publickey.getEncoded()))).thenReturn(Boolean.TRUE);
+
+ when(m_userAdmin.getUser(eq("username"), eq("bob"))).thenReturn(user);
+
+ User result = createAuthorizationProcessor().authenticate(m_userAdmin, m_servletRequest);
+ assertNull(result);
+ }
+
+ /**
+ * Tests that authenticating a known user with an invalid (not valid) certificate will yield null.
+ */
+ @Test
+ public void testAuthenticateKnownUserWithNotValidCertificateYieldsNull() {
+ X509Certificate[] certificateChain = createExpiredCertificateChain("bob");
+ PublicKey publickey = certificateChain[0].getPublicKey();
+
+ when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(
+ createNotValidCertificateChain("bob"));
+
+ User user = mock(User.class);
+ when(user.getName()).thenReturn("bob");
+ when(user.hasCredential(eq("publickey"), eq(publickey.getEncoded()))).thenReturn(Boolean.TRUE);
+
+ when(m_userAdmin.getUser(eq("username"), eq("bob"))).thenReturn(user);
+
+ User result = createAuthorizationProcessor().authenticate(m_userAdmin, m_servletRequest);
+ assertNull(result);
+ }
+
+ /**
+ * Tests that authenticating a known user with a valid certificate will not yield null.
+ */
+ @Test
+ public void testAuthenticateKnownUserYieldsValidResult() {
+ X509Certificate[] certChain = createValidCertificateChain("bob");
+ PublicKey publicKey = certChain[0].getPublicKey();
+
+ when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(certChain);
+
+ User user = mock(User.class);
+ when(user.getName()).thenReturn("bob");
+ when(user.hasCredential(eq("publickey"), eq(publicKey.getEncoded()))).thenReturn(Boolean.TRUE);
+
+ when(m_userAdmin.getUser(eq("username"), eq("bob"))).thenReturn(user);
+
+ User result = createAuthorizationProcessor().authenticate(m_userAdmin, m_servletRequest);
+ assertNotNull(result);
+
+ assertEquals("bob", user.getName());
+ }
+
+ /**
+ * Tests that a missing cipher suite header will the authenticate method to yield null.
+ */
+ @Test
+ public void testAuthenticateMissingCipherSuiteHeaderYieldsNull() {
+ when(m_servletRequest.getAttribute(ATTRIBUTE_CIPHER_SUITE)).thenReturn(null);
+ when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(createValidCertificateChain("bob"));
+
+ User result = createAuthorizationProcessor().authenticate(m_userAdmin, m_servletRequest);
+ assertNull(result);
+ }
+
+ /**
+ * Tests that a class cast exception is thrown for invalid context when calling authenticate.
+ */
+ @Test(expected = ClassCastException.class)
+ public void testAuthenticateThrowsClassCastForInvalidContext() {
+ createAuthorizationProcessor().authenticate(m_userAdmin, new Object());
+ }
+
+ /**
+ * Tests that an unknown user will yield null.
+ */
+ @Test
+ public void testAuthenticateUnknownUserYieldsNull() {
+ when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(createValidCertificateChain("bob"));
+
+ User result = createAuthorizationProcessor().authenticate(m_userAdmin, m_servletRequest);
+ assertNull(result);
+ }
+
+ /**
+ * Tests that canHandle yields false for any object other than {@link HttpServletRequest}.
+ */
+ @Test
+ public void testCanHandleDoesAcceptServletRequest() {
+ assertTrue(createAuthorizationProcessor().canHandle(mock(HttpServletRequest.class)));
+ }
+
+ /**
+ * Tests that canHandle throws an {@link IllegalArgumentException} for an empty context.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testCanHandleDoesNotAcceptEmptyArray() {
+ createAuthorizationProcessor().canHandle(new Object[0]);
+ }
+
+ /**
+ * Tests that canHandle throws an {@link IllegalArgumentException} for a null context.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testCanHandleDoesNotAcceptNull() {
+ createAuthorizationProcessor().canHandle((Object[]) null);
+ }
+
+ /**
+ * Tests that canHandle yields false for any object other than {@link HttpServletRequest}.
+ */
+ @Test
+ public void testCanHandleDoesNotAcceptUnhandledContext() {
+ assertFalse(createAuthorizationProcessor().canHandle(new Object()));
+ }
+
+ /**
+ * Tests that updated does not throw an exception for a correct configuration.
+ */
+ @Test
+ public void testUpdatedDoesAcceptCorrectProperties() throws ConfigurationException {
+ final String keyUsername = "foo";
+ final String keyPublicKey = "bar";
+
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_USERNAME, keyUsername);
+ props.put(PROPERTY_KEY_PUBLICKEY, keyPublicKey);
+
+ ClientCertAuthenticationProcessor processor = createAuthorizationProcessor();
+
+ processor.updated(props);
+
+ X509Certificate[] certificateChain = createValidCertificateChain("bob");
+ PublicKey publickey = certificateChain[0].getPublicKey();
+
+ // Test whether we can use the new properties...
+ when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(certificateChain);
+
+ User user = mock(User.class);
+ when(user.getName()).thenReturn("bob");
+ when(user.hasCredential(eq(keyPublicKey), eq(publickey.getEncoded()))).thenReturn(Boolean.TRUE);
+
+ when(m_userAdmin.getUser(eq(keyUsername), eq("bob"))).thenReturn(user);
+
+ User result = processor.authenticate(m_userAdmin, m_servletRequest);
+ assertNotNull(result);
+
+ assertEquals("bob", user.getName());
+ }
+
+ /**
+ * Tests that updated throws an exception for missing "key.password" property.
+ */
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedDoesNotAcceptEmptyKeyPassword() throws ConfigurationException {
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_USERNAME, "foo");
+ props.put(PROPERTY_KEY_PUBLICKEY, "");
+
+ createAuthorizationProcessor().updated(props);
+ }
+
+ /**
+ * Tests that updated throws an exception for missing "key.username" property.
+ */
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedDoesNotAcceptEmptyKeyUsername() throws ConfigurationException {
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_USERNAME, "");
+ props.put(PROPERTY_KEY_PUBLICKEY, "foo");
+
+ createAuthorizationProcessor().updated(props);
+ }
+
+ /**
+ * Tests that updated throws an exception for missing "key.password" property.
+ */
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedDoesNotAcceptMissingKeyPassword() throws ConfigurationException {
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_USERNAME, "foo");
+
+ createAuthorizationProcessor().updated(props);
+ }
+
+ /**
+ * Tests that updated throws an exception for missing "key.username" property.
+ */
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedDoesNotAcceptMissingKeyUsername() throws ConfigurationException {
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_PUBLICKEY, "foo");
+
+ createAuthorizationProcessor().updated(props);
+ }
+
+ /**
+ * Creates a new {@link ClientCertAuthenticationProcessor} instance.
+ *
+ * @return a new authentication processor instance, never <code>null</code>.
+ */
+ private ClientCertAuthenticationProcessor createAuthorizationProcessor() {
+ return new ClientCertAuthenticationProcessor(m_log);
+ }
+
+ /**
+ * Creates a new certificate.
+ *
+ * @param name the (common) name of the certificate;
+ * @param notBefore the date after which the certificate is valid;
+ * @param notAfter the date until the certificate is valid.
+ * @return a new {@link X509Certificate}, never <code>null</code>.
+ */
+ private X509Certificate createCertificate(String name, final Date notBefore, final Date notAfter) {
+ KeyPair keypair = m_keystore.generateKeyPair();
+ return m_keystore.createCertificate("alias", "cn=" + name, notBefore, notAfter, keypair.getPublic());
+ }
+
+ /**
+ * Creates a new (valid) certificate valid from yesterday until tomorrow.
+ *
+ * @param name the (common) name of the certificate;
+ * @return a new {@link X509Certificate}, never <code>null</code>.
+ */
+ private X509Certificate[] createValidCertificateChain(String name) {
+ X509Certificate[] result = new X509Certificate[1];
+ result[0] = createCertificate(name, yesterday(), tomorrow());
+ return result;
+ }
+
+ /**
+ * Creates a new (expired) certificate valid from two days ago until yesterday.
+ *
+ * @param name the (common) name of the certificate;
+ * @return a new {@link X509Certificate}, never <code>null</code>.
+ */
+ private X509Certificate[] createExpiredCertificateChain(String name) {
+ X509Certificate[] result = new X509Certificate[1];
+ result[0] = createCertificate(name, dayBeforeYesterday(), yesterday());
+ return result;
+ }
+
+ /**
+ * Creates a new (not yet valid) certificate valid from tomorrow until the day after tomorrow.
+ *
+ * @param name the (common) name of the certificate;
+ * @return a new {@link X509Certificate}, never <code>null</code>.
+ */
+ private X509Certificate[] createNotValidCertificateChain(String name) {
+ X509Certificate[] result = new X509Certificate[1];
+ result[0] = createCertificate(name, tomorrow(), dayAfterTomorrow());
+ return result;
+ }
+}
Propchange: ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/ClientCertAuthenticationProcessorTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/MemoryKeyStore.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/MemoryKeyStore.java?rev=1325151&view=auto
==============================================================================
--- ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/MemoryKeyStore.java (added)
+++ ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/MemoryKeyStore.java Thu Apr 12 08:14:17 2012
@@ -0,0 +1,117 @@
+/*
+ * 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.ace.authenticationprocessor.clientcert;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.bouncycastle.x509.X509V1CertificateGenerator;
+
+/**
+ * Provides a memory-only certificate keystore.
+ */
+final class MemoryKeyStore {
+ private static final String SIGNATURE_ALGORITHM = "SHA1withRSA"; // MD5withRSA
+
+ private final X509V1CertificateGenerator m_certGen = new X509V1CertificateGenerator();
+ private final KeyPair m_caKey;
+ private final X509Certificate m_rootCert;
+ private int m_serial = 0;
+
+ private final KeyPairGenerator m_generator;
+
+ /**
+ * Creates a new {@link MemoryKeyStore} instance.
+ */
+ public MemoryKeyStore(String name, Date notBefore, Date notAfter) {
+ try {
+ m_generator = KeyPairGenerator.getInstance("RSA");
+ m_generator.initialize(1024);
+
+ m_caKey = generateKeyPair();
+
+ m_rootCert = generateRootCertificate(name, notBefore, notAfter);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Generates a new 512-bit keypair.
+ *
+ * @return a new {@link KeyPair}, never <code>null</code>.
+ */
+ public final KeyPair generateKeyPair() {
+ try {
+ return m_generator.generateKeyPair();
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @throws IllegalStateException if an internal exception occurs.
+ * @throws IllegalArgumentException if the alias already exists.
+ */
+ public X509Certificate createCertificate(String alias, String name, Date before, Date after, PublicKey key)
+ throws IllegalStateException, IllegalArgumentException {
+ try {
+ m_certGen.reset();
+ m_certGen.setSerialNumber(BigInteger.valueOf(++m_serial));
+ m_certGen.setIssuerDN(m_rootCert.getIssuerX500Principal());
+ m_certGen.setNotBefore(before);
+ m_certGen.setNotAfter(after);
+ m_certGen.setSubjectDN(new X500Principal(name));
+ m_certGen.setPublicKey(key);
+ m_certGen.setSignatureAlgorithm(SIGNATURE_ALGORITHM);
+
+ X509Certificate cert = m_certGen.generate(m_caKey.getPrivate());
+
+ return cert;
+ }
+ catch (IllegalArgumentException e) {
+ throw e;
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private X509Certificate generateRootCertificate(String name, Date notBefore, Date notAfter) throws Exception {
+ m_certGen.reset();
+ m_certGen.setSerialNumber(BigInteger.valueOf(1));
+ m_certGen.setIssuerDN(new X500Principal(name));
+ m_certGen.setNotBefore(notBefore);
+ m_certGen.setNotAfter(notAfter);
+ m_certGen.setSubjectDN(new X500Principal(name));
+ m_certGen.setPublicKey(m_caKey.getPublic());
+ m_certGen.setSignatureAlgorithm(SIGNATURE_ALGORITHM);
+
+ return m_certGen.generate(m_caKey.getPrivate());
+ }
+}
Propchange: ace/trunk/ace-authenticationprocessor-clientcert/src/test/java/org/apache/ace/authenticationprocessor/clientcert/MemoryKeyStore.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: ace/trunk/ace-authenticationprocessor-password/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Thu Apr 12 08:14:17 2012
@@ -0,0 +1,13 @@
+.metadata
+.settings
+bin
+.classpath
+.project
+target
+*.ipr
+*.iws
+*.iml
+store
+test-output
+velocity.log
+
Added: ace/trunk/ace-authenticationprocessor-password/pom.xml
URL: http://svn.apache.org/viewvc/ace/trunk/ace-authenticationprocessor-password/pom.xml?rev=1325151&view=auto
==============================================================================
--- ace/trunk/ace-authenticationprocessor-password/pom.xml (added)
+++ ace/trunk/ace-authenticationprocessor-password/pom.xml Thu Apr 12 08:14:17 2012
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>ace-pom</artifactId>
+ <version>0.8.1-SNAPSHOT</version>
+ <relativePath>../pom/pom.xml</relativePath>
+ </parent>
+
+ <artifactId>org.apache.ace.authenticationprocessor.password</artifactId>
+
+ <name>Apache ACE :: Authentication :: Password</name>
+ <description>Provides an authentication processor that performs simple username/password authentication.</description>
+ <packaging>bundle</packaging>
+
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/ace/trunk/ace-authenticationprocessor-password</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/ace/trunk/ace-authenticationprocessor-password</developerConnection>
+ <url>http://svn.apache.org/repos/asf/ace/trunk/ace-authenticationprocessor-password</url>
+ </scm>
+
+ <properties>
+ <import.package>
+ org.apache.ace.authentication.api,
+ org.apache.felix.dm,
+ org.osgi.framework,
+ org.osgi.service.cm,
+ org.osgi.service.useradmin
+ </import.package>
+ <private.package>
+ org.apache.ace.authenticationprocessor.password,
+ org.apache.commons.codec,
+ org.apache.commons.codec.binary,
+ org.apache.commons.codec.digest
+ </private.package>
+ <bundle.activator>org.apache.ace.authenticationprocessor.password.Activator</bundle.activator>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authentication.api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.dependencymanager</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ </dependency>
+ </dependencies>
+</project>
Propchange: ace/trunk/ace-authenticationprocessor-password/pom.xml
------------------------------------------------------------------------------
svn:eol-style = native
Added: ace/trunk/ace-authenticationprocessor-password/src/main/java/org/apache/ace/authenticationprocessor/password/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-authenticationprocessor-password/src/main/java/org/apache/ace/authenticationprocessor/password/Activator.java?rev=1325151&view=auto
==============================================================================
--- ace/trunk/ace-authenticationprocessor-password/src/main/java/org/apache/ace/authenticationprocessor/password/Activator.java (added)
+++ ace/trunk/ace-authenticationprocessor-password/src/main/java/org/apache/ace/authenticationprocessor/password/Activator.java Thu Apr 12 08:14:17 2012
@@ -0,0 +1,58 @@
+/*
+ * 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.ace.authenticationprocessor.password;
+
+import java.util.Properties;
+
+import org.apache.ace.authentication.api.AuthenticationProcessor;
+import org.apache.felix.dm.DependencyActivatorBase;
+import org.apache.felix.dm.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ManagedService;
+
+/**
+ * Provides a bundle activator for the {@link BasicHttpAuthenticationProcessor}.
+ */
+public class Activator extends DependencyActivatorBase {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void init(BundleContext context, DependencyManager manager) throws Exception {
+ Properties props = new Properties();
+ props.put(Constants.SERVICE_PID, PasswordAuthenticationProcessor.PID);
+
+// @formatter:off
+ manager.add(createComponent()
+ .setInterface(new String[]{ AuthenticationProcessor.class.getName(), ManagedService.class.getName() }, props)
+ .setImplementation(new PasswordAuthenticationProcessor())
+ );
+// @formatter:on
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+ // Nop
+ }
+}
Propchange: ace/trunk/ace-authenticationprocessor-password/src/main/java/org/apache/ace/authenticationprocessor/password/Activator.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: ace/trunk/ace-authenticationprocessor-password/src/main/java/org/apache/ace/authenticationprocessor/password/PasswordAuthenticationProcessor.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-authenticationprocessor-password/src/main/java/org/apache/ace/authenticationprocessor/password/PasswordAuthenticationProcessor.java?rev=1325151&view=auto
==============================================================================
--- ace/trunk/ace-authenticationprocessor-password/src/main/java/org/apache/ace/authenticationprocessor/password/PasswordAuthenticationProcessor.java (added)
+++ ace/trunk/ace-authenticationprocessor-password/src/main/java/org/apache/ace/authenticationprocessor/password/PasswordAuthenticationProcessor.java Thu Apr 12 08:14:17 2012
@@ -0,0 +1,192 @@
+/*
+ * 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.ace.authenticationprocessor.password;
+
+import java.util.Dictionary;
+
+import org.apache.ace.authentication.api.AuthenticationProcessor;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Provides an {@link AuthenticationProcessor} that implements simple username/password-based
+ * authentication and looks up a user in the {@link UserAdmin} service using (by default, can be
+ * configured otherwise) the keys "username" and "password". It also supports (MD5, SHA1, SHA256,
+ * SHA384 or SHA512) hashed passwords.
+ */
+public class PasswordAuthenticationProcessor implements AuthenticationProcessor, ManagedService {
+
+ public static final String PID = "org.apache.ace.authenticationprocessor.password";
+
+ static final String PROPERTY_KEY_USERNAME = "key.username";
+ static final String PROPERTY_KEY_PASSWORD = "key.password";
+ static final String PROPERTY_PASSWORD_HASHMETHOD = "password.hashmethod";
+
+ private static final String DEFAULT_PROPERTY_KEY_USERNAME = "username";
+ private static final String DEFAULT_PROPERTY_KEY_PASSWORD = "password";
+ private static final String DEFAULT_PROPERTY_PASSWORD_HASHMETHOD = "none";
+
+ private volatile String m_keyUsername = DEFAULT_PROPERTY_KEY_USERNAME;
+ private volatile String m_keyPassword = DEFAULT_PROPERTY_KEY_PASSWORD;
+ private volatile String m_passwordHashMethod = DEFAULT_PROPERTY_PASSWORD_HASHMETHOD;
+
+ /**
+ * {@inheritDoc}
+ */
+ public User authenticate(UserAdmin userAdmin, Object... context) {
+ final String username = (String) context[0];
+ final Object password = context[1];
+
+ if (username == null || "".equals(username.trim())) {
+ // Invalid/no username given!
+ return null;
+ }
+
+ if (password == null) {
+ // Invalid/no password given!
+ return null;
+ }
+
+ User user = userAdmin.getUser(m_keyUsername, username);
+ if (user == null || !user.hasCredential(m_keyPassword, hashPassword(password))) {
+ // Invalid/unknown user!
+ return null;
+ }
+
+ return user;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean canHandle(Object... context) {
+ if (context == null || context.length == 0) {
+ throw new IllegalArgumentException("Invalid context!");
+ }
+
+ if (context.length != 2) {
+ return false;
+ }
+
+ if (!(context[0] instanceof String)) {
+ return false;
+ }
+
+ return ((context[1] instanceof String) || (context[1] instanceof byte[]));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void updated(Dictionary dictionary) throws ConfigurationException {
+ if (dictionary != null) {
+ String keyUsername = (String) dictionary.get(PROPERTY_KEY_USERNAME);
+ if (keyUsername == null || "".equals(keyUsername.trim())) {
+ throw new ConfigurationException(PROPERTY_KEY_USERNAME, "Missing property");
+ }
+
+ String keyPassword = (String) dictionary.get(PROPERTY_KEY_PASSWORD);
+ if (keyPassword == null || "".equals(keyPassword.trim())) {
+ throw new ConfigurationException(PROPERTY_KEY_PASSWORD, "Missing property");
+ }
+
+ String passwordHashType = (String) dictionary.get(PROPERTY_PASSWORD_HASHMETHOD);
+ if (passwordHashType == null || "".equals(passwordHashType.trim())) {
+ throw new ConfigurationException(PROPERTY_PASSWORD_HASHMETHOD, "Missing property");
+ }
+ if (!isValidHashMethod(passwordHashType)) {
+ throw new ConfigurationException(PROPERTY_PASSWORD_HASHMETHOD, "Invalid hash method!");
+ }
+
+ m_keyUsername = keyUsername;
+ m_keyPassword = keyPassword;
+ m_passwordHashMethod = passwordHashType;
+ }
+ else {
+ m_keyUsername = DEFAULT_PROPERTY_KEY_USERNAME;
+ m_keyPassword = DEFAULT_PROPERTY_KEY_PASSWORD;
+ m_passwordHashMethod = DEFAULT_PROPERTY_PASSWORD_HASHMETHOD;
+ }
+ }
+
+ /**
+ * Hashes a given password using the current set hash method.
+ *
+ * @param password the password to hash, should not be <code>null</code>.
+ * @return the hashed password, never <code>null</code>.
+ */
+ private Object hashPassword(Object password) {
+ if ("none".equalsIgnoreCase(m_passwordHashMethod)) {
+ // Very special ROT26 hashing method...
+ return password;
+ }
+
+ if ("md5".equalsIgnoreCase(m_passwordHashMethod)) {
+ if (password instanceof byte[]) {
+ return DigestUtils.md5((byte[]) password);
+ }
+ return DigestUtils.md5((String) password);
+ }
+ if ("sha1".equalsIgnoreCase(m_passwordHashMethod)) {
+ if (password instanceof byte[]) {
+ return DigestUtils.sha((byte[]) password);
+ }
+ return DigestUtils.sha((String) password);
+ }
+ if ("sha256".equalsIgnoreCase(m_passwordHashMethod)) {
+ if (password instanceof byte[]) {
+ return DigestUtils.sha256((byte[]) password);
+ }
+ return DigestUtils.sha256((String) password);
+ }
+ if ("sha384".equalsIgnoreCase(m_passwordHashMethod)) {
+ if (password instanceof byte[]) {
+ return DigestUtils.sha384((byte[]) password);
+ }
+ return DigestUtils.sha384((String) password);
+ }
+ if ("sha512".equalsIgnoreCase(m_passwordHashMethod)) {
+ if (password instanceof byte[]) {
+ return DigestUtils.sha512((byte[]) password);
+ }
+ return DigestUtils.sha512((String) password);
+ }
+ return password;
+ }
+
+ /**
+ * Determines whether the given hash method is valid.
+ *
+ * @param hashMethod the hash method to test, can be <code>null</code> or empty.
+ * @return <code>true</code> if the given hash method is valid/supported, <code>false</code> otherwise.
+ */
+ private boolean isValidHashMethod(String hashMethod) {
+// @formatter:off
+ return "none".equalsIgnoreCase(hashMethod)
+ || "md5".equalsIgnoreCase(hashMethod)
+ || "sha1".equalsIgnoreCase(hashMethod)
+ || "sha256".equalsIgnoreCase(hashMethod)
+ || "sha384".equalsIgnoreCase(hashMethod)
+ || "sha512".equalsIgnoreCase(hashMethod);
+// @formatter:on
+ }
+}
Propchange: ace/trunk/ace-authenticationprocessor-password/src/main/java/org/apache/ace/authenticationprocessor/password/PasswordAuthenticationProcessor.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: ace/trunk/ace-authenticationprocessor-password/src/test/java/org/apache/ace/authenticationprocessor/password/PasswordAuthenticationProcessorTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-authenticationprocessor-password/src/test/java/org/apache/ace/authenticationprocessor/password/PasswordAuthenticationProcessorTest.java?rev=1325151&view=auto
==============================================================================
--- ace/trunk/ace-authenticationprocessor-password/src/test/java/org/apache/ace/authenticationprocessor/password/PasswordAuthenticationProcessorTest.java (added)
+++ ace/trunk/ace-authenticationprocessor-password/src/test/java/org/apache/ace/authenticationprocessor/password/PasswordAuthenticationProcessorTest.java Thu Apr 12 08:14:17 2012
@@ -0,0 +1,290 @@
+/*
+ * 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.ace.authenticationprocessor.password;
+
+import static org.apache.ace.authenticationprocessor.password.PasswordAuthenticationProcessor.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Properties;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Test cases for {@link PasswordAuthenticationProcessor}.
+ */
+public class PasswordAuthenticationProcessorTest {
+
+ private UserAdmin m_userAdmin;
+
+ @Before
+ public void setUp() {
+ m_userAdmin = mock(UserAdmin.class);
+ }
+
+ /**
+ * Tests that authenticating with a empty username will yield null.
+ */
+ @Test
+ public void testAuthenticateEmptyUserNameYieldsNull() {
+ User result = new PasswordAuthenticationProcessor().authenticate(m_userAdmin, "", "secret");
+ assertNull(result);
+ }
+
+ /**
+ * Tests that authenticating a known user with an invalid password will yield null.
+ */
+ @Test
+ public void testAuthenticateKnownUserWithInvalidPasswordYieldsNull() {
+ User user = mock(User.class);
+ when(user.getName()).thenReturn("bob");
+ when(user.hasCredential(eq("password"), eq("otherSecret"))).thenReturn(Boolean.TRUE);
+
+ when(m_userAdmin.getUser(eq("username"), eq("bob"))).thenReturn(user);
+
+ User result = new PasswordAuthenticationProcessor().authenticate(m_userAdmin, "bob", "secret");
+ assertNull(result);
+ }
+
+ /**
+ * Tests that authenticating a known user with a correct password will not yield null.
+ */
+ @Test
+ public void testAuthenticateKnownUserYieldsValidResult() {
+ User user = mock(User.class);
+ when(user.getName()).thenReturn("bob");
+ when(user.hasCredential(eq("password"), eq("secret"))).thenReturn(Boolean.TRUE);
+
+ when(m_userAdmin.getUser(eq("username"), eq("bob"))).thenReturn(user);
+
+ User result = new PasswordAuthenticationProcessor().authenticate(m_userAdmin, "bob", "secret");
+ assertNotNull(result);
+
+ assertEquals("bob", user.getName());
+ }
+
+ /**
+ * Tests that authenticating with a null password will yield null.
+ */
+ @Test
+ public void testAuthenticateNullPasswordYieldsNull() {
+ User result = new PasswordAuthenticationProcessor().authenticate(m_userAdmin, "bob", null);
+ assertNull(result);
+ }
+
+ /**
+ * Tests that authenticating with a null username will yield null.
+ */
+ @Test
+ public void testAuthenticateNullUserNameYieldsNull() {
+ User result = new PasswordAuthenticationProcessor().authenticate(m_userAdmin, null, "secret");
+ assertNull(result);
+ }
+
+ /**
+ * Tests that a class cast exception is thrown for invalid context when calling authenticate.
+ */
+ @Test(expected = ClassCastException.class)
+ public void testAuthenticateThrowsClassCastForInvalidContext() {
+ new PasswordAuthenticationProcessor().authenticate(m_userAdmin, new Object(), "foo");
+ }
+
+ /**
+ * Tests that authenticating an unknown user will yield null.
+ */
+ @Test
+ public void testAuthenticateUnknownUserYieldsNull() {
+ User result = new PasswordAuthenticationProcessor().authenticate(m_userAdmin, "bob", "secret");
+ assertNull(result);
+ }
+
+ /**
+ * Tests that canHandle yields true for string and byte array.
+ */
+ @Test
+ public void testCanHandleDoesAcceptStringAndByteArray() {
+ assertTrue(new PasswordAuthenticationProcessor().canHandle("foo", "bar".getBytes()));
+ }
+
+ /**
+ * Tests that canHandle yields true for two strings.
+ */
+ @Test
+ public void testCanHandleDoesAcceptTwoStrings() {
+ assertTrue(new PasswordAuthenticationProcessor().canHandle("foo", "bar"));
+ }
+
+ /**
+ * Tests that canHandle throws an {@link IllegalArgumentException} for an empty context.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testCanHandleDoesNotAcceptEmptyArray() {
+ new PasswordAuthenticationProcessor().canHandle(new Object[0]);
+ }
+
+ /**
+ * Tests that canHandle throws an {@link IllegalArgumentException} for a null context.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testCanHandleDoesNotAcceptNull() {
+ new PasswordAuthenticationProcessor().canHandle((Object[]) null);
+ }
+
+ /**
+ * Tests that canHandle yields false for too few arguments.
+ */
+ @Test
+ public void testCanHandleDoesNotAcceptSingleArgument() {
+ assertFalse(new PasswordAuthenticationProcessor().canHandle(new Object()));
+ }
+
+ /**
+ * Tests that canHandle yields false for a string and other object.
+ */
+ @Test
+ public void testCanHandleDoesNotAcceptStringAndOtherObject() {
+ assertFalse(new PasswordAuthenticationProcessor().canHandle("foo", new Object()));
+ }
+
+ /**
+ * Tests that canHandle yields false for any object other than {@link HttpServletRequest}.
+ */
+ @Test
+ public void testCanHandleDoesNotAcceptWrongTypes() {
+ assertFalse(new PasswordAuthenticationProcessor().canHandle(new Object(), new Object()));
+ }
+
+ /**
+ * Tests that updated does not throw an exception for a correct configuration.
+ */
+ @Test
+ public void testUpdatedDoesAcceptCorrectProperties() throws ConfigurationException {
+ final String keyUsername = "foo";
+ final String keyPassword = "bar";
+
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_USERNAME, keyUsername);
+ props.put(PROPERTY_KEY_PASSWORD, keyPassword);
+ props.put(PROPERTY_PASSWORD_HASHMETHOD, "sha1");
+
+ PasswordAuthenticationProcessor processor = new PasswordAuthenticationProcessor();
+
+ processor.updated(props);
+
+ byte[] hashedPw = DigestUtils.sha("secret");
+
+ // Test whether we can use the new properties...
+ User user = mock(User.class);
+ when(user.getName()).thenReturn("bob");
+ when(user.hasCredential(eq(keyPassword), eq(hashedPw))).thenReturn(Boolean.TRUE);
+
+ when(m_userAdmin.getUser(eq(keyUsername), eq("bob"))).thenReturn(user);
+
+ User result = processor.authenticate(m_userAdmin, "bob", "secret");
+ assertNotNull(result);
+
+ assertEquals("bob", user.getName());
+ }
+
+ /**
+ * Tests that updated throws an exception for missing "key.password" property.
+ */
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedDoesNotAcceptEmptyKeyPassword() throws ConfigurationException {
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_USERNAME, "foo");
+ props.put(PROPERTY_KEY_PASSWORD, "");
+ props.put(PROPERTY_PASSWORD_HASHMETHOD, "none");
+
+ new PasswordAuthenticationProcessor().updated(props);
+ }
+
+ /**
+ * Tests that updated throws an exception for missing "key.username" property.
+ */
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedDoesNotAcceptEmptyKeyUsername() throws ConfigurationException {
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_USERNAME, "");
+ props.put(PROPERTY_KEY_PASSWORD, "foo");
+ props.put(PROPERTY_PASSWORD_HASHMETHOD, "none");
+
+ new PasswordAuthenticationProcessor().updated(props);
+ }
+
+ /**
+ * Tests that updated throws an exception for missing "password.hashtype" property.
+ */
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedDoesNotAcceptEmptyPasswordHashType() throws ConfigurationException {
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_USERNAME, "foo");
+ props.put(PROPERTY_KEY_PASSWORD, "bar");
+ props.put(PROPERTY_PASSWORD_HASHMETHOD, "");
+
+ new PasswordAuthenticationProcessor().updated(props);
+ }
+
+ /**
+ * Tests that updated throws an exception for missing "key.password" property.
+ */
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedDoesNotAcceptMissingKeyPassword() throws ConfigurationException {
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_USERNAME, "foo");
+ props.put(PROPERTY_PASSWORD_HASHMETHOD, "none");
+
+ new PasswordAuthenticationProcessor().updated(props);
+ }
+
+ /**
+ * Tests that updated throws an exception for missing "key.username" property.
+ */
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedDoesNotAcceptMissingKeyUsername() throws ConfigurationException {
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_PASSWORD, "foo");
+ props.put(PROPERTY_PASSWORD_HASHMETHOD, "none");
+
+ new PasswordAuthenticationProcessor().updated(props);
+ }
+
+ /**
+ * Tests that updated throws an exception for missing "password.hashtype" property.
+ */
+ @Test(expected = ConfigurationException.class)
+ public void testUpdatedDoesNotAcceptMissingPasswordHashType() throws ConfigurationException {
+ Properties props = new Properties();
+ props.put(PROPERTY_KEY_USERNAME, "foo");
+ props.put(PROPERTY_KEY_PASSWORD, "foo");
+
+ new PasswordAuthenticationProcessor().updated(props);
+ }
+}
Propchange: ace/trunk/ace-authenticationprocessor-password/src/test/java/org/apache/ace/authenticationprocessor/password/PasswordAuthenticationProcessorTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: ace/trunk/ace-client-rest/pom.xml
URL: http://svn.apache.org/viewvc/ace/trunk/ace-client-rest/pom.xml?rev=1325151&r1=1325150&r2=1325151&view=diff
==============================================================================
--- ace/trunk/ace-client-rest/pom.xml (original)
+++ ace/trunk/ace-client-rest/pom.xml Thu Apr 12 08:14:17 2012
@@ -56,6 +56,10 @@
<dependencies>
<dependency>
<groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authentication.api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
<artifactId>org.apache.ace.client.repository.api</artifactId>
</dependency>
<dependency>
Modified: ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/RESTClientServlet.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/RESTClientServlet.java?rev=1325151&r1=1325150&r2=1325151&view=diff
==============================================================================
--- ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/RESTClientServlet.java (original)
+++ ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/RESTClientServlet.java Thu Apr 12 08:14:17 2012
@@ -291,7 +291,7 @@ public class RESTClientServlet extends H
}
if (pathElements.length == 1) {
- createWorkspace(resp);
+ createWorkspace(req, resp);
}
else {
// more than one path elements...
@@ -380,10 +380,10 @@ public class RESTClientServlet extends H
/**
* Creates a new workspace.
*
- * @param resp the servlet repsonse to write the response data to.
+ * @param resp the servlet response to write the response data to.
* @throws IOException in case of I/O errors.
*/
- private void createWorkspace(HttpServletResponse resp) throws IOException {
+ private void createWorkspace(final HttpServletRequest req, final HttpServletResponse resp) throws IOException {
// TODO get data from post body (if no data, assume latest??) -> for now always assume latest
final String sessionID;
final Workspace workspace;
@@ -400,7 +400,11 @@ public class RESTClientServlet extends H
m_sessionFactory.createSession(sessionID);
m_dm.add(component);
- resp.sendRedirect(buildPathFromElements(WORK_FOLDER, sessionID));
+ if (!workspace.login(req)) {
+ resp.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+ } else {
+ resp.sendRedirect(buildPathFromElements(WORK_FOLDER, sessionID));
+ }
}
/**
Modified: ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/Workspace.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/Workspace.java?rev=1325151&r1=1325150&r2=1325151&view=diff
==============================================================================
--- ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/Workspace.java (original)
+++ ace/trunk/ace-client-rest/src/main/java/org/apache/ace/client/rest/Workspace.java Thu Apr 12 08:14:17 2012
@@ -27,6 +27,9 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.ace.authentication.api.AuthenticationService;
import org.apache.ace.client.repository.ObjectRepository;
import org.apache.ace.client.repository.RepositoryAdmin;
import org.apache.ace.client.repository.RepositoryObject;
@@ -62,6 +65,7 @@ public class Workspace {
private final String m_distributionRepositoryName;
private final String m_deploymentRepositoryName;
private final String m_serverUser;
+ private volatile AuthenticationService m_authenticationService;
private volatile DependencyManager m_manager;
private volatile RepositoryAdmin m_repositoryAdmin;
private volatile ArtifactRepository m_artifactRepository;
@@ -118,13 +122,30 @@ public class Workspace {
addSessionDependency(component, Artifact2FeatureAssociationRepository.class, true);
addSessionDependency(component, Feature2DistributionAssociationRepository.class, true);
addSessionDependency(component, Distribution2TargetAssociationRepository.class, true);
+ addDependency(component, AuthenticationService.class, true);
addDependency(component, UserAdmin.class, true);
addDependency(component, LogService.class, false);
}
public void start() {
+ }
+
+ public void destroy() {
+ }
+
+ public boolean login(HttpServletRequest request) {
try {
- User user = m_userAdmin.getUser("username", m_serverUser);
+ User user = m_authenticationService.authenticate(request);
+ if (user == null) {
+ // No user obtained through request; try fallback scenario...
+ // TODO this shouldn't be here, but otherwise we break all existing clients
+ user = m_userAdmin.getUser("username", m_serverUser);
+ if (user == null) {
+ // Still no user obtained; no succesful login...
+ return false;
+ }
+ }
+
m_repositoryAdmin.login(m_repositoryAdmin.createLoginContext(user)
.setObrBase(new URL(m_obrURL))
.addShopRepository(new URL(m_repositoryURL), m_customerName, m_storeRepositoryName, true)
@@ -137,13 +158,12 @@ public class Workspace {
e.printStackTrace();
m_log.log(LogService.LOG_ERROR, "Could not login and checkout. Workspace will probably not work correctly.", e);
}
- }
-
- public void destroy() {
+
+ return true;
}
public void checkout() throws IOException {
- m_repositoryAdmin.checkout();
+ m_repositoryAdmin.checkout();
}
public void commit() throws IOException {
Modified: ace/trunk/ace-target-devserver/pom.xml
URL: http://svn.apache.org/viewvc/ace/trunk/ace-target-devserver/pom.xml?rev=1325151&r1=1325150&r2=1325151&view=diff
==============================================================================
--- ace/trunk/ace-target-devserver/pom.xml (original)
+++ ace/trunk/ace-target-devserver/pom.xml Thu Apr 12 08:14:17 2012
@@ -174,6 +174,21 @@
</dependency>
<dependency>
<groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authentication</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authenticationprocessor.password</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authenticationprocessor.basicauth</artifactId>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
<artifactId>org.apache.ace.client.repository.api</artifactId>
<scope>runtime</scope>
</dependency>
Modified: ace/trunk/ace-webui-vaadin/pom.xml
URL: http://svn.apache.org/viewvc/ace/trunk/ace-webui-vaadin/pom.xml?rev=1325151&r1=1325150&r2=1325151&view=diff
==============================================================================
--- ace/trunk/ace-webui-vaadin/pom.xml (original)
+++ ace/trunk/ace-webui-vaadin/pom.xml Thu Apr 12 08:14:17 2012
@@ -87,6 +87,14 @@
<artifactId>org.apache.ace.httplistener</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authentication.api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authentication</artifactId>
+ </dependency>
+ <dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin</artifactId>
</dependency>
Modified: ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java?rev=1325151&r1=1325150&r2=1325151&view=diff
==============================================================================
--- ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java (original)
+++ ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinClient.java Thu Apr 12 08:14:17 2012
@@ -28,6 +28,7 @@ import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.ace.authentication.api.AuthenticationService;
import org.apache.ace.client.repository.RepositoryAdmin;
import org.apache.ace.client.repository.RepositoryAdminLoginContext;
import org.apache.ace.client.repository.RepositoryObject;
@@ -51,6 +52,7 @@ import org.apache.ace.client.repository.
import org.apache.ace.test.utils.FileUtils;
import org.apache.ace.webui.NamedObject;
import org.apache.ace.webui.UIExtensionFactory;
+import org.apache.ace.webui.vaadin.LoginWindow.LoginFunction;
import org.apache.ace.webui.vaadin.component.ArtifactsPanel;
import org.apache.ace.webui.vaadin.component.DistributionsPanel;
import org.apache.ace.webui.vaadin.component.FeaturesPanel;
@@ -101,7 +103,7 @@ import com.vaadin.ui.Window;
- Add buttons to create new items in all of the tables (done for those that make sense)
*/
@SuppressWarnings("serial")
-public class VaadinClient extends com.vaadin.Application implements AssociationRemover {
+public class VaadinClient extends com.vaadin.Application implements AssociationRemover, LoginFunction {
private static final long serialVersionUID = 1L;
@@ -113,6 +115,7 @@ public class VaadinClient extends com.va
private static String customerName = "apache";
private static String endpoint = "/repository";
+ private volatile AuthenticationService m_authenticationService;
private volatile DependencyManager m_manager;
private volatile BundleContext m_context;
private volatile SessionFactory m_sessionFactory;
@@ -234,12 +237,10 @@ public class VaadinClient extends com.va
* Shows the login window on the center of the main window.
*/
private void showLoginWindow() {
- LoginWindow loginWindow = new LoginWindow(m_log, new LoginWindow.LoginFunction() {
- public boolean login(String name, String password) {
- return VaadinClient.this.login(name, password);
- }
- });
+ LoginWindow loginWindow = new LoginWindow(m_log, this);
+
m_mainWindow.addWindow(loginWindow);
+
loginWindow.center();
}
@@ -448,15 +449,16 @@ public class VaadinClient extends com.va
m_feature2distributionAssociationRepository.remove(association);
}
- boolean login(String username, String password) {
+ /**
+ * {@inheritDoc}
+ */
+ public boolean login(String username, String password) {
try {
- User user = m_userAdmin.getUser("username", username);
+ User user = m_authenticationService.authenticate(username, password);
if (user == null) {
return false;
}
- if (!user.hasCredential("password", password)) {
- return false;
- }
+
RepositoryAdminLoginContext context = m_admin.createLoginContext(user);
// @formatter:off
@@ -465,13 +467,15 @@ public class VaadinClient extends com.va
.addTargetRepository(new URL(m_aceHost, endpoint), customerName, targetRepo, true)
.addDeploymentRepository(new URL(m_aceHost, endpoint), customerName, deployRepo, true);
// @formatter:on
+
m_admin.login(context);
initGrid(user);
m_admin.checkout();
+
return true;
}
catch (IOException e) {
- e.printStackTrace();
+ m_log.log(LogService.LOG_WARNING, "Login failed!", e);
return false;
}
}
Modified: ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinServlet.java
URL: http://svn.apache.org/viewvc/ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinServlet.java?rev=1325151&r1=1325150&r2=1325151&view=diff
==============================================================================
--- ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinServlet.java (original)
+++ ace/trunk/ace-webui-vaadin/src/main/java/org/apache/ace/webui/vaadin/VaadinServlet.java Thu Apr 12 08:14:17 2012
@@ -18,9 +18,14 @@
*/
package org.apache.ace.webui.vaadin;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Dictionary;
+
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
+import org.apache.ace.authentication.api.AuthenticationService;
import org.apache.ace.client.repository.SessionFactory;
import org.apache.felix.dm.DependencyManager;
import org.osgi.service.cm.ConfigurationException;
@@ -31,10 +36,6 @@ import org.osgi.service.useradmin.UserAd
import com.vaadin.Application;
import com.vaadin.terminal.gwt.server.AbstractApplicationServlet;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.Dictionary;
-
public class VaadinServlet extends AbstractApplicationServlet implements ManagedService {
private static final long serialVersionUID = 1L;
@@ -68,6 +69,10 @@ public class VaadinServlet extends Abstr
.setRequired(true)
)
.add(m_manager.createServiceDependency()
+ .setService(AuthenticationService.class)
+ .setRequired(true)
+ )
+ .add(m_manager.createServiceDependency()
.setService(LogService.class)
.setRequired(false)
)
Modified: ace/trunk/pom.xml
URL: http://svn.apache.org/viewvc/ace/trunk/pom.xml?rev=1325151&r1=1325150&r2=1325151&view=diff
==============================================================================
--- ace/trunk/pom.xml (original)
+++ ace/trunk/pom.xml Thu Apr 12 08:14:17 2012
@@ -38,6 +38,12 @@
<module>ace-ant-tasks</module>
<module>ace-builder</module>
+ <module>ace-authentication-api</module>
+ <module>ace-authentication</module>
+ <module>ace-authenticationprocessor-basicauth</module>
+ <module>ace-authenticationprocessor-clientcert</module>
+ <module>ace-authenticationprocessor-password</module>
+
<module>ace-range-api</module>
<module>ace-repository-api</module>
@@ -198,6 +204,9 @@
<modules>
<module>pom</module>
<module>ace-util</module>
+ <module>ace-authentication</module>
+ <module>ace-authenticationprocessor-basicauth</module>
+ <module>ace-authenticationprocessor-password</module>
<module>ace-client-repository-impl</module>
<module>ace-client-repository-helper-bundle</module>
<module>ace-configurator</module>
Modified: ace/trunk/pom/pom.xml
URL: http://svn.apache.org/viewvc/ace/trunk/pom/pom.xml?rev=1325151&r1=1325150&r2=1325151&view=diff
==============================================================================
--- ace/trunk/pom/pom.xml (original)
+++ ace/trunk/pom/pom.xml Thu Apr 12 08:14:17 2012
@@ -134,6 +134,7 @@
<deployment.providesresourceprocessor />
<sourceReleaseAssemblyDescriptor>source-release-zip-tar</sourceReleaseAssemblyDescriptor>
<commons-collections.version>3.2.1</commons-collections.version>
+ <commons-codec.version>1.5</commons-codec.version>
<commons-lang.version>2.4</commons-lang.version>
<commons-logging.version>1.1.1</commons-logging.version>
<commons-io.version>2.0.1</commons-io.version>
@@ -325,6 +326,31 @@
<!-- ACE modules -->
<dependency>
<groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authentication.api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authentication</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authenticationprocessor.basicauth</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authenticationprocessor.clientcert</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
+ <artifactId>org.apache.ace.authenticationprocessor.password</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ace</groupId>
<artifactId>org.apache.ace.client.automation</artifactId>
<version>${project.version}</version>
</dependency>
@@ -687,6 +713,11 @@
<!-- Commons -->
<dependency>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ <version>${commons-codec.version}</version>
+ </dependency>
+ <dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>${commons-collections.version}</version>