cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cschnei...@apache.org
Subject git commit: CXF-5118 Add cert based auth to JaasLoginInterceptor
Date Mon, 11 Aug 2014 11:15:07 GMT
Repository: cxf
Updated Branches:
  refs/heads/master bb1edc5dd -> 9344b093b


CXF-5118 Add cert based auth to JaasLoginInterceptor


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/9344b093
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/9344b093
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/9344b093

Branch: refs/heads/master
Commit: 9344b093b8081a68c9f83f0c740d60508e99fd5e
Parents: bb1edc5
Author: Christian Schneider <chris@die-schneider.net>
Authored: Mon Aug 4 22:12:42 2014 +0200
Committer: Christian Schneider <chris@die-schneider.net>
Committed: Mon Aug 11 13:14:22 2014 +0200

----------------------------------------------------------------------
 .../security/JAASLoginInterceptor.java          |  93 +++++++----
 .../callback/CallbackHandlerProvider.java       |  32 ++++
 .../CallbackHandlerProviderAuthPol.java         |  38 +++++
 .../CallbackHandlerProviderUsernameToken.java   |  40 +++++
 .../callback/CallbackHandlerTlsCert.java        |  95 +++++++++++
 .../callback/CertKeyToUserNameMapper.java       |  68 ++++++++
 .../callback/CertificateToNameMapper.java       |  28 ++++
 .../security/callback/NameToPasswordMapper.java |  26 +++
 .../security/JAASLoginInterceptorTest.java      | 158 +++++++++++++++++++
 .../security/TestUserPasswordLoginModule.java   |  91 +++++++++++
 10 files changed, 638 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/9344b093/core/src/main/java/org/apache/cxf/interceptor/security/JAASLoginInterceptor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/interceptor/security/JAASLoginInterceptor.java
b/core/src/main/java/org/apache/cxf/interceptor/security/JAASLoginInterceptor.java
index 24c7bf2..42a4c64 100644
--- a/core/src/main/java/org/apache/cxf/interceptor/security/JAASLoginInterceptor.java
+++ b/core/src/main/java/org/apache/cxf/interceptor/security/JAASLoginInterceptor.java
@@ -19,21 +19,24 @@
 package org.apache.cxf.interceptor.security;
 
 import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.logging.Logger;
 
 import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
 import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
 import javax.security.auth.login.Configuration;
 import javax.security.auth.login.LoginContext;
 import javax.security.auth.login.LoginException;
 
 import org.apache.cxf.common.logging.LogUtils;
-import org.apache.cxf.common.security.SecurityToken;
-import org.apache.cxf.common.security.TokenType;
-import org.apache.cxf.common.security.UsernameToken;
-import org.apache.cxf.configuration.security.AuthorizationPolicy;
 import org.apache.cxf.interceptor.Fault;
 import org.apache.cxf.interceptor.InterceptorChain;
+import org.apache.cxf.interceptor.security.callback.CallbackHandlerProvider;
+import org.apache.cxf.interceptor.security.callback.CallbackHandlerProviderAuthPol;
+import org.apache.cxf.interceptor.security.callback.CallbackHandlerProviderUsernameToken;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.phase.AbstractPhaseInterceptor;
 import org.apache.cxf.phase.Phase;
@@ -51,14 +54,18 @@ public class JAASLoginInterceptor extends AbstractPhaseInterceptor<Message>
{
     private String roleClassifierType = ROLE_CLASSIFIER_PREFIX;
     private boolean reportFault;
     private boolean useDoAs = true;
-    
+    private List<CallbackHandlerProvider> callbackHandlerProviders;
+    private boolean allowAnonymous = true;
     
     public JAASLoginInterceptor() {
-        super(Phase.UNMARSHAL);
+        this(Phase.UNMARSHAL);
     }
     
     public JAASLoginInterceptor(String phase) {
         super(phase);
+        this.callbackHandlerProviders = new ArrayList<CallbackHandlerProvider>();
+        this.callbackHandlerProviders.add(new CallbackHandlerProviderAuthPol());
+        this.callbackHandlerProviders.add(new CallbackHandlerProviderUsernameToken());
     }
     
     public void setContextName(String name) {
@@ -101,37 +108,29 @@ public class JAASLoginInterceptor extends AbstractPhaseInterceptor<Message>
{
     public void setUseDoAs(boolean useDoAs) {
         this.useDoAs = useDoAs;
     }
-
-    public void handleMessage(final Message message) throws Fault {
-
-        String name = null;
-        String password = null;
-        
-        AuthorizationPolicy policy = message.get(AuthorizationPolicy.class);
-        if (policy != null) {
-            name = policy.getUserName();
-            password = policy.getPassword();
-        } else {
-            // try the UsernameToken
-            SecurityToken token = message.get(SecurityToken.class);
-            if (token != null && token.getTokenType() == TokenType.UsernameToken)
{
-                UsernameToken ut = (UsernameToken)token;
-                name = ut.getName();
-                password = ut.getPassword();
+    
+    private CallbackHandler getFirstCallbackHandler(Message message) {
+        for (CallbackHandlerProvider cbp : callbackHandlerProviders) {
+            CallbackHandler cbh = cbp.create(message);
+            if (cbh != null) {
+                return cbh;
             }
         }
+        return null;
+    }
 
-        if (name == null || password == null) {
-            throw new AuthenticationException("Authentication required but no user or password
was supplied");
+    public void handleMessage(final Message message) throws Fault {
+        CallbackHandler handler = getFirstCallbackHandler(message);
+
+        if (handler == null && !allowAnonymous) {
+            throw new AuthenticationException("Authentication required but no authentication
information was supplied");
         }
 
         try {
-            CallbackHandler handler = getCallbackHandler(name, password);  
             LoginContext ctx = new LoginContext(getContextName(), null, handler, loginConfig);
 
-            
             ctx.login();
-            
             Subject subject = ctx.getSubject();
+            String name = getUsername(handler);
             message.put(SecurityContext.class, createSecurityContext(name, subject));
             
             // Run the further chain in the context of this subject.
@@ -151,16 +150,32 @@ public class JAASLoginInterceptor extends AbstractPhaseInterceptor<Message>
{
             }
 
         } catch (LoginException ex) {
-            String errorMessage = "Authentication failed for user " + name + " : " + ex.getMessage();
+            String errorMessage = "Authentication failed: " + ex.getMessage();
             LOG.fine(errorMessage);
             if (reportFault) {
-                throw new AuthenticationException(errorMessage);
+                AuthenticationException aex = new AuthenticationException(errorMessage);
+                aex.initCause(ex);
+                throw aex;
+                
             } else {
                 throw new AuthenticationException("Authentication failed (details can be
found in server log)");
             }
         }
     }
 
+    private String getUsername(CallbackHandler handler) {
+        if (handler == null) {
+            return null;
+        }
+        try {
+            NameCallback usernameCallBack = new NameCallback("user");
+            handler.handle(new Callback[]{usernameCallBack });
+            return usernameCallBack.getName();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
     protected CallbackHandler getCallbackHandler(String name, String password) {
         return new NamePasswordCallbackHandler(name, password);
     }
@@ -181,6 +196,22 @@ public class JAASLoginInterceptor extends AbstractPhaseInterceptor<Message>
{
     public void setLoginConfig(Configuration loginConfig) {
         this.loginConfig = loginConfig;
     }
+
+    public List<CallbackHandlerProvider> getCallbackHandlerProviders() {
+        return callbackHandlerProviders;
+    }
+
+    public void setCallbackHandlerProviders(List<CallbackHandlerProvider> callbackHandlerProviders)
{
+        this.callbackHandlerProviders.clear();
+        this.callbackHandlerProviders.addAll(callbackHandlerProviders);
+    }
     
-    
+    public void addCallbackHandlerProviders(List<CallbackHandlerProvider> callbackHandlerProviders2)
{
+        this.callbackHandlerProviders.addAll(callbackHandlerProviders2);
+    }
+
+    public void setAllowAnonymous(boolean allowAnonymous) {
+        this.allowAnonymous = allowAnonymous;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/9344b093/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProvider.java
b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProvider.java
new file mode 100644
index 0000000..3b0fca8
--- /dev/null
+++ b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProvider.java
@@ -0,0 +1,32 @@
+/**
+ * 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.cxf.interceptor.security.callback;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.apache.cxf.message.Message;
+
+/**
+ * Create a suitable CallbackHandler for the given message.
+ * For example if the message contains username and password the Callbackhandler
+ * should supply these.
+ */
+public interface CallbackHandlerProvider {
+    CallbackHandler create(Message message);
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/9344b093/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProviderAuthPol.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProviderAuthPol.java
b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProviderAuthPol.java
new file mode 100644
index 0000000..38cf449
--- /dev/null
+++ b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProviderAuthPol.java
@@ -0,0 +1,38 @@
+/**
+ * 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.cxf.interceptor.security.callback;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.apache.cxf.configuration.security.AuthorizationPolicy;
+import org.apache.cxf.interceptor.security.NamePasswordCallbackHandler;
+import org.apache.cxf.message.Message;
+
+public class CallbackHandlerProviderAuthPol implements CallbackHandlerProvider {
+
+    @Override
+    public CallbackHandler create(Message message) {
+        AuthorizationPolicy policy = message.get(AuthorizationPolicy.class);
+        if (policy == null) {
+            return null;
+        }
+        return new NamePasswordCallbackHandler(policy.getUserName(), policy.getPassword());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/9344b093/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProviderUsernameToken.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProviderUsernameToken.java
b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProviderUsernameToken.java
new file mode 100644
index 0000000..0534c83
--- /dev/null
+++ b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerProviderUsernameToken.java
@@ -0,0 +1,40 @@
+/**
+ * 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.cxf.interceptor.security.callback;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.apache.cxf.common.security.SecurityToken;
+import org.apache.cxf.common.security.UsernameToken;
+import org.apache.cxf.interceptor.security.NamePasswordCallbackHandler;
+import org.apache.cxf.message.Message;
+
+public class CallbackHandlerProviderUsernameToken implements CallbackHandlerProvider {
+
+    @Override
+    public CallbackHandler create(Message message) {
+        SecurityToken token = message.get(SecurityToken.class);
+        if (!(token instanceof UsernameToken)) {
+            return null;
+        }
+        UsernameToken ut = (UsernameToken)token;
+        return new NamePasswordCallbackHandler(ut.getName(), ut.getPassword());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/9344b093/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerTlsCert.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerTlsCert.java
b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerTlsCert.java
new file mode 100644
index 0000000..29c18ab
--- /dev/null
+++ b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CallbackHandlerTlsCert.java
@@ -0,0 +1,95 @@
+/**
+ * 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.cxf.interceptor.security.callback;
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.apache.cxf.interceptor.security.NamePasswordCallbackHandler;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.security.transport.TLSSessionInfo;
+
+public class CallbackHandlerTlsCert implements CallbackHandlerProvider {
+    private CertificateToNameMapper certMapper;
+    private NameToPasswordMapper nameToPasswordMapper;
+    private String fixedPassword;
+    
+    public CallbackHandlerTlsCert() {
+        // By default use subjectDN as userName
+        this.certMapper = new CertificateToNameMapper() {
+            public String getUserName(Certificate cert) {
+                return ((X509Certificate)cert).getSubjectDN().getName();
+            }
+        };
+        // By default use fixed password 
+        this.nameToPasswordMapper = new NameToPasswordMapper() {
+            public String getPassword(String userName) {
+                return fixedPassword;
+            }
+        };
+    }
+
+    @Override
+    public CallbackHandler create(Message message) {
+        TLSSessionInfo tlsSession = message.get(TLSSessionInfo.class);
+        if (tlsSession == null) {
+            return null;
+        }
+        Certificate cert = getCertificate(message);
+        String name = certMapper.getUserName(cert);
+        String password = nameToPasswordMapper.getPassword(name);
+        return new NamePasswordCallbackHandler(name, password);
+    }
+    
+    /**
+     * Extracts certificate from message, expecting to find TLSSessionInfo inside.
+     *
+     * @param message
+     * @return
+     */
+    private Certificate getCertificate(Message message) {
+        TLSSessionInfo tlsSessionInfo = message.get(TLSSessionInfo.class);
+        if (tlsSessionInfo == null) {
+            throw new SecurityException("Not TLS connection");
+        }
+
+        Certificate[] certificates = tlsSessionInfo.getPeerCertificates();
+        if (certificates == null || certificates.length == 0) {
+            throw new SecurityException("No certificate found");
+        }
+
+        // Due to RFC5246, senders certificates always comes 1st
+        return certificates[0];
+    }
+    
+    public void setCertMapper(CertificateToNameMapper certMapper) {
+        this.certMapper = certMapper;
+    }
+
+    public void setFixedPassword(String fixedPassword) {
+        this.fixedPassword = fixedPassword;
+    }
+
+    public void setNameToPasswordMapper(NameToPasswordMapper nameToPasswordMapper) {
+        this.nameToPasswordMapper = nameToPasswordMapper;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/9344b093/core/src/main/java/org/apache/cxf/interceptor/security/callback/CertKeyToUserNameMapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/interceptor/security/callback/CertKeyToUserNameMapper.java
b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CertKeyToUserNameMapper.java
new file mode 100644
index 0000000..e8159b6
--- /dev/null
+++ b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CertKeyToUserNameMapper.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.interceptor.security.callback;
+
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+
+import javax.naming.InvalidNameException;
+import javax.naming.ldap.LdapName;
+import javax.naming.ldap.Rdn;
+
+public class CertKeyToUserNameMapper implements CertificateToNameMapper {
+    private String key;
+    
+    /**
+     * Returns Subject DN from X509Certificate
+     *
+     * @param certificate
+     * @return Subject DN as a user name
+     */
+    @Override
+    public String getUserName(Certificate cert) {
+        X509Certificate certificate = (X509Certificate) cert;
+        String dn = certificate.getSubjectDN().getName();
+        LdapName ldapDn = getLdapName(dn);
+
+        if (key == null) {
+            throw new IllegalArgumentException("Must set a key");
+        }
+
+        for (Rdn rdn : ldapDn.getRdns()) {
+            if (key.equalsIgnoreCase(rdn.getType())) {
+                return (String)rdn.getValue();
+            }
+        }
+
+        throw new IllegalArgumentException("No " + key + " key found in certificate DN: "
+ dn);
+    }
+
+    private LdapName getLdapName(String dn) {
+        try {
+            return new LdapName(dn);
+        } catch (InvalidNameException e) {
+            throw new IllegalArgumentException("Invalid DN", e);
+        }
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/9344b093/core/src/main/java/org/apache/cxf/interceptor/security/callback/CertificateToNameMapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/interceptor/security/callback/CertificateToNameMapper.java
b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CertificateToNameMapper.java
new file mode 100644
index 0000000..9738cd5
--- /dev/null
+++ b/core/src/main/java/org/apache/cxf/interceptor/security/callback/CertificateToNameMapper.java
@@ -0,0 +1,28 @@
+/**
+ * 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.cxf.interceptor.security.callback;
+
+import java.security.cert.Certificate;
+
+/**
+ * Map a certificate to a user name for the jaas login
+ */
+public interface CertificateToNameMapper {
+    String getUserName(Certificate cert);
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/9344b093/core/src/main/java/org/apache/cxf/interceptor/security/callback/NameToPasswordMapper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/interceptor/security/callback/NameToPasswordMapper.java
b/core/src/main/java/org/apache/cxf/interceptor/security/callback/NameToPasswordMapper.java
new file mode 100644
index 0000000..aee77e5
--- /dev/null
+++ b/core/src/main/java/org/apache/cxf/interceptor/security/callback/NameToPasswordMapper.java
@@ -0,0 +1,26 @@
+/**
+ * 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.cxf.interceptor.security.callback;
+
+/**
+ * Supply a password for a userName to get roles from LoginModule
+ */
+public interface NameToPasswordMapper {
+    String getPassword(String userName);
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/9344b093/core/src/test/java/org/apache/cxf/interceptor/security/JAASLoginInterceptorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/cxf/interceptor/security/JAASLoginInterceptorTest.java
b/core/src/test/java/org/apache/cxf/interceptor/security/JAASLoginInterceptorTest.java
new file mode 100644
index 0000000..462efc1
--- /dev/null
+++ b/core/src/test/java/org/apache/cxf/interceptor/security/JAASLoginInterceptorTest.java
@@ -0,0 +1,158 @@
+/**
+ * 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.cxf.interceptor.security;
+
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
+import javax.security.auth.login.Configuration;
+
+import org.apache.cxf.common.security.SecurityToken;
+import org.apache.cxf.common.security.UsernameToken;
+import org.apache.cxf.configuration.security.AuthorizationPolicy;
+import org.apache.cxf.interceptor.security.callback.CallbackHandlerProvider;
+import org.apache.cxf.interceptor.security.callback.CallbackHandlerTlsCert;
+import org.apache.cxf.interceptor.security.callback.CertKeyToUserNameMapper;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.security.transport.TLSSessionInfo;
+import org.easymock.EasyMock;
+import org.easymock.IMocksControl;
+import org.junit.Test;
+
+public class JAASLoginInterceptorTest {
+
+    private static final String TEST_SUBJECT_DN = "CN=" + TestUserPasswordLoginModule.TESTUSER
+                                                  + ", o=Test Org";
+
+    /**
+     * Using default CallbackhandlerProviders and no authentication information
+     */
+    @Test(expected = AuthenticationException.class)
+    public void testLoginWithDefaultHandler() {
+        JAASLoginInterceptor jaasInt = createTestJaasLoginInterceptor();
+        Message message = new MessageImpl();
+        jaasInt.handleMessage(message);
+    }
+
+    /**
+     * Using default CallbackhandlerProviders and AuthPolicy
+     */
+    @Test
+    public void testLoginWithDefaultHandlerAndAuthPol() {
+        JAASLoginInterceptor jaasInt = createTestJaasLoginInterceptor();
+        Message message = new MessageImpl();
+        addAuthPolicy(message, TestUserPasswordLoginModule.TESTUSER, TestUserPasswordLoginModule.TESTPASS);
+        jaasInt.handleMessage(message);
+    }
+
+    @Test(expected = AuthenticationException.class)
+    public void testLoginWithDefaultHandlerAndAuthPolWrongPass() {
+        JAASLoginInterceptor jaasInt = createTestJaasLoginInterceptor();
+        Message message = new MessageImpl();
+        addAuthPolicy(message, TestUserPasswordLoginModule.TESTUSER, "wrong");
+        jaasInt.handleMessage(message);
+    }
+
+    /**
+     * Using default CallbackhandlerProviders and UserNameToken
+     */
+    @Test
+    public void testLoginWithDefaultHandlerAndUsernameToken() {
+        JAASLoginInterceptor jaasInt = createTestJaasLoginInterceptor();
+        Message message = new MessageImpl();
+        addUsernameToken(message, TestUserPasswordLoginModule.TESTUSER, TestUserPasswordLoginModule.TESTPASS);
+        jaasInt.handleMessage(message);
+    }
+
+    @Test(expected = AuthenticationException.class)
+    public void testLoginWithDefaultHandlerAndUsernameTokenWrongPass() {
+        JAASLoginInterceptor jaasInt = createTestJaasLoginInterceptor();
+        Message message = new MessageImpl();
+        addUsernameToken(message, TestUserPasswordLoginModule.TESTUSER, "wrong");
+        jaasInt.handleMessage(message);
+    }
+
+    @Test
+    public void testLoginWithTlsHandler() {
+        JAASLoginInterceptor jaasInt = createTestJaasLoginInterceptor();
+        CallbackHandlerTlsCert tlsHandler = new CallbackHandlerTlsCert();
+        tlsHandler.setFixedPassword(TestUserPasswordLoginModule.TESTPASS);
+        CertKeyToUserNameMapper certMapper = new CertKeyToUserNameMapper();
+        certMapper.setKey("CN");
+        tlsHandler.setCertMapper(certMapper);
+        jaasInt.setCallbackHandlerProviders(Collections.singletonList((CallbackHandlerProvider)tlsHandler));
+        Message message = new MessageImpl();
+        TLSSessionInfo sessionInfo = new TLSSessionInfo("", null, new Certificate[] {
+            createTestCert(TEST_SUBJECT_DN)
+        });
+        message.put(TLSSessionInfo.class, sessionInfo);
+
+        jaasInt.handleMessage(message);
+    }
+
+    private X509Certificate createTestCert(String subjectDn) {
+        IMocksControl c = EasyMock.createControl();
+        X509Certificate cert = c.createMock(X509Certificate.class);
+        Principal principal = c.createMock(Principal.class);
+        EasyMock.expect(principal.getName()).andReturn(subjectDn);
+        EasyMock.expect(cert.getSubjectDN()).andReturn(principal);
+        c.replay();
+        return cert;
+    }
+
+    private void addAuthPolicy(Message message, String username, String password) {
+        AuthorizationPolicy authPol = new AuthorizationPolicy();
+        authPol.setUserName(username);
+        authPol.setPassword(password);
+        message.put(AuthorizationPolicy.class, authPol);
+    }
+
+    private void addUsernameToken(Message message, String username, String password) {
+        UsernameToken token = new UsernameToken(username, password, "", false, null, "");
+        message.put(SecurityToken.class, token);
+    }
+
+    private JAASLoginInterceptor createTestJaasLoginInterceptor() {
+        JAASLoginInterceptor jaasInt = new JAASLoginInterceptor();
+        jaasInt.setReportFault(true);
+        Configuration config = new Configuration() {
+
+            @Override
+            public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+                Map<String, String> options = new HashMap<String, String>();
+                AppConfigurationEntry configEntry = new AppConfigurationEntry(
+                                                                              TestUserPasswordLoginModule.class
+                                                                                  .getName(),
+                                                                              LoginModuleControlFlag.REQUIRED,
+                                                                              options);
+                return Collections.singleton(configEntry).toArray(new AppConfigurationEntry[]
{});
+            }
+        };
+        jaasInt.setLoginConfig(config);
+        return jaasInt;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/9344b093/core/src/test/java/org/apache/cxf/interceptor/security/TestUserPasswordLoginModule.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/cxf/interceptor/security/TestUserPasswordLoginModule.java
b/core/src/test/java/org/apache/cxf/interceptor/security/TestUserPasswordLoginModule.java
new file mode 100644
index 0000000..c5209cb
--- /dev/null
+++ b/core/src/test/java/org/apache/cxf/interceptor/security/TestUserPasswordLoginModule.java
@@ -0,0 +1,91 @@
+/**
+ * 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.cxf.interceptor.security;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+import org.apache.cxf.common.security.SimpleGroup;
+import org.apache.cxf.common.security.SimplePrincipal;
+
+public class TestUserPasswordLoginModule implements LoginModule {
+
+    public static final String TESTGROUP = "testgroup";
+    public static final String TESTPASS = "testpass";
+    public static final String TESTUSER = "testuser";
+    private Subject subject;
+    private CallbackHandler callbackHandler;
+
+    @Override
+    public void initialize(Subject subject2, CallbackHandler callbackHandler2, Map<String,
?> sharedState,
+                           Map<String, ?> options) {
+        this.subject = subject2;
+        this.callbackHandler = callbackHandler2;
+    }
+
+    @Override
+    public boolean login() throws LoginException {
+        NameCallback nameCallback = new NameCallback("User");
+        PasswordCallback passwordCallback = new PasswordCallback("Password", false);
+        Callback[] callbacks = new Callback[] {nameCallback, passwordCallback};
+        try {
+            this.callbackHandler.handle(callbacks);
+        } catch (IOException e) {
+            throw new LoginException(e.getMessage());
+        } catch (UnsupportedCallbackException e) {
+            throw new LoginException(e.getMessage());
+        }
+        String userName = nameCallback.getName();
+        String password = new String(passwordCallback.getPassword());
+        if (!TESTUSER.equals(userName)) {
+            throw new LoginException("wrong username");
+        }
+        if (!TESTPASS.equals(password)) {
+            throw new LoginException("wrong password");
+        }
+        subject.getPrincipals().add(new SimplePrincipal(userName));
+        subject.getPrincipals().add(new SimpleGroup(TESTGROUP));
+        return true;
+    }
+
+    @Override
+    public boolean commit() throws LoginException {
+        return true;
+    }
+
+    @Override
+    public boolean abort() throws LoginException {
+        return false;
+    }
+
+    @Override
+    public boolean logout() throws LoginException {
+        return false;
+    }
+
+}


Mime
View raw message