You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2013/04/02 14:41:44 UTC

svn commit: r1463507 [1/2] - in /ace/trunk: org.apache.ace.authentication.api/ org.apache.ace.authentication.itest/ org.apache.ace.authentication/ org.apache.ace.authentication/src/org/apache/ace/authentication/api/ org.apache.ace.authentication/src/or...

Author: marrs
Date: Tue Apr  2 12:41:42 2013
New Revision: 1463507

URL: http://svn.apache.org/r1463507
Log:
ACE-325 ACE-333 Updated our client, server, obr, and merged the authentication projects into one.

Added:
    ace/trunk/org.apache.ace.authentication/api.bnd
    ace/trunk/org.apache.ace.authentication/impl.bnd
    ace/trunk/org.apache.ace.authentication/processor.basicauth.bnd
    ace/trunk/org.apache.ace.authentication/processor.clientcert.bnd
    ace/trunk/org.apache.ace.authentication/processor.password.bnd
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/AuthenticationProcessor.java
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/AuthenticationService.java
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/packageinfo
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/basicauth/
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/basicauth/Activator.java
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/basicauth/BasicHttpAuthenticationProcessor.java
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/clientcert/
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/clientcert/Activator.java
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/clientcert/ClientCertAuthenticationProcessor.java
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/password/
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/password/Activator.java
    ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/password/PasswordAuthenticationProcessor.java
    ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/
    ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/basicauth/
    ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/basicauth/BasicHttpAuthenticationProcessorTest.java
    ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/clientcert/
    ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/clientcert/ClientCertAuthenticationProcessorTest.java
    ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/clientcert/MemoryKeyStore.java
    ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/password/
    ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/password/PasswordAuthenticationProcessorTest.java
    ace/trunk/run-client/
    ace/trunk/run-client/.classpath
    ace/trunk/run-client/.project
    ace/trunk/run-client/.settings/
    ace/trunk/run-client/.settings/org.eclipse.jdt.core.prefs
    ace/trunk/run-client/bnd.bnd
    ace/trunk/run-client/build.xml
    ace/trunk/run-client/client.bndrun
    ace/trunk/run-client/conf/
    ace/trunk/run-client/conf/org.apache.ace.client.rest.cfg
    ace/trunk/run-client/conf/org.apache.ace.configurator.useradmin.task.UpdateUserAdminTask.cfg
    ace/trunk/run-client/conf/org.apache.ace.connectionfactory/
    ace/trunk/run-client/conf/org.apache.ace.connectionfactory/auditlog.cfg
    ace/trunk/run-client/conf/org.apache.ace.connectionfactory/client.cfg
    ace/trunk/run-client/conf/org.apache.ace.connectionfactory/deployment.cfg
    ace/trunk/run-client/conf/org.apache.ace.connectionfactory/obr.cfg
    ace/trunk/run-client/conf/org.apache.ace.connectionfactory/replication.cfg
    ace/trunk/run-client/conf/org.apache.ace.connectionfactory/repository.cfg
    ace/trunk/run-client/conf/org.apache.ace.discovery.property.cfg
    ace/trunk/run-client/conf/org.apache.ace.scheduler.cfg
    ace/trunk/run-client/conf/org.apache.ace.server.log.store.factory/
    ace/trunk/run-client/conf/org.apache.ace.server.log.store.factory/auditlog.cfg
    ace/trunk/run-client/conf/org.apache.ace.server.log.task.factory/
    ace/trunk/run-client/conf/org.apache.ace.server.log.task.factory/auditlog.cfg
    ace/trunk/run-client/conf/org.apache.ace.server.repository.factory/
    ace/trunk/run-client/conf/org.apache.ace.server.repository.factory/ace-user.cfg
    ace/trunk/run-client/conf/org.apache.ace.webui.vaadin.cfg
    ace/trunk/run-client/src/
    ace/trunk/run-client/test/
    ace/trunk/run-obr/src/
    ace/trunk/run-obr/test/
    ace/trunk/run-server/
    ace/trunk/run-server/.classpath
    ace/trunk/run-server/.project
    ace/trunk/run-server/.settings/
    ace/trunk/run-server/.settings/org.eclipse.jdt.core.prefs
    ace/trunk/run-server/bnd.bnd
    ace/trunk/run-server/build.xml
    ace/trunk/run-server/conf/
    ace/trunk/run-server/conf/org.apache.ace.configurator.useradmin.task.UpdateUserAdminTask.cfg
    ace/trunk/run-server/conf/org.apache.ace.connectionfactory/
    ace/trunk/run-server/conf/org.apache.ace.connectionfactory/auditlog.cfg
    ace/trunk/run-server/conf/org.apache.ace.connectionfactory/client.cfg
    ace/trunk/run-server/conf/org.apache.ace.connectionfactory/deployment.cfg
    ace/trunk/run-server/conf/org.apache.ace.connectionfactory/replication.cfg
    ace/trunk/run-server/conf/org.apache.ace.connectionfactory/repository.cfg
    ace/trunk/run-server/conf/org.apache.ace.deployment.provider.repositorybased.cfg
    ace/trunk/run-server/conf/org.apache.ace.deployment.servlet.cfg
    ace/trunk/run-server/conf/org.apache.ace.distribution.servlet.cfg
    ace/trunk/run-server/conf/org.apache.ace.repository.servlet.RepositoryReplicationServlet.cfg
    ace/trunk/run-server/conf/org.apache.ace.repository.servlet.RepositoryServlet.cfg
    ace/trunk/run-server/conf/org.apache.ace.scheduler.cfg
    ace/trunk/run-server/conf/org.apache.ace.server.log.servlet.factory/
    ace/trunk/run-server/conf/org.apache.ace.server.log.servlet.factory/auditlog.cfg
    ace/trunk/run-server/conf/org.apache.ace.server.log.store.factory/
    ace/trunk/run-server/conf/org.apache.ace.server.log.store.factory/auditlog.cfg
    ace/trunk/run-server/conf/org.apache.ace.server.repository.factory/
    ace/trunk/run-server/conf/org.apache.ace.server.repository.factory/ace-activation.cfg
    ace/trunk/run-server/conf/org.apache.ace.server.repository.factory/ace-activationinfo.cfg
    ace/trunk/run-server/conf/org.apache.ace.server.repository.factory/ace-deployment.cfg
    ace/trunk/run-server/conf/org.apache.ace.server.repository.factory/ace-shop.cfg
    ace/trunk/run-server/conf/org.apache.ace.server.repository.factory/ace-target.cfg
    ace/trunk/run-server/conf/org.apache.ace.server.repository.factory/ace-user.cfg
    ace/trunk/run-server/server.bndrun
    ace/trunk/run-server/src/
    ace/trunk/run-server/test/
Removed:
    ace/trunk/org.apache.ace.authentication.api/
    ace/trunk/org.apache.ace.authenticationprocessor.basicauth/
    ace/trunk/org.apache.ace.authenticationprocessor.clientcert/
    ace/trunk/org.apache.ace.authenticationprocessor.password/
Modified:
    ace/trunk/org.apache.ace.authentication.itest/bnd.bnd
    ace/trunk/org.apache.ace.authentication/bnd.bnd
    ace/trunk/org.apache.ace.useradmin.ui.itest/bnd.bnd
    ace/trunk/org.apache.ace.useradmin.ui/bnd.bnd
    ace/trunk/org.apache.ace.webui.vaadin/bnd.bnd
    ace/trunk/run-obr/obr.bndrun
    ace/trunk/run-server-allinone/server-allinone.bndrun

Modified: ace/trunk/org.apache.ace.authentication.itest/bnd.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication.itest/bnd.bnd?rev=1463507&r1=1463506&r2=1463507&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.authentication.itest/bnd.bnd (original)
+++ ace/trunk/org.apache.ace.authentication.itest/bnd.bnd Tue Apr  2 12:41:42 2013
@@ -34,7 +34,7 @@ Test-Cases: ${classes;CONCRETE;EXTENDS;o
 	org.apache.ace.configurator.serveruseradmin;version=latest,\
 	org.apache.ace.range.api;version=latest,\
 	org.apache.ace.repository.impl;version=latest,\
-	org.apache.ace.authenticationprocessor.basicauth;version=latest,\
+	org.apache.ace.authentication.processor.basicauth;version=latest,\
 	org.apache.ace.client.repository.helper.base;version=latest,\
 	org.apache.ace.repository.api;version=latest,\
 	org.apache.ace.client.repository.api;version=latest,\
@@ -55,7 +55,7 @@ Test-Cases: ${classes;CONCRETE;EXTENDS;o
 	org.apache.ace.authentication.api;version=latest,\
 	org.apache.ace.client.repository.impl;version=latest,\
 	org.apache.ace.connectionfactory;version=latest,\
-	org.apache.ace.authentication;version=latest,\
+	org.apache.ace.authentication.impl;version=latest,\
 	org.apache.ace.log.listener;version=latest,\
 	org.apache.ace.obr.servlet;version=latest,\
 	org.apache.ace.deployment.provider.api;version=latest,\

Added: ace/trunk/org.apache.ace.authentication/api.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/api.bnd?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/api.bnd (added)
+++ ace/trunk/org.apache.ace.authentication/api.bnd Tue Apr  2 12:41:42 2013
@@ -0,0 +1,2 @@
+Export-Package: org.apache.ace.authentication.api
+Bundle-Version: 1.0.0

Modified: ace/trunk/org.apache.ace.authentication/bnd.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/bnd.bnd?rev=1463507&r1=1463506&r2=1463507&view=diff
==============================================================================
--- ace/trunk/org.apache.ace.authentication/bnd.bnd (original)
+++ ace/trunk/org.apache.ace.authentication/bnd.bnd Tue Apr  2 12:41:42 2013
@@ -1,10 +1,9 @@
--buildpath: \
-	osgi.core,\
+-buildpath: osgi.core,\
 	osgi.cmpn,\
 	org.mockito.mockito-all,\
 	org.apache.felix.dependencymanager,\
-	org.apache.ace.authentication.api;version=latest,\
+	javax.servlet,\
+	bcprov;version=latest,\
+	commons-codec;version=1.4,\
 	org.apache.ace.test;version=latest
-Private-Package: org.apache.ace.authentication.impl
-Bundle-Activator: org.apache.ace.authentication.impl.Activator
-Bundle-Version: 1.0.0
+-sub: *.bnd
\ No newline at end of file

Added: ace/trunk/org.apache.ace.authentication/impl.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/impl.bnd?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/impl.bnd (added)
+++ ace/trunk/org.apache.ace.authentication/impl.bnd Tue Apr  2 12:41:42 2013
@@ -0,0 +1,3 @@
+Private-Package: org.apache.ace.authentication.impl
+Bundle-Activator: org.apache.ace.authentication.impl.Activator
+Bundle-Version: 1.0.0

Added: ace/trunk/org.apache.ace.authentication/processor.basicauth.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/processor.basicauth.bnd?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/processor.basicauth.bnd (added)
+++ ace/trunk/org.apache.ace.authentication/processor.basicauth.bnd Tue Apr  2 12:41:42 2013
@@ -0,0 +1,5 @@
+Private-Package: org.apache.ace.authentication.processor.basicauth,\
+	org.apache.commons.codec,\
+	org.apache.commons.codec.binary
+Bundle-Activator: org.apache.ace.authentication.processor.basicauth.Activator
+Bundle-Version: 1.0.0

Added: ace/trunk/org.apache.ace.authentication/processor.clientcert.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/processor.clientcert.bnd?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/processor.clientcert.bnd (added)
+++ ace/trunk/org.apache.ace.authentication/processor.clientcert.bnd Tue Apr  2 12:41:42 2013
@@ -0,0 +1,3 @@
+Private-Package: org.apache.ace.authentication.processor.clientcert
+Bundle-Activator: org.apache.ace.authentication.processor.clientcert.Activator
+Bundle-Version: 1.0.0
\ No newline at end of file

Added: ace/trunk/org.apache.ace.authentication/processor.password.bnd
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/processor.password.bnd?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/processor.password.bnd (added)
+++ ace/trunk/org.apache.ace.authentication/processor.password.bnd Tue Apr  2 12:41:42 2013
@@ -0,0 +1,6 @@
+Private-Package: org.apache.ace.authentication.processor.password,\
+	org.apache.commons.codec,\
+	org.apache.commons.codec.binary,\
+	org.apache.commons.codec.digest
+Bundle-Activator: org.apache.ace.authentication.processor.password.Activator
+Bundle-Version: 1.0.0
\ No newline at end of file

Added: ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/AuthenticationProcessor.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/AuthenticationProcessor.java?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/AuthenticationProcessor.java (added)
+++ ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/AuthenticationProcessor.java Tue Apr  2 12:41:42 2013
@@ -0,0 +1,67 @@
+/*
+ * 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.authentication.api;
+
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Provides a pluggable authentication processor, responsible for the actual authentication of a
+ * user based on given context information.
+ * <p>
+ * When multiple authentication processors are implemented and used for the authentication process,
+ * an order in which they should be used is determined based on their <em>service ranking</em>.
+ * </p>
+ */
+public interface AuthenticationProcessor {
+
+    /**
+     * Returns whether or not this authentication processor can handle the given context
+     * information.
+     * <p>
+     * NOTE: this method does not need to perform the actual authentication!
+     * </p>
+     * <p>
+     * For example, for an implementation that authenticates a user based on its username
+     * and password might check whether the given context information consists of two
+     * strings.
+     * </p>
+     * 
+     * @param context the context information to check, should never be <code>null</code> or an
+     *        empty array.
+     * @return <code>true</code> if this authentication processor can handle the given context
+     *         information, <code>false</code> otherwise.
+     * @throws IllegalArgumentException in case the given context was <code>null</code> or an empty array;
+     * @throws NullPointerException in case the given array contains <code>null</code> as element(s).
+     */
+    boolean canHandle(Object... context);
+
+    /**
+     * Authenticates a user based on the given context information.
+     * 
+     * @param userAdmin the user admin service, to use for verifying/retrieving user information,
+     *        cannot be <code>null</code>;
+     * @param context the context information to authenticate the user with, should never be
+     *        <code>null</code> or an empty array.
+     * @return the authenticated user, or <code>null</code> if authentication failed.
+     * @throws IllegalArgumentException in case the given context was <code>null</code> or an empty array;
+     * @throws NullPointerException in case the given array contains <code>null</code> as element(s).
+     */
+    User authenticate(UserAdmin userAdmin, Object... context);
+}

Added: ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/AuthenticationService.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/AuthenticationService.java?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/AuthenticationService.java (added)
+++ ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/AuthenticationService.java Tue Apr  2 12:41:42 2013
@@ -0,0 +1,57 @@
+/*
+ * 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.authentication.api;
+
+import org.osgi.service.useradmin.User;
+
+/**
+ * Provides a generic and pluggable authentication service.
+ * <p>
+ * This service provides the front end to all services that wish to authenticate a user. In short,
+ * this service will look up all available {@link AuthenticationProcessor}s and use them to perform
+ * the actual authentication.
+ * </p>
+ * 
+ * @see AuthenticationProcessor
+ */
+public interface AuthenticationService {
+
+    /**
+     * Authenticates a user based on the given context information.
+     * <p>
+     * The context information can be any kind of object, hence it is not exactly typed. As this
+     * service is pluggable, it is up to the authentication processors to interpret the context
+     * information.
+     * </p>
+     * <p>
+     * Implementations can decide on the strategy of authentication, whether all participating
+     * authentication processors <b>must</b> or <b>may</b> match.<br/>
+     * If multiple authentication processors are found, they <b>must</b> be ordered on their 
+     * <em>service.ranking</em> property. The one with the higest service ranking is used first,
+     * and so on.
+     * </p>
+     * 
+     * @param context the context information, cannot be <code>null</code> or an empty array.
+     * @return an authenticated {@link User}, or <code>null</code> if authentication failed
+     *         (or otherwise was not possible).
+     * @throws IllegalArgumentException in case the given context was <code>null</code> or an empty array;
+     * @throws NullPointerException in case the given array contains <code>null</code> as element(s).
+     */
+    User authenticate(Object... context);
+}

Added: ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/packageinfo
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/packageinfo?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/packageinfo (added)
+++ ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/api/packageinfo Tue Apr  2 12:41:42 2013
@@ -0,0 +1 @@
+version 1.0
\ No newline at end of file

Added: ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/basicauth/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/basicauth/Activator.java?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/basicauth/Activator.java (added)
+++ ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/basicauth/Activator.java Tue Apr  2 12:41:42 2013
@@ -0,0 +1,59 @@
+/*
+ * 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.authentication.processor.basicauth;
+
+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, BasicHttpAuthenticationProcessor.PID);
+        
+// @formatter:off
+        manager.add(createComponent()
+            .setInterface(new String[]{ AuthenticationProcessor.class.getName(), ManagedService.class.getName() }, props)
+            .setImplementation(new BasicHttpAuthenticationProcessor())
+        );
+// @formatter:on
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+        // Nop
+    }
+}

Added: ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/basicauth/BasicHttpAuthenticationProcessor.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/basicauth/BasicHttpAuthenticationProcessor.java?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/basicauth/BasicHttpAuthenticationProcessor.java (added)
+++ ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/basicauth/BasicHttpAuthenticationProcessor.java Tue Apr  2 12:41:42 2013
@@ -0,0 +1,168 @@
+/*
+ * 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.authentication.processor.basicauth;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Dictionary;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.ace.authentication.api.AuthenticationProcessor;
+import org.apache.commons.codec.binary.Base64;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Provides an {@link AuthenticationProcessor} that implements basic HTTP authentication and looks
+ * up a user in the {@link UserAdmin} service using (by default, can be configured otherwise) the
+ * keys "username" and "password".
+ */
+public class BasicHttpAuthenticationProcessor implements AuthenticationProcessor, ManagedService {
+
+    public static final String PID = "org.apache.ace.authenticationprocessor.basicauth";
+
+    /** The name of the HTTP-header used for HTTP authentication. */
+    static final String AUTHORIZATION_HEADER = "Authorization";
+
+    static final String PROPERTY_KEY_USERNAME = "key.username";
+    static final String PROPERTY_KEY_PASSWORD = "key.password";
+
+    private static final String DEFAULT_PROPERTY_KEY_USERNAME = "username";
+    private static final String DEFAULT_PROPERTY_KEY_PASSWORD = "password";
+
+    private volatile String m_keyUsername = DEFAULT_PROPERTY_KEY_USERNAME;
+    private volatile String m_keyPassword = DEFAULT_PROPERTY_KEY_PASSWORD;
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean canHandle(Object... context) {
+        if (context == null || context.length == 0) {
+            throw new IllegalArgumentException("Invalid context!");
+        }
+
+        return (context[0] instanceof HttpServletRequest);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public User authenticate(UserAdmin userAdmin, Object... context) {
+        final HttpServletRequest request = (HttpServletRequest) context[0];
+
+        String header = request.getHeader(AUTHORIZATION_HEADER);
+        if (header == null || header.length() < 6) {
+            // No authorization header obtained; cannot authorize...
+            return null;
+        }
+
+        // Form = 'Basic ' + base64 encoded credentials
+        String packedCredentials = decodeBase64(header);
+        if (packedCredentials == null) {
+            // No credentials obtained; cannot authenticate...
+            return null;
+        }
+
+        // Form = <user>:<password>
+        String[] credentials = packedCredentials.split(":");
+        if (credentials.length != 2) {
+            // A colon should always be present!
+            return null;
+        }
+
+        User user = getUser(userAdmin, credentials[0]);
+        if (user == null || !user.hasCredential(m_keyPassword, credentials[1])) {
+            // Invalid/unknown user!
+            return null;
+        }
+
+        return user;
+    }
+
+    /**
+     * {@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");
+            }
+
+            m_keyUsername = keyUsername;
+            m_keyPassword = keyPassword;
+        }
+        else {
+            m_keyUsername = DEFAULT_PROPERTY_KEY_USERNAME;
+            m_keyPassword = DEFAULT_PROPERTY_KEY_PASSWORD;
+        }
+    }
+
+    /**
+     * Decodes a given base64-encoded string.
+     * 
+     * @param header the base64 encoded header to decode.
+     * @return the base64 decoded string, can be <code>null</code>.
+     */
+    private String decodeBase64(String header) {
+        byte[] array = Base64.decodeBase64(header.substring(6));
+        if (array == null) {
+            return null;
+        }
+
+        try {
+            return new String(array, "UTF-8");
+        }
+        catch (UnsupportedEncodingException e) {
+            // Should never occur, as Java is always capable of handling UTF-8!
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Searches for a user with a given name.
+     * <p>
+     * This method first looks whether there's a user with the property 
+     * "m_keyUsername" that matches the given username, if not found, it will 
+     * try to retrieve a role with the given name.
+     * </p>
+     * 
+     * @param userAdmin the {@link UserAdmin} service to get users from;
+     * @param name the name of the user to retrieve.
+     * @return a {@link User}, can be <code>null</code> if no such user is found.
+     */
+    private User getUser(UserAdmin userAdmin, String name) {
+        Role user = null;
+        if (m_keyUsername != null) {
+            user = userAdmin.getUser(m_keyUsername, name);
+        }
+        if (user == null) {
+            user = userAdmin.getRole(name);
+        }
+        return (user instanceof User) ? (User) user : null;
+    }
+}

Added: ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/clientcert/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/clientcert/Activator.java?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/clientcert/Activator.java (added)
+++ ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/clientcert/Activator.java Tue Apr  2 12:41:42 2013
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ace.authentication.processor.clientcert;
+
+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;
+import org.osgi.service.log.LogService;
+
+/**
+ * Provides a bundle activator for the {@link ClientCertAuthenticationProcessor}.
+ */
+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, ClientCertAuthenticationProcessor.PID);
+
+// @formatter:off
+        manager.add(createComponent()
+            .setInterface(new String[]{ AuthenticationProcessor.class.getName(), ManagedService.class.getName() }, props)
+            .setImplementation(new ClientCertAuthenticationProcessor())
+            .add(createServiceDependency()
+                .setService(LogService.class)
+                .setRequired(false)
+            )
+        );
+// @formatter:on
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+        // Nop
+    }
+}

Added: ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/clientcert/ClientCertAuthenticationProcessor.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/clientcert/ClientCertAuthenticationProcessor.java?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/clientcert/ClientCertAuthenticationProcessor.java (added)
+++ ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/clientcert/ClientCertAuthenticationProcessor.java Tue Apr  2 12:41:42 2013
@@ -0,0 +1,252 @@
+/*
+ * 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.authentication.processor.clientcert;
+
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Dictionary;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.ace.authentication.api.AuthenticationProcessor;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.Role;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+
+/**
+ * Provides an {@link AuthenticationProcessor} that implements authentication based on certificates 
+ * and looks up a user in the {@link UserAdmin} service using (by default, can be configured 
+ * otherwise) the key "username". If a matching user is found, it is considered authenticated.
+ */
+public class ClientCertAuthenticationProcessor implements AuthenticationProcessor, ManagedService {
+
+    public static final String PID = "org.apache.ace.authenticationprocessor.clientcert";
+
+    static final String ATTRIBUTE_X509_CERTIFICATE = "javax.servlet.request.X509Certificate";
+    static final String ATTRIBUTE_CIPHER_SUITE = "javax.servlet.request.cipher_suite";
+
+    static final String PROPERTY_USERNAME_LOOKUPKEY = "user.name.lookupKey";
+    static final String PROPERTY_USERNAME_MATCH_POLICY = "user.name.matchPolicy";
+    static final String PROPERTY_VERIFY_CERT_VALIDITY = "certificate.verifyValidity";
+
+    private static final String DEFAULT_PROPERTY_USERNAME_LOOKUPKEY = "username";
+    private static final String DEFAULT_PROPERTY_USERNAME_MATCHPOLICY = "cn";
+    private static final boolean DEFAULT_PROPERTY_VERIFY_CERT_VALIDITY = true;
+
+    private volatile String m_nameLookupKey = DEFAULT_PROPERTY_USERNAME_LOOKUPKEY;
+    private volatile String m_nameMatchPolicy = DEFAULT_PROPERTY_USERNAME_MATCHPOLICY;
+    private volatile boolean m_verifyCertValidity = DEFAULT_PROPERTY_VERIFY_CERT_VALIDITY;
+    private volatile LogService m_log;
+
+    /**
+     * Creates a new {@link ClientCertAuthenticationProcessor} instance.
+     */
+    public ClientCertAuthenticationProcessor() {
+        // Nop
+    }
+
+    /**
+     * Creates a new {@link ClientCertAuthenticationProcessor} with a given logger.
+     */
+    ClientCertAuthenticationProcessor(LogService log) {
+        m_log = log;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean canHandle(Object... context) {
+        if (context == null || context.length == 0) {
+            throw new IllegalArgumentException("Invalid context!");
+        }
+
+        if (!(context[0] instanceof HttpServletRequest)) {
+            return false;
+        }
+        
+        final HttpServletRequest request = (HttpServletRequest) context[0];
+        return (request.getAttribute(ATTRIBUTE_CIPHER_SUITE) != null) && (request.getAttribute(ATTRIBUTE_X509_CERTIFICATE) != null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public User authenticate(UserAdmin userAdmin, Object... context) {
+        final HttpServletRequest request = (HttpServletRequest) context[0];
+
+        String cipherSuite = (String) request.getAttribute(ATTRIBUTE_CIPHER_SUITE);
+        if (cipherSuite == null) {
+            // No SSL connection?!
+            m_log.log(LogService.LOG_DEBUG, "No SSL connection (no cipher suite found)?!");
+            return null;
+        }
+
+        X509Certificate certificateChain[] = (X509Certificate[]) request.getAttribute(ATTRIBUTE_X509_CERTIFICATE);
+        if (certificateChain == null || certificateChain.length == 0) {
+            // No certificates given...
+            m_log.log(LogService.LOG_DEBUG, "Failed to obtain X509 certificate chain from request!");
+            return null;
+        }
+
+        // Validate the certificate chain...
+        // TODO there should be more checks performed here...
+        final X509Certificate cert = validateCertificateChain(certificateChain);
+        if (cert == null) {
+            // Invalid certificate(chain)...
+            m_log.log(LogService.LOG_DEBUG, "Failed to validate X509 certificate chain!");
+            return null;
+        }
+
+        String name = getName(cert);
+        if (name == null) {
+            // No common name given; cannot retrieve user credentials...
+            m_log.log(LogService.LOG_DEBUG, "Failed to obtain common name of X509 certificate!");
+            return null;
+        }
+        
+        User user = getUser(userAdmin, name);
+        if (user == null) {
+            // Invalid/unknown user!
+            m_log.log(LogService.LOG_DEBUG, "Failed to validate user using certificate!");
+            return null;
+        }
+
+        return user;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void updated(Dictionary dictionary) throws ConfigurationException {
+        if (dictionary != null) {
+            String usernameLookupKey = (String) dictionary.get(PROPERTY_USERNAME_LOOKUPKEY);
+            if (usernameLookupKey == null || "".equals(usernameLookupKey.trim())) {
+                throw new ConfigurationException(PROPERTY_USERNAME_LOOKUPKEY, "Missing property");
+            }
+
+            String usernameMatchPolicy = (String) dictionary.get(PROPERTY_USERNAME_MATCH_POLICY);
+            if (usernameMatchPolicy == null || "".equals(usernameMatchPolicy.trim())) {
+                throw new ConfigurationException(PROPERTY_USERNAME_MATCH_POLICY, "Missing property");
+            }
+            
+            Object verifyCertValidity = dictionary.get(PROPERTY_VERIFY_CERT_VALIDITY);
+            if (verifyCertValidity == null || !("true".equals(verifyCertValidity) || "false".equals(verifyCertValidity))) {
+                throw new ConfigurationException(PROPERTY_VERIFY_CERT_VALIDITY, "Missing or invalid property!");
+            }
+
+            m_nameLookupKey = usernameLookupKey;
+            m_nameMatchPolicy = usernameMatchPolicy;
+            m_verifyCertValidity = Boolean.parseBoolean((String) verifyCertValidity);
+        }
+        else {
+            m_nameLookupKey = DEFAULT_PROPERTY_USERNAME_LOOKUPKEY;
+            m_nameMatchPolicy = DEFAULT_PROPERTY_USERNAME_MATCHPOLICY;
+            m_verifyCertValidity = DEFAULT_PROPERTY_VERIFY_CERT_VALIDITY;
+        }
+    }
+
+    /**
+     * Retrieves the name for the given certificate.
+     * 
+     * @param certificate the certificate to get its name for, cannot be <code>null</code>.
+     * @return the name for the given certificate, can be <code>null</code>.
+     */
+    private String getName(X509Certificate certificate) {
+        try {
+            String dn = certificate.getSubjectX500Principal().getName();
+            if ("dn".equalsIgnoreCase(m_nameMatchPolicy)) {
+                return dn;
+            }
+
+            LdapName ldapDN = new LdapName(dn);
+            for (Rdn rdn : ldapDN.getRdns()) {
+                if (m_nameMatchPolicy.equalsIgnoreCase(rdn.getType())) {
+                    return (String) rdn.getValue();
+                }
+            }
+        }
+        catch (InvalidNameException e) {
+            // Ignore...
+        }
+        return null;
+    }
+
+    /**
+     * Searches for a user with a given name.
+     * <p>
+     * This method first looks whether there's a user with the property 
+     * "m_keyUsername" that matches the given username, if not found, it will 
+     * try to retrieve a role with the given name.
+     * </p>
+     * 
+     * @param userAdmin the {@link UserAdmin} service to get users from;
+     * @param name the name of the user to retrieve.
+     * @return a {@link User}, can be <code>null</code> if no such user is found.
+     */
+    private User getUser(UserAdmin userAdmin, String name) {
+        Role user = null;
+        if (m_nameLookupKey != null) {
+            user = userAdmin.getUser(m_nameLookupKey, name);
+        }
+        if (user == null) {
+            user = userAdmin.getRole(name);
+        }
+        return (user instanceof User) ? (User) user : null;
+    }
+
+    /**
+     * Validates the certificate chain whether all certificates are valid and not expired.
+     * 
+     * @param certificateChain the chain of certificates to validate, cannot be <code>null</code>.
+     * @return if the chain is valid, the first certificate, <code>null</code> otherwise.
+     */
+    private X509Certificate validateCertificateChain(X509Certificate[] certificateChain) {
+        try {
+            for (X509Certificate cert : certificateChain) {
+                if (cert == null) {
+                    // Bogus certificate given...
+                    return null;
+                }
+                if (m_verifyCertValidity) {
+                    cert.checkValidity();
+                }
+            }
+        }
+        catch (CertificateExpiredException e) {
+            // Refuse to go further with expired certificates...
+            m_log.log(LogService.LOG_DEBUG, "Certificate expired!", e);
+            return null;
+        }
+        catch (CertificateNotYetValidException e) {
+            // Refuse to go further with invalid certificates...
+            m_log.log(LogService.LOG_DEBUG, "Certificate not yet valid!", e);
+            return null;
+        }
+
+        // This *might* be a valid certificate chain; return the first certificate...
+        return certificateChain[0];
+    }
+}

Added: ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/password/Activator.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/password/Activator.java?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/password/Activator.java (added)
+++ ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/password/Activator.java Tue Apr  2 12:41:42 2013
@@ -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.authentication.processor.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
+    }
+}

Added: ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/password/PasswordAuthenticationProcessor.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/password/PasswordAuthenticationProcessor.java?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/password/PasswordAuthenticationProcessor.java (added)
+++ ace/trunk/org.apache.ace.authentication/src/org/apache/ace/authentication/processor/password/PasswordAuthenticationProcessor.java Tue Apr  2 12:41:42 2013
@@ -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.authentication.processor.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
+    }
+}

Added: ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/basicauth/BasicHttpAuthenticationProcessorTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/basicauth/BasicHttpAuthenticationProcessorTest.java?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/basicauth/BasicHttpAuthenticationProcessorTest.java (added)
+++ ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/basicauth/BasicHttpAuthenticationProcessorTest.java Tue Apr  2 12:41:42 2013
@@ -0,0 +1,263 @@
+/*
+ * 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.authentication.processor.basicauth;
+
+import static org.apache.ace.authentication.processor.basicauth.BasicHttpAuthenticationProcessor.AUTHORIZATION_HEADER;
+import static org.apache.ace.authentication.processor.basicauth.BasicHttpAuthenticationProcessor.PROPERTY_KEY_PASSWORD;
+import static org.apache.ace.authentication.processor.basicauth.BasicHttpAuthenticationProcessor.PROPERTY_KEY_USERNAME;
+import static org.apache.ace.test.utils.TestUtils.UNIT;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Properties;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.ace.authentication.processor.basicauth.BasicHttpAuthenticationProcessor;
+import org.apache.commons.codec.binary.Base64;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ * Test cases for {@link BasicHttpAuthenticationProcessor}.
+ */
+public class BasicHttpAuthenticationProcessorTest {
+    
+    private UserAdmin m_userAdmin;
+    private HttpServletRequest m_servletRequest;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() {
+        m_userAdmin = mock(UserAdmin.class);
+        m_servletRequest = mock(HttpServletRequest.class);
+    }
+
+    /**
+     * Tests that a null authentication header will yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateEmptyAuthenticationHeaderYieldsNull() {
+        User result = new BasicHttpAuthenticationProcessor().authenticate(m_userAdmin, m_servletRequest);
+        assert result == null : "Expected no result!";
+    }
+
+    /**
+     * Tests that an invalid authentication header will yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateInvalidAuthenticationHeaderYieldsNull() {
+        when(m_servletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn(createAuthHeaderValue("bob"));
+        
+        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 BasicHttpAuthenticationProcessor().authenticate(m_userAdmin, m_servletRequest);
+        assert result == null : "Expected no result!";
+    }
+
+    /**
+     * Tests that a known user with an invalid password will yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateKnownUserWithInvalidPasswordYieldsNull() {
+        when(m_servletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn(createAuthHeaderValue("bob:secret"));
+        
+        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 BasicHttpAuthenticationProcessor().authenticate(m_userAdmin, m_servletRequest);
+        assert result == null : "Expected no result!";
+    }
+
+    /**
+     * Tests that a known user will not yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateKnownUserYieldsValidResult() {
+        when(m_servletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn(createAuthHeaderValue("bob:secret"));
+        
+        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 BasicHttpAuthenticationProcessor().authenticate(m_userAdmin, m_servletRequest);
+        assert result != null : "Expected a valid user to be returned!";
+        
+        assert "bob".equals(user.getName()) : "Expected user bob to be returned!";
+    }
+
+    /**
+     * Tests that a non Base64 authentication header will yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateNonBase64AuthenticationHeaderYieldsNull() {
+        when(m_servletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn("foo");
+        
+        User result = new BasicHttpAuthenticationProcessor().authenticate(m_userAdmin, m_servletRequest);
+        assert result == null : "Expected no result!";
+    }
+
+    /**
+     * Tests that a class cast exception is thrown for invalid context when calling authenticate.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ClassCastException.class)
+    public void testAuthenticateThrowsClassCastForInvalidContext() {
+        new BasicHttpAuthenticationProcessor().authenticate(m_userAdmin, new Object());
+    }
+
+    /**
+     * Tests that an unknown user will yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateUnknownUserYieldsNull() {
+        when(m_servletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn(createAuthHeaderValue("alice:secret"));
+        
+        User result = new BasicHttpAuthenticationProcessor().authenticate(m_userAdmin, m_servletRequest);
+        assert result == null : "Expected no result!";
+    }
+
+    /**
+     * Tests that canHandle yields false for any object other than {@link HttpServletRequest}.
+     */
+    @Test(groups = { UNIT })
+    public void testCanHandleDoesAcceptServletRequest() {
+        assert new BasicHttpAuthenticationProcessor().canHandle(mock(HttpServletRequest.class));
+    }
+
+    /**
+     * Tests that canHandle throws an {@link IllegalArgumentException} for an empty context.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = IllegalArgumentException.class)
+    public void testCanHandleDoesNotAcceptEmptyArray() {
+        new BasicHttpAuthenticationProcessor().canHandle(new Object[0]);
+    }
+
+    /**
+     * Tests that canHandle throws an {@link IllegalArgumentException} for a null context.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = IllegalArgumentException.class)
+    public void testCanHandleDoesNotAcceptNull() {
+        new BasicHttpAuthenticationProcessor().canHandle((Object[]) null);
+    }
+    
+    /**
+     * Tests that canHandle yields false for any object other than {@link HttpServletRequest}.
+     */
+    @Test(groups = { UNIT })
+    public void testCanHandleDoesNotAcceptUnhandledContext() {
+        assert new BasicHttpAuthenticationProcessor().canHandle(new Object()) == false;
+    }
+    
+    /**
+     * Tests that updated throws an exception for missing "key.username" property. 
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptEmptyKeyUsername() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_KEY_USERNAME, "");
+        props.put(PROPERTY_KEY_PASSWORD, "foo");
+        
+        new BasicHttpAuthenticationProcessor().updated(props);
+    }
+    
+    /**
+     * Tests that updated throws an exception for missing "key.username" property. 
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptMissingKeyUsername() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_KEY_PASSWORD, "foo");
+        
+        new BasicHttpAuthenticationProcessor().updated(props);
+    }
+    
+    /**
+     * Tests that updated throws an exception for missing "key.password" property. 
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptMissingKeyPassword() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_KEY_USERNAME, "foo");
+        
+        new BasicHttpAuthenticationProcessor().updated(props);
+    }
+    
+    /**
+     * Tests that updated throws an exception for missing "key.password" property. 
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptEmptyKeyPassword() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_KEY_USERNAME, "foo");
+        props.put(PROPERTY_KEY_PASSWORD, "");
+        
+        new BasicHttpAuthenticationProcessor().updated(props);
+    }
+    
+    /**
+     * Tests that updated does not throw an exception for a correct configuration.
+     */
+    @Test(groups = { UNIT })
+    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);
+        
+        BasicHttpAuthenticationProcessor processor = new BasicHttpAuthenticationProcessor();
+
+        processor.updated(props);
+        
+        // Test whether we can use the new properties...
+        when(m_servletRequest.getHeader(AUTHORIZATION_HEADER)).thenReturn(createAuthHeaderValue("bob:secret"));
+        
+        User user = mock(User.class);
+        when(user.getName()).thenReturn("bob");
+        when(user.hasCredential(eq(keyPassword), eq("secret"))).thenReturn(Boolean.TRUE);
+
+        when(m_userAdmin.getUser(eq(keyUsername), eq("bob"))).thenReturn(user);
+
+        User result = processor.authenticate(m_userAdmin, m_servletRequest);
+        assert result != null : "Expected a valid user to be returned!";
+        
+        assert "bob".equals(user.getName()) : "Expected user bob to be returned!";
+    }
+
+
+    /**
+     * @return the basic authentication header, never <code>null</code>.
+     */
+    private String createAuthHeaderValue(String credentials) {
+        return "Basic " + new Base64().encodeToString(credentials.getBytes());
+    }
+}

Added: ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/clientcert/ClientCertAuthenticationProcessorTest.java
URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/clientcert/ClientCertAuthenticationProcessorTest.java?rev=1463507&view=auto
==============================================================================
--- ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/clientcert/ClientCertAuthenticationProcessorTest.java (added)
+++ ace/trunk/org.apache.ace.authentication/test/org/apache/ace/authentication/processor/clientcert/ClientCertAuthenticationProcessorTest.java Tue Apr  2 12:41:42 2013
@@ -0,0 +1,499 @@
+/*
+ * 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.authentication.processor.clientcert;
+
+import static org.apache.ace.authentication.processor.clientcert.ClientCertAuthenticationProcessor.ATTRIBUTE_CIPHER_SUITE;
+import static org.apache.ace.authentication.processor.clientcert.ClientCertAuthenticationProcessor.ATTRIBUTE_X509_CERTIFICATE;
+import static org.apache.ace.authentication.processor.clientcert.ClientCertAuthenticationProcessor.PROPERTY_USERNAME_LOOKUPKEY;
+import static org.apache.ace.authentication.processor.clientcert.ClientCertAuthenticationProcessor.PROPERTY_USERNAME_MATCH_POLICY;
+import static org.apache.ace.authentication.processor.clientcert.ClientCertAuthenticationProcessor.PROPERTY_VERIFY_CERT_VALIDITY;
+import static org.apache.ace.test.utils.TestUtils.UNIT;
+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.security.auth.x500.X500Principal;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.ace.authentication.processor.clientcert.ClientCertAuthenticationProcessor;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.log.LogService;
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ * 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(alwaysRun = true)
+    public static void init() {
+        m_keystore = new MemoryKeyStore("cn=testCA", dayBeforeYesterday(), dayAfterTomorrow());
+    }
+
+    /**
+     * Set up for each individual test.
+     */
+    @BeforeMethod(alwaysRun = true)
+    public void setUp() {
+        m_log = mock(LogService.class);
+
+        m_userAdmin = mock(UserAdmin.class);
+        m_servletRequest = mock(HttpServletRequest.class);
+
+        when(m_servletRequest.getAttribute(ATTRIBUTE_CIPHER_SUITE)).thenReturn("bogus-cipher-suite");
+    }
+
+    /**
+     * Tests that a null certificate chain will yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateNoCertificateChainYieldsNull() {
+        User result = createAuthorizationProcessor().authenticate(m_userAdmin, m_servletRequest);
+        assert result == null : "Did not expect a valid user to be returned!";
+    }
+
+    /**
+     * Tests that an empty certificate chain will yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateEmptyCertificateChainYieldsNull() {
+        when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(new X509Certificate[0]);
+
+        User result = createAuthorizationProcessor().authenticate(m_userAdmin, m_servletRequest);
+        assert result == null : "Did not expect a valid user to be returned!";
+    }
+
+    /**
+     * Tests that authenticating a known user with an invalid (expired) certificate will yield null.
+     */
+    @Test(groups = { UNIT })
+    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);
+        assert result == null : "Did not expect a valid user to be returned!";
+    }
+
+    /**
+     * Tests that authenticating a known user with an invalid (not valid) certificate will yield null.
+     */
+    @Test(groups = { UNIT })
+    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);
+        assert result == null : "Did not expect a valid user to be returned!";
+    }
+
+    /**
+     * Tests that authenticating a known user with a valid certificate will not yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateKnownUserYieldsValidResult() {
+        X509Certificate[] certChain = createValidCertificateChain("bob");
+
+        when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(certChain);
+
+        User user = mock(User.class);
+        when(user.getName()).thenReturn("bob");
+
+        when(m_userAdmin.getUser(eq("username"), eq("bob"))).thenReturn(user);
+
+        User result = createAuthorizationProcessor().authenticate(m_userAdmin, m_servletRequest);
+        assert result != null : "Expected a valid user to be returned!";
+
+        assert "bob".equals(user.getName()) : "Expected bob to be returned as user!";
+    }
+
+    /**
+     * Tests that authenticating a known user with a valid certificate chain will not yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateKnownUserWithValidCertificateChainYieldsValidResult() throws ConfigurationException {
+        ClientCertAuthenticationProcessor processor = createAuthorizationProcessor();
+
+        final String lookupKey = "anyKey";
+        final String matchPolicy = "dn";
+
+        Properties props = new Properties();
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, lookupKey);
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, matchPolicy);
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
+        processor.updated(props);
+
+        X509Certificate[] certChain = createValidCertificateChainWithDN("cn=Alice,dc=acme,dc=corp", "cn=Fido,ou=dev,dc=acme,dc=corp", "cn=Bob,ou=dev,dc=acme,dc=corp");
+
+        when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(certChain);
+
+        User user = mock(User.class);
+        when(user.getName()).thenReturn("bob");
+
+        when(m_userAdmin.getUser(eq(lookupKey), eq("CN=Bob,OU=dev,DC=acme,DC=corp"))).thenReturn(user);
+
+        User result = processor.authenticate(m_userAdmin, m_servletRequest);
+        assert result != null : "Expected a valid user to be returned!";
+
+        assert "bob".equals(user.getName()) : "Expected bob to be returned as user!";
+    }
+
+    /**
+     * Tests that a missing cipher suite header will the authenticate method to yield null.
+     */
+    @Test(groups = { UNIT })
+    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);
+        assert result == null : "Did not expect a valid user to be returned!";
+    }
+
+    /**
+     * Tests that a class cast exception is thrown for invalid context when calling authenticate.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ClassCastException.class)
+    public void testAuthenticateThrowsClassCastForInvalidContext() {
+        createAuthorizationProcessor().authenticate(m_userAdmin, new Object());
+    }
+
+    /**
+     * Tests that an unknown user will yield null.
+     */
+    @Test(groups = { UNIT })
+    public void testAuthenticateUnknownUserYieldsNull() {
+        when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(createValidCertificateChain("bob"));
+
+        User result = createAuthorizationProcessor().authenticate(m_userAdmin, m_servletRequest);
+        assert result == null : "Did not expect a valid user to be returned!";
+    }
+
+    /**
+     * Tests that canHandle yields false for any object other than {@link HttpServletRequest}.
+     */
+    @Test(groups = { UNIT })
+    public void testCanHandleDoesAcceptServletRequest() {
+        when(m_servletRequest.getAttribute(ATTRIBUTE_X509_CERTIFICATE)).thenReturn(createValidCertificateChain("alice"));
+
+        assert createAuthorizationProcessor().canHandle(m_servletRequest);
+    }
+
+    /**
+     * Tests that canHandle throws an {@link IllegalArgumentException} for an empty context.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = IllegalArgumentException.class)
+    public void testCanHandleDoesNotAcceptEmptyArray() {
+        createAuthorizationProcessor().canHandle(new Object[0]);
+    }
+
+    /**
+     * Tests that canHandle throws an {@link IllegalArgumentException} for a null context.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = IllegalArgumentException.class)
+    public void testCanHandleDoesNotAcceptNull() {
+        createAuthorizationProcessor().canHandle((Object[]) null);
+    }
+
+    /**
+     * Tests that canHandle yields false for any object other than {@link HttpServletRequest}.
+     */
+    @Test(groups = { UNIT })
+    public void testCanHandleDoesNotAcceptUnhandledContext() {
+        assert createAuthorizationProcessor().canHandle(new Object()) == false;
+    }
+
+    /**
+     * Tests that updated does not throw an exception for a correct configuration.
+     */
+    @Test(groups = { UNIT })
+    public void testUpdatedDoesAcceptCorrectProperties() throws ConfigurationException {
+        final String lookupKey = "anyKey";
+        final String matchPolicy = "cn";
+
+        Properties props = new Properties();
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, lookupKey);
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, matchPolicy);
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
+
+        ClientCertAuthenticationProcessor processor = createAuthorizationProcessor();
+
+        processor.updated(props);
+
+        X509Certificate[] certificateChain = createValidCertificateChain("alice");
+
+        // 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("alice");
+
+        when(m_userAdmin.getUser(eq(lookupKey), eq("alice"))).thenReturn(user);
+
+        User result = processor.authenticate(m_userAdmin, m_servletRequest);
+        assert result != null : "Expected a valid user to be returned!";
+
+        assert "alice".equals(user.getName()) : "Expected alice to be returned as user!";
+    }
+
+    /**
+     * Tests that updated throws an exception for missing "username match policy" property.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptEmptyMatchPolicy() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, "foo");
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, "");
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
+
+        createAuthorizationProcessor().updated(props);
+    }
+
+    /**
+     * Tests that updated throws an exception for missing "username lookup key" property.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptEmptyLookupKey() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, "");
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, "foo");
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
+
+        createAuthorizationProcessor().updated(props);
+    }
+
+    /**
+     * Tests that updated throws an exception for missing "verify cert validity" property.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptEmptyVerifyCertValidity() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, "foo");
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, "bar");
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "");
+
+        createAuthorizationProcessor().updated(props);
+    }
+
+    /**
+     * Tests that updated throws an exception for missing "username match policy" property.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptMissingMatchPolicy() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, "foo");
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
+
+        createAuthorizationProcessor().updated(props);
+    }
+
+    /**
+     * Tests that updated throws an exception for missing "user name lookup key" property.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptMissingUsernameLookupKey() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, "foo");
+        props.put(PROPERTY_VERIFY_CERT_VALIDITY, "true");
+
+        createAuthorizationProcessor().updated(props);
+    }
+
+    /**
+     * Tests that updated throws an exception for missing "verify cert validity" property.
+     */
+    @Test(groups = { UNIT }, expectedExceptions = ConfigurationException.class)
+    public void testUpdatedDoesNotAcceptMissingVerifyCertValidity() throws ConfigurationException {
+        Properties props = new Properties();
+        props.put(PROPERTY_USERNAME_LOOKUPKEY, "foo");
+        props.put(PROPERTY_USERNAME_MATCH_POLICY, "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) chain with certificate(s) valid from yesterday until tomorrow.
+     * 
+     * @param dns the distinguished names of the certificates in the returned chain.
+     * @return a new chain with {@link X509Certificate}s, never <code>null</code>.
+     */
+    private X509Certificate[] createValidCertificateChainWithDN(String... dns) {
+        X509Certificate[] result = new X509Certificate[dns.length];
+        
+        X500Principal signerDN = m_keystore.getCA_DN();
+        KeyPair signerKeyPair = m_keystore.getCA_KeyPair();
+
+        for (int i = 0; i < result.length; i++) {
+            KeyPair certKeyPair = m_keystore.generateKeyPair();
+            
+            String alias = String.format("alias%d", i);
+            String dn = dns[i];
+            int idx = result.length - i - 1;
+            
+            result[idx] = m_keystore.createCertificate(signerDN, signerKeyPair.getPrivate(), alias, dn, yesterday(), tomorrow(), certKeyPair.getPublic());
+            
+            signerDN = result[idx].getSubjectX500Principal();
+            signerKeyPair = certKeyPair;
+        }
+        return result;
+    }
+
+    /**
+     * 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;
+    }
+}