cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cohei...@apache.org
Subject git commit: Adding support for authenticating to the IdP with a Kerberos ticket
Date Wed, 27 Aug 2014 16:08:43 GMT
Repository: cxf-fediz
Updated Branches:
  refs/heads/1.1.x-fixes e8897bf3c -> 72ba368e9


Adding support for authenticating to the IdP with a Kerberos ticket

Conflicts:
	services/idp/src/main/java/org/apache/cxf/fediz/service/idp/STSAuthenticationProvider.java


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

Branch: refs/heads/1.1.x-fixes
Commit: 72ba368e913910b99d89fb500adc7f3cb2ce586b
Parents: e8897bf
Author: Colm O hEigeartaigh <coheigea@apache.org>
Authored: Tue Aug 26 16:58:04 2014 +0100
Committer: Colm O hEigeartaigh <coheigea@apache.org>
Committed: Wed Aug 27 17:08:31 2014 +0100

----------------------------------------------------------------------
 services/idp/pom.xml                            |   5 +
 .../service/idp/STSAuthenticationProvider.java  | 119 +++++-
 .../KerberosAuthenticationProcessingFilter.java | 199 ++++++++++
 .../idp/kerberos/KerberosEntryPoint.java        |  70 ++++
 .../kerberos/KerberosServiceRequestToken.java   | 139 +++++++
 .../idp/kerberos/KerberosTokenValidator.java    | 175 +++++++++
 .../idp/src/main/webapp/WEB-INF/kerberos.jaas   |   8 +
 .../webapp/WEB-INF/security-config-kerberos.xml |  76 ++++
 .../fediz/integrationtests/KerberosTest.java    | 388 +++++++++++++++++++
 .../tomcat7/src/test/resources/kerberos.jaas    |   8 +
 10 files changed, 1171 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/72ba368e/services/idp/pom.xml
----------------------------------------------------------------------
diff --git a/services/idp/pom.xml b/services/idp/pom.xml
index 44f80a3..1ba7880 100644
--- a/services/idp/pom.xml
+++ b/services/idp/pom.xml
@@ -118,6 +118,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.cxf</groupId>
+            <artifactId>cxf-rt-ws-addr</artifactId>
+            <version>${cxf.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.cxf</groupId>
             <artifactId>cxf-rt-transports-http</artifactId>
             <version>${cxf.version}</version>
         </dependency>

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/72ba368e/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/STSAuthenticationProvider.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/STSAuthenticationProvider.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/STSAuthenticationProvider.java
index 7f7f536..c77126b 100644
--- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/STSAuthenticationProvider.java
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/STSAuthenticationProvider.java
@@ -19,27 +19,38 @@
 package org.apache.cxf.fediz.service.idp;
 
 import java.net.URI;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import javax.security.auth.login.LoginException;
 import javax.xml.namespace.QName;
 
 import org.w3c.dom.Element;
-
 import org.apache.cxf.Bus;
 import org.apache.cxf.BusFactory;
 //import org.apache.cxf.endpoint.Client;
 import org.apache.cxf.fediz.core.Claim;
 import org.apache.cxf.fediz.core.ClaimTypes;
+import org.apache.cxf.fediz.service.idp.kerberos.KerberosServiceRequestToken;
+import org.apache.cxf.fediz.service.idp.kerberos.KerberosTokenValidator;
 //import org.apache.cxf.transport.http.HTTPConduit;
 //import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
 import org.apache.cxf.ws.security.SecurityConstants;
 import org.apache.cxf.ws.security.tokenstore.SecurityToken;
 import org.apache.ws.security.WSConstants;
+import org.apache.ws.security.message.token.KerberosServiceContext;
 import org.apache.ws.security.saml.ext.AssertionWrapper;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
 import org.opensaml.xml.XMLObject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -80,12 +91,13 @@ public class STSAuthenticationProvider implements AuthenticationProvider {
     
     protected Map<String, Object> properties = new HashMap<String, Object>();
     
+    private KerberosTokenValidator kerberosTokenValidator;
+    
     
     @Override
     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
         
         Bus cxfBus = getBus();
-        
         IdpSTSClient sts = new IdpSTSClient(cxfBus);
         sts.setAddressingNamespace("http://www.w3.org/2005/08/addressing");
         if (tokenType != null && tokenType.length() > 0) {
@@ -101,8 +113,44 @@ public class STSAuthenticationProvider implements AuthenticationProvider {
         sts.setEndpointQName(new QName(
                                        HTTP_DOCS_OASIS_OPEN_ORG_WS_SX_WS_TRUST_200512,
                                        wsdlEndpoint));
-        sts.getProperties().put(SecurityConstants.USERNAME, authentication.getName());
-        sts.getProperties().put(SecurityConstants.PASSWORD, (String)authentication.getCredentials());
+        
+        Principal kerberosPrincipal = null;
+        if (authentication instanceof KerberosServiceRequestToken) {
+
+            if (kerberosTokenValidator == null) {
+                LOG.error("KerberosTokenValidator must be configured to support kerberos");
+                return null;
+            }
+            KerberosServiceContext kerberosContext;
+            try {
+                kerberosContext = 
+                    kerberosTokenValidator.validate((KerberosServiceRequestToken)authentication);
+                if (kerberosContext != null) {
+                    GSSCredential delegatedCredential = kerberosContext.getDelegationCredential();
+                    if (delegatedCredential != null) {
+                        sts.getProperties().put(SecurityConstants.DELEGATED_CREDENTIAL, 
+                                                delegatedCredential);
+                        sts.getProperties().put(SecurityConstants.KERBEROS_USE_CREDENTIAL_DELEGATION, "true");
+                    }
+                    kerberosPrincipal = kerberosContext.getPrincipal();
+                }
+            } catch (LoginException ex) {
+                LOG.info("Failed to authenticate user '" + authentication.getName() + "'", ex);
+                return null;
+            } catch (PrivilegedActionException ex) {
+                LOG.info("Failed to authenticate user '" + authentication.getName() + "'", ex);
+                return null;
+            }
+            
+            sts.getProperties().put(SecurityConstants.KERBEROS_JAAS_CONTEXT_NAME, 
+                                    kerberosTokenValidator.getContextName());
+            sts.getProperties().put(SecurityConstants.KERBEROS_SPN,
+                                    kerberosTokenValidator.getServiceName());
+        } else {
+            sts.getProperties().put(SecurityConstants.USERNAME, authentication.getName());
+            sts.getProperties().put(SecurityConstants.PASSWORD, (String)authentication.getCredentials());
+        }
+        
         sts.getProperties().putAll(properties);
            
         if (lifetime != null) {
@@ -140,29 +188,60 @@ public class STSAuthenticationProvider implements AuthenticationProvider {
                 }
             }
             
-            UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(
-                authentication.getName(), authentication.getCredentials(), authorities);
-            
-            STSUserDetails details = new STSUserDetails(authentication.getName(),
-                                                        (String)authentication.getCredentials(),
-                                                        authorities,
-                                                        token);
-            upat.setDetails(details);
+            //Add IDP_LOGIN role to be able to access resource Idp, TrustedIdp, etc.
+            authorities.add(new SimpleGrantedAuthority("ROLE_IDP_LOGIN"));
             
-            if (LOG.isDebugEnabled()) {
-                LOG.debug("[IDP_TOKEN=" + token.getId() + "] provided for user '" + authentication.getName() + "'");
+            if (authentication instanceof KerberosServiceRequestToken) {
+                KerberosServiceRequestToken ksrt = 
+                    new KerberosServiceRequestToken(kerberosPrincipal, authorities, 
+                                                    ((KerberosServiceRequestToken)authentication).getToken());
+                
+                STSUserDetails details = new STSUserDetails(kerberosPrincipal.getName(),
+                                                            "",
+                                                            authorities,
+                                                            token);
+                ksrt.setDetails(details);
+                
+                LOG.debug("[IDP_TOKEN={}] provided for user '{}'", token.getId(), kerberosPrincipal.getName());
+                return ksrt;
+            } else {
+                UsernamePasswordAuthenticationToken upat = new UsernamePasswordAuthenticationToken(
+                    authentication.getName(), authentication.getCredentials(), authorities);
+                
+                STSUserDetails details = new STSUserDetails(authentication.getName(),
+                                                            (String)authentication.getCredentials(),
+                                                            authorities,
+                                                            token);
+                upat.setDetails(details);
+                
+                LOG.debug("[IDP_TOKEN={}] provided for user '{}'", token.getId(), authentication.getName());
+                return upat;
             }
-            return upat;
         } catch (Exception ex) {
             LOG.info("Failed to authenticate user '" + authentication.getName() + "'", ex);
             return null;
         }
         
     }
+    
+    protected GSSContext createGSSContext() throws GSSException {
+        Oid oid = new Oid("1.2.840.113554.1.2.2");
+
+        GSSManager gssManager = GSSManager.getInstance();
+
+        String spn = "bob@service.ws.apache.org";
+        GSSName gssService = gssManager.createName(spn, null);
+
+        return gssManager.createContext(gssService.canonicalize(oid),
+                                        oid, null, GSSContext.DEFAULT_LIFETIME);
+
+    }
+
 
     @Override
     public boolean supports(Class<?> authentication) {
-        return authentication.equals(UsernamePasswordAuthenticationToken.class);
+        return authentication.equals(UsernamePasswordAuthenticationToken.class)
+            || authentication.equals(KerberosServiceRequestToken.class);
     }
     
     public String getWsdlLocation() {
@@ -324,6 +403,14 @@ public class STSAuthenticationProvider implements AuthenticationProvider {
         return properties;
     }
 
+    public KerberosTokenValidator getKerberosTokenValidator() {
+        return kerberosTokenValidator;
+    }
+
+    public void setKerberosTokenValidator(KerberosTokenValidator kerberosTokenValidator) {
+        this.kerberosTokenValidator = kerberosTokenValidator;
+    }
+
 //May be uncommented for debugging    
 //    private void setTimeout(Client client, Long timeout) {
 //        HTTPConduit conduit = (HTTPConduit) client.getConduit();

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/72ba368e/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosAuthenticationProcessingFilter.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosAuthenticationProcessingFilter.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosAuthenticationProcessingFilter.java
new file mode 100644
index 0000000..60173f9
--- /dev/null
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosAuthenticationProcessingFilter.java
@@ -0,0 +1,199 @@
+/**
+ * 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.
+ */
+/*
+ * Copyright 2002-2008 the original author or authors.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cxf.fediz.service.idp.kerberos;
+
+import java.io.IOException;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.authentication.AuthenticationDetailsSource;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.crypto.codec.Base64;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
+import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
+import org.springframework.util.Assert;
+import org.springframework.web.filter.GenericFilterBean;
+/**
+ * Parses the SPNEGO authentication Header, which was generated by the browser
+ * and creates a {@link KerberosServiceRequestToken} out if it. It will then
+ * call the {@link AuthenticationManager}.
+ *
+ * @author Mike Wiesner
+ * @since 1.0
+ * @version $Id$
+ * @see KerberosServiceAuthenticationProvider
+ * @see KerberosEntryPoint
+ */
+public class KerberosAuthenticationProcessingFilter extends GenericFilterBean {
+    private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = 
+        new WebAuthenticationDetailsSource();
+    private AuthenticationManager authenticationManager;
+    private AuthenticationSuccessHandler successHandler;
+    private AuthenticationFailureHandler failureHandler;
+    private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
+    private boolean skipIfAlreadyAuthenticated = true;
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
+     * javax.servlet.ServletResponse, javax.servlet.FilterChain)
+     */
+    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) 
+        throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) req;
+        HttpServletResponse response = (HttpServletResponse) res;
+        if (skipIfAlreadyAuthenticated) {
+            Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
+            if (existingAuth != null && existingAuth.isAuthenticated()
+                && !(existingAuth instanceof AnonymousAuthenticationToken)) {
+                chain.doFilter(request, response);
+                return;
+            }
+        }
+        String header = request.getHeader("Authorization");
+        if ((header != null) && header.startsWith("Negotiate ")) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Received Negotiate Header for request " + request.getRequestURL() + ": " + header);
+            }
+            byte[] base64Token = header.substring(10).getBytes("UTF-8");
+            byte[] kerberosTicket = Base64.decode(base64Token);
+            KerberosServiceRequestToken authenticationRequest = new KerberosServiceRequestToken(kerberosTicket);
+            authenticationRequest.setDetails(authenticationDetailsSource.buildDetails(request));
+            Authentication authentication;
+            try {
+                authentication = authenticationManager.authenticate(authenticationRequest);
+            } catch (AuthenticationException e) {
+                //That shouldn't happen, as it is most likely a wrong
+                //configuration on the server side
+                logger.warn("Negotiate Header was invalid: " + header, e);
+                SecurityContextHolder.clearContext();
+                if (failureHandler != null) {
+                    failureHandler.onAuthenticationFailure(request, response, e);
+                } else {
+                    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                    response.flushBuffer();
+                }
+                return;
+            }
+            sessionStrategy.onAuthentication(authentication, request, response);
+            SecurityContextHolder.getContext().setAuthentication(authentication);
+            if (successHandler != null) {
+                successHandler.onAuthenticationSuccess(request, response, authentication);
+            }
+        }
+        chain.doFilter(request, response);
+    }
+    /**
+     * The authentication manager for validating the ticket.
+     *
+     * @param authenticationManager
+     */
+    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
+        this.authenticationManager = authenticationManager;
+    }
+    /**
+     * This handler is called after a successful authentication. One can add
+     * additional authentication behavior by setting this.<br />
+     * Default is null, which means nothing additional happens
+     *
+     * @param successHandler
+     */
+    public void setSuccessHandler(AuthenticationSuccessHandler successHandler) {
+        this.successHandler = successHandler;
+    }
+    /**
+     * This handler is called after a failure authentication. In most cases you
+     * only get Kerberos/SPNEGO failures with a wrong server or network
+     * configurations and not during runtime. If the client encounters an error,
+     * he will just stop the communication with server and therefore this
+     * handler will not be called in this case.<br />
+     * Default is null, which means that the Filter returns the HTTP 500 code
+     *
+     * @param failureHandler
+     */
+    public void setFailureHandler(AuthenticationFailureHandler failureHandler) {
+        this.failureHandler = failureHandler;
+    }
+    /**
+     * Should Kerberos authentication be skipped if a user is already authenticated
+     * for this request (e.g. in the HTTP session).
+     *
+     * @param skipIfAlreadyAuthenticated default is true
+     */
+    public void setSkipIfAlreadyAuthenticated(boolean skipIfAlreadyAuthenticated) {
+        this.skipIfAlreadyAuthenticated = skipIfAlreadyAuthenticated;
+    }
+    /**
+     * The session handling strategy which will be invoked immediately after an authentication request is
+     * successfully processed by the <tt>AuthenticationManager</tt>. Used, for example, to handle changing of the
+     * session identifier to prevent session fixation attacks.
+     *
+     * @param sessionStrategy the implementation to use. If not set a null implementation is
+     * used.
+     */
+    public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionAuthStrategy) {
+        this.sessionStrategy = sessionAuthStrategy;
+    }
+    public void setAuthenticationDetailsSource(
+        AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
+        Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
+        this.authenticationDetailsSource = authenticationDetailsSource;
+    }
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.springframework.web.filter.GenericFilterBean#afterPropertiesSet()
+     */
+    @Override
+    public void afterPropertiesSet() throws ServletException {
+        super.afterPropertiesSet();
+        Assert.notNull(this.authenticationManager, "authenticationManager must be specified");
+    }
+}
+
+
+

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/72ba368e/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosEntryPoint.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosEntryPoint.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosEntryPoint.java
new file mode 100644
index 0000000..457a60e
--- /dev/null
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosEntryPoint.java
@@ -0,0 +1,70 @@
+/**
+ * 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.
+ */
+/*
+ * Copyright 2009 the original author or authors.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cxf.fediz.service.idp.kerberos;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+
+/**
+* Sends back a request for a Negotiate Authentication to the browser.
+*
+* @author Mike Wiesner
+* @since 1.0
+* @version $Id$
+* @see KerberosAuthenticationProcessingFilter
+*/
+public class KerberosEntryPoint implements AuthenticationEntryPoint {
+    
+    private static final Log LOG = LogFactory.getLog(KerberosEntryPoint.class);
+    
+    public void commence(HttpServletRequest request, HttpServletResponse response,
+                         AuthenticationException ex) throws IOException, ServletException {
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Sending back Negotiate Header for request: " + request.getRequestURL());
+        }
+        response.addHeader("WWW-Authenticate", "Negotiate");
+        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+        response.flushBuffer();
+    }
+    
+}
+

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/72ba368e/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosServiceRequestToken.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosServiceRequestToken.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosServiceRequestToken.java
new file mode 100644
index 0000000..ecee024
--- /dev/null
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosServiceRequestToken.java
@@ -0,0 +1,139 @@
+/**
+ * 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.
+ */
+/*
+ * Copyright 2009 the original author or authors.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.cxf.fediz.service.idp.kerberos;
+
+import java.util.Arrays;
+import java.util.Collection;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.GrantedAuthority;
+
+/**
+ * Holds the Kerberos/SPNEGO token for requesting a kerberized service
+ * and is also the output of <code>KerberosServiceAuthenticationProvider</code>.<br>
+ * Will mostly be created in <code>SpnegoAuthenticationProcessingFilter</code>
+ * and authenticated in <code>KerberosServiceAuthenticationProvider</code>.
+ *
+ * This token cannot be re-authenticated, as you will get a Kerberos Reply error.
+ *
+ * @author Mike Wiesner
+ * @since 1.0
+ * @version $Id$
+ * @see KerberosServiceAuthenticationProvider
+ * @see KerberosAuthenticationProcessingFilter
+ */
+public class KerberosServiceRequestToken extends AbstractAuthenticationToken {
+    private static final long serialVersionUID = 395488921064775014L;
+    private final byte[] token;
+    private final Object principal;
+    
+    /** Creates an authenticated token, normally used as an output of an authentication provider.
+     * @param principal the user principal (mostly of instance <code>UserDetails</code>
+     * @param authorities the authorities which are granted to the user
+     * @param token the Kerberos/SPNEGO token
+     * @see UserDetails
+     */
+    public KerberosServiceRequestToken(Object principal, 
+                                       Collection<? extends GrantedAuthority> authorities, 
+                                       byte[] token) {
+        super(authorities);
+        this.token = token;
+        this.principal = principal;
+        super.setAuthenticated(true);
+    }
+    
+    /**
+     * Creates an unauthenticated instance which should then be authenticated by
+     * <code>KerberosServiceAuthenticationProvider/code>
+     *
+     * @param token Kerberos/SPNEGO token
+     * @see KerberosServiceAuthenticationProvider
+     */
+    public KerberosServiceRequestToken(byte[] token) {
+        super(null);
+        this.token = token;
+        this.principal = null;
+    }
+    
+    /**
+     * Calculates hashcode based on the Kerberos token
+     */
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = super.hashCode();
+        result = prime * result + Arrays.hashCode(token);
+        return result;
+    }
+    
+    /**
+     * equals() is based only on the Kerberos token
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!super.equals(obj)) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        KerberosServiceRequestToken other = (KerberosServiceRequestToken) obj;
+        if (!Arrays.equals(token, other.token)) {
+            return false;
+        }
+        return true;
+    }
+    
+    /* (non-Javadoc)
+     * @see org.springframework.security.core.Authentication#getCredentials()
+     */
+    public Object getCredentials() {
+        return null;
+    }
+    
+    /* (non-Javadoc)
+     * @see org.springframework.security.core.Authentication#getPrincipal()
+     */
+    public Object getPrincipal() {
+        return this.principal;
+    }
+    
+    /** Returns the Kerberos token
+     */
+    public byte[] getToken() {
+        return this.token;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/72ba368e/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosTokenValidator.java
----------------------------------------------------------------------
diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosTokenValidator.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosTokenValidator.java
new file mode 100644
index 0000000..ae32a40
--- /dev/null
+++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/kerberos/KerberosTokenValidator.java
@@ -0,0 +1,175 @@
+/**
+ * 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.fediz.service.idp.kerberos;
+
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.apache.ws.security.message.token.KerberosServiceContext;
+import org.apache.ws.security.message.token.KerberosServiceExceptionAction;
+
+/**
+ * Validate a Kerberos Token
+ */
+public class KerberosTokenValidator {
+
+    private static final org.slf4j.Logger LOG =
+        org.slf4j.LoggerFactory.getLogger(KerberosTokenValidator.class);
+
+    private String serviceName;
+    private CallbackHandler callbackHandler;
+    private String contextName;
+    private boolean usernameServiceNameForm;
+
+    /**
+     * Get the JAAS Login context name to use.
+     * @return the JAAS Login context name to use
+     */
+    public String getContextName() {
+        return contextName;
+    }
+
+    /**
+     * Set the JAAS Login context name to use.
+     * @param contextName the JAAS Login context name to use
+     */
+    public void setContextName(String contextName) {
+        this.contextName = contextName;
+    }
+
+    /**
+     * Get the CallbackHandler to use with the LoginContext
+     * @return the CallbackHandler to use with the LoginContext
+     */
+    public CallbackHandler getCallbackHandler() {
+        return callbackHandler;
+    }
+
+    /**
+     * Set the CallbackHandler to use with the LoginContext. It can be null.
+     * @param callbackHandler the CallbackHandler to use with the LoginContext
+     */
+    public void setCallbackHandler(CallbackHandler callbackHandler) {
+        this.callbackHandler = callbackHandler;
+    }
+
+    /**
+     * The name of the service to use when contacting the KDC. This value can be null, in which
+     * case it defaults to the current principal name.
+     * @param serviceName the name of the service to use when contacting the KDC
+     */
+    public void setServiceName(String serviceName) {
+        this.serviceName = serviceName;
+    }
+
+    /**
+     * Get the name of the service to use when contacting the KDC. This value can be null, in which
+     * case it defaults to the current principal name.
+     * @return the name of the service to use when contacting the KDC
+     */
+    public String getServiceName() {
+        return serviceName;
+    }
+
+    public KerberosServiceContext validate(KerberosServiceRequestToken token) 
+        throws LoginException, PrivilegedActionException {
+        if (LOG.isDebugEnabled()) {
+            try {
+                String jaasAuth = System.getProperty("java.security.auth.login.config");
+                String krbConf = System.getProperty("java.security.krb5.conf");
+                LOG.debug("KerberosTokenValidator - Using JAAS auth login file: " + jaasAuth);
+                LOG.debug("KerberosTokenValidator - Using KRB conf file: " + krbConf);
+            } catch (SecurityException ex) {
+                LOG.debug(ex.getMessage(), ex);
+            }
+        }
+
+        // Get a TGT from the KDC using JAAS
+        LoginContext loginContext = null;
+        if (callbackHandler != null) {
+            loginContext = new LoginContext(getContextName(), callbackHandler);
+        } else {
+            loginContext = new LoginContext(getContextName());
+        }
+        loginContext.login();
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Successfully authenticated to the TGT");
+        }
+
+        // Get the service name to use - fall back on the principal
+        Subject subject = loginContext.getSubject();
+        String service = serviceName;
+        if (service == null) {
+            Set<Principal> principals = subject.getPrincipals();
+            if (principals.isEmpty()) {
+                LOG.debug("No Client principals found after login");
+                return null;
+            }
+            service = principals.iterator().next().getName();
+        }
+
+        // Validate the ticket
+        KerberosServiceExceptionAction action = 
+            new KerberosServiceExceptionAction(token.getToken(), service, isUsernameServiceNameForm());
+        KerberosServiceContext krbServiceCtx = Subject.doAs(subject, action);
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Successfully validated a ticket");
+        }
+
+        return krbServiceCtx;
+    }
+
+    /**
+     * SPN can be configured to be in either <b>"hostbased"</b> or <b>"username"</b> form.<br/>
+     *     - <b>"hostbased"</b> - specifies that the service principal name should be interpreted
+     *      as a "host-based" name as specified in GSS API Rfc, section "4.1: Host-Based Service 
+     *      Name Form" - The service name, as it is specified in LDAP/AD, as it is listed in the
+     *      KDC.<br/>
+     *     - <b>"username"</b> - specifies that the service principal name should be interpreted
+     *      as a "username" name as specified in GSS API Rfc, section "4.2: User Name Form" 
+     *      This is usually the client username in LDAP/AD used for authentication to the KDC.
+     * 
+     * <br/><br/>Default is <b>"hostbased"</b>.
+     * 
+     * @return the isUsernameServiceNameForm
+     */
+    public boolean isUsernameServiceNameForm() {
+        return usernameServiceNameForm;
+    }
+
+    /**
+     * If true - sets the SPN form to "username"
+     * <br/>If false<b>(default)</b> - the SPN form is "hostbased"
+     * 
+     * @see KerberosSecurity#retrieveServiceTicket(String, CallbackHandler, String, boolean)
+     * 
+     * @param isUsernameServiceNameForm the isUsernameServiceNameForm to set
+     */
+    public void setUsernameServiceNameForm(boolean isUsernameServiceNameForm) {
+        this.usernameServiceNameForm = isUsernameServiceNameForm;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/72ba368e/services/idp/src/main/webapp/WEB-INF/kerberos.jaas
----------------------------------------------------------------------
diff --git a/services/idp/src/main/webapp/WEB-INF/kerberos.jaas b/services/idp/src/main/webapp/WEB-INF/kerberos.jaas
new file mode 100644
index 0000000..b773cf6
--- /dev/null
+++ b/services/idp/src/main/webapp/WEB-INF/kerberos.jaas
@@ -0,0 +1,8 @@
+
+alice {
+    com.sun.security.auth.module.Krb5LoginModule required refreshKrb5Config=true useKeyTab=true keyTab="/etc/alice.keytab" principal="alice";
+};
+
+bob {
+    com.sun.security.auth.module.Krb5LoginModule required refreshKrb5Config=true useKeyTab=true storeKey=true keyTab="/etc/bob.keytab" principal="bob/service.ws.apache.org";
+};

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/72ba368e/services/idp/src/main/webapp/WEB-INF/security-config-kerberos.xml
----------------------------------------------------------------------
diff --git a/services/idp/src/main/webapp/WEB-INF/security-config-kerberos.xml b/services/idp/src/main/webapp/WEB-INF/security-config-kerberos.xml
new file mode 100644
index 0000000..3e9e35d
--- /dev/null
+++ b/services/idp/src/main/webapp/WEB-INF/security-config-kerberos.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xmlns:security="http://www.springframework.org/schema/security"
+    xmlns:context="http://www.springframework.org/schema/context"
+    xsi:schemaLocation="
+        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
+        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
+
+    <context:property-placeholder location="classpath:realm.properties"/>
+    <context:component-scan base-package="org.apache.cxf.fediz.service.idp"/>
+    
+    <!-- DIABLE in production as it might log confidential information about the user -->
+    <!-- <security:debug /> -->
+
+    <!-- Configure Spring Security -->
+    <security:http auto-config="false" use-expressions="true" entry-point-ref="kerberosEntryPoint">
+        <security:custom-filter after="CHANNEL_FILTER" ref="stsPortFilter" />
+        <security:intercept-url pattern="/FederationMetadata/2007-06/FederationMetadata.xml" access="isAnonymous() or isAuthenticated()" />
+
+        <!-- MUST be http-basic thus systests run fine -->
+        <!--<security:http-basic />-->
+        <!--<security:form-login />-->
+        <security:custom-filter ref="kerberosAuthenticationProcessingFilter" position="BASIC_AUTH_FILTER" />
+    </security:http>
+    
+    <bean id="kerberosEntryPoint"
+          class="org.apache.cxf.fediz.service.idp.kerberos.KerberosEntryPoint" />
+    
+    <bean id="kerberosAuthenticationProcessingFilter"
+          class="org.apache.cxf.fediz.service.idp.kerberos.KerberosAuthenticationProcessingFilter">
+          <property name="authenticationManager" ref="authenticationManager" />
+    </bean>
+
+    <security:authentication-manager alias="authenticationManager">
+        <security:authentication-provider ref="stsAuthProvider" />
+    </security:authentication-manager>
+	
+    <bean id="stsPortFilter" class="org.apache.cxf.fediz.service.idp.STSPortFilter" />
+    
+    <bean id="kerberosTokenValidator" class="org.apache.cxf.fediz.service.idp.kerberos.KerberosTokenValidator">
+        <property name="contextName" value="bob"/>
+        <property name="serviceName" value="bob@service.ws.apache.org"/>
+    </bean>
+	
+    <bean id="stsAuthProvider" class="org.apache.cxf.fediz.service.idp.STSAuthenticationProvider">
+        <!--<property name="wsdlLocation" value="https://localhost:0/fediz-idp-sts/${realm.STS_URI}/STSServiceTransportUT?wsdl"/>
+        <property name="wsdlEndpoint" value="TransportUT_Port"/>-->
+        <property name="wsdlLocation" value="https://localhost:0/fediz-idp-sts/${realm.STS_URI}/STSServiceTransportKerberos?wsdl"/>
+        <property name="wsdlEndpoint" value="TransportKerberos_Port"/>
+        <property name="wsdlService" value="SecurityTokenService"/>
+        <property name="appliesTo" value="urn:fediz:idp"/>
+        <property name="tokenType" value="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"/>
+        <property name="kerberosTokenValidator" ref="kerberosTokenValidator"/>
+    </bean>
+
+</beans>

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/72ba368e/systests/tomcat7/src/test/java/org/apache/cxf/fediz/integrationtests/KerberosTest.java
----------------------------------------------------------------------
diff --git a/systests/tomcat7/src/test/java/org/apache/cxf/fediz/integrationtests/KerberosTest.java b/systests/tomcat7/src/test/java/org/apache/cxf/fediz/integrationtests/KerberosTest.java
new file mode 100644
index 0000000..c205ed8
--- /dev/null
+++ b/systests/tomcat7/src/test/java/org/apache/cxf/fediz/integrationtests/KerberosTest.java
@@ -0,0 +1,388 @@
+/**
+ * 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.fediz.integrationtests;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.security.KeyStore;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.net.ssl.SSLContext;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+
+import net.htmlparser.jericho.Element;
+import net.htmlparser.jericho.FormField;
+import net.htmlparser.jericho.FormFields;
+import net.htmlparser.jericho.HTMLElementName;
+import net.htmlparser.jericho.Source;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleState;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.cxf.fediz.core.ClaimTypes;
+import org.apache.cxf.fediz.tomcat.FederationAuthenticator;
+import org.apache.http.Consts;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLContextBuilder;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.LaxRedirectStrategy;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.apache.xml.security.utils.Base64;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+
+/**
+ * A test that sends a Kerberos ticket to the IdP for authentication. The IdP must be configured
+ * to validate the Kerberos ticket, and in turn get a delegation token to authenticate to the
+ * STS + retrieve claims etc.
+ */
+@org.junit.Ignore
+public class KerberosTest {
+
+    static String idpHttpsPort;
+    static String rpHttpsPort;
+    
+    private static Tomcat idpServer;
+    private static Tomcat rpServer;
+    
+    @BeforeClass
+    public static void init() {
+        System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
+        System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
+        System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire", "info");
+        System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "info");
+        System.setProperty("org.apache.commons.logging.simplelog.log.org.springframework.webflow", "info");
+        System.setProperty("org.apache.commons.logging.simplelog.log.org.springframework.security.web", "info");
+        System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.cxf.fediz", "info");
+        System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.cxf", "info");  
+        
+        idpHttpsPort = System.getProperty("idp.https.port");
+        Assert.assertNotNull("Property 'idp.https.port' null", idpHttpsPort);
+        rpHttpsPort = System.getProperty("rp.https.port");
+        Assert.assertNotNull("Property 'rp.https.port' null", rpHttpsPort);
+
+        initIdp();
+        initRp();
+    }
+    
+    private static void initIdp() {
+        try {
+            idpServer = new Tomcat();
+            idpServer.setPort(0);
+            String currentDir = new File(".").getCanonicalPath();
+            idpServer.setBaseDir(currentDir + File.separator + "target");
+            
+            idpServer.getHost().setAppBase("tomcat/idp/webapps");
+            idpServer.getHost().setAutoDeploy(true);
+            idpServer.getHost().setDeployOnStartup(true);
+            
+            Connector httpsConnector = new Connector();
+            httpsConnector.setPort(Integer.parseInt(idpHttpsPort));
+            httpsConnector.setSecure(true);
+            httpsConnector.setScheme("https");
+            //httpsConnector.setAttribute("keyAlias", keyAlias);
+            httpsConnector.setAttribute("keystorePass", "tompass");
+            httpsConnector.setAttribute("keystoreFile", "test-classes/server.jks");
+            httpsConnector.setAttribute("truststorePass", "tompass");
+            httpsConnector.setAttribute("truststoreFile", "test-classes/server.jks");
+            httpsConnector.setAttribute("clientAuth", "want");
+            // httpsConnector.setAttribute("clientAuth", "false");
+            httpsConnector.setAttribute("sslProtocol", "TLS");
+            httpsConnector.setAttribute("SSLEnabled", true);
+
+            idpServer.getService().addConnector(httpsConnector);
+            
+            idpServer.addWebapp("/fediz-idp-sts", "fediz-idp-sts");
+            idpServer.addWebapp("/fediz-idp", "fediz-idp");
+            
+            idpServer.start();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+    
+    private static void initRp() {
+        try {
+            rpServer = new Tomcat();
+            rpServer.setPort(0);
+            String currentDir = new File(".").getCanonicalPath();
+            rpServer.setBaseDir(currentDir + File.separator + "target");
+            
+            rpServer.getHost().setAppBase("tomcat/rp/webapps");
+            rpServer.getHost().setAutoDeploy(true);
+            rpServer.getHost().setDeployOnStartup(true);
+            
+            Connector httpsConnector = new Connector();
+            httpsConnector.setPort(Integer.parseInt(rpHttpsPort));
+            httpsConnector.setSecure(true);
+            httpsConnector.setScheme("https");
+            //httpsConnector.setAttribute("keyAlias", keyAlias);
+            httpsConnector.setAttribute("keystorePass", "tompass");
+            httpsConnector.setAttribute("keystoreFile", "test-classes/server.jks");
+            httpsConnector.setAttribute("truststorePass", "tompass");
+            httpsConnector.setAttribute("truststoreFile", "test-classes/server.jks");
+            // httpsConnector.setAttribute("clientAuth", "false");
+            httpsConnector.setAttribute("clientAuth", "want");
+            httpsConnector.setAttribute("sslProtocol", "TLS");
+            httpsConnector.setAttribute("SSLEnabled", true);
+
+            rpServer.getService().addConnector(httpsConnector);
+            
+            //Context ctx =
+            Context cxt = rpServer.addWebapp("/fedizhelloworld", "simpleWebapp");
+            FederationAuthenticator fa = new FederationAuthenticator();
+            fa.setConfigFile(currentDir + File.separator + "target" + File.separator
+                             + "test-classes" + File.separator + "fediz_config.xml");
+            cxt.getPipeline().addValve(fa);
+            
+            
+            rpServer.start();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+    
+    @AfterClass
+    public static void cleanup() {
+        try {
+            if (idpServer.getServer() != null
+                && idpServer.getServer().getState() != LifecycleState.DESTROYED) {
+                if (idpServer.getServer().getState() != LifecycleState.STOPPED) {
+                    idpServer.stop();
+                }
+                idpServer.destroy();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        try {
+            if (rpServer.getServer() != null
+                && rpServer.getServer().getState() != LifecycleState.DESTROYED) {
+                if (rpServer.getServer().getState() != LifecycleState.STOPPED) {
+                    rpServer.stop();
+                }
+                rpServer.destroy();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public String getIdpHttpsPort() {
+        return idpHttpsPort;
+    }
+
+    public String getRpHttpsPort() {
+        return rpHttpsPort;
+    }
+    
+    public String getServletContextName() {
+        return "fedizhelloworld";
+    }
+    
+    @org.junit.Test
+    public void testKerberos() throws Exception {
+        String url = "https://localhost:" + getRpHttpsPort() + "/fedizhelloworld/secure/fedservlet";
+        // Get a Kerberos Ticket +  Base64 encode it
+        String ticket = getEncodedKerberosTicket();
+        
+        String response = sendHttpGet(url, ticket, 200, 200, Integer.parseInt(getIdpHttpsPort()));
+
+        String user = "alice";
+        Assert.assertTrue("Principal not " + user, response.indexOf("userPrincipal=" + user) > 0);
+        Assert.assertTrue("User " + user + " does not have role Admin", response.indexOf("role:Admin=false") > 0);
+        Assert.assertTrue("User " + user + " does not have role Manager", response.indexOf("role:Manager=false") > 0);
+        Assert.assertTrue("User " + user + " must have role User", response.indexOf("role:User=true") > 0);
+
+        String claim = ClaimTypes.FIRSTNAME.toString();
+        Assert.assertTrue("User " + user + " claim " + claim + " is not 'Alice'",
+                          response.indexOf(claim + "=Alice") > 0);
+        claim = ClaimTypes.LASTNAME.toString();
+        Assert.assertTrue("User " + user + " claim " + claim + " is not 'Smith'",
+                          response.indexOf(claim + "=Smith") > 0);
+        claim = ClaimTypes.EMAILADDRESS.toString();
+        Assert.assertTrue("User " + user + " claim " + claim + " is not 'alice@realma.org'",
+                          response.indexOf(claim + "=alice@realma.org") > 0);
+    
+    }
+    
+    private String getEncodedKerberosTicket() throws Exception {
+        
+        System.setProperty("java.security.auth.login.config", "src/test/resources/kerberos.jaas");
+        System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
+        
+        Oid kerberos5Oid = new Oid("1.2.840.113554.1.2.2");
+        
+        GSSManager manager = GSSManager.getInstance();
+        GSSName serverName = manager.createName("bob@service.ws.apache.org", 
+                                                GSSName.NT_HOSTBASED_SERVICE);
+
+        GSSContext context = manager
+                .createContext(serverName.canonicalize(kerberos5Oid), kerberos5Oid, 
+                               null, GSSContext.DEFAULT_LIFETIME);
+        
+        context.requestCredDeleg(true);
+        
+        final byte[] token = new byte[0];
+
+        String contextName = "alice";
+        LoginContext lc = new LoginContext(contextName);
+        lc.login();
+        
+        byte[] ticket = (byte[])Subject.doAs(lc.getSubject(), new CreateServiceTicketAction(context, token));
+        return Base64.encode(ticket);
+    }
+    
+    private final class CreateServiceTicketAction implements PrivilegedExceptionAction<byte[]> {
+        private final GSSContext context;
+        private final byte[] token;
+
+        private CreateServiceTicketAction(GSSContext context, byte[] token) {
+            this.context = context;
+            this.token = token;
+        }
+
+        public byte[] run() throws GSSException {
+            return context.initSecContext(token, 0, token.length);
+        }
+    }
+    
+    public static String sendHttpGet(String url, String ticket,
+                                     int returnCodeIDP, int returnCodeRP, int idpPort)
+        throws Exception {
+        
+        CloseableHttpClient httpClient = null;
+        try {
+            KeyStore trustStore  = KeyStore.getInstance(KeyStore.getDefaultType());
+            FileInputStream instream = new FileInputStream(new File("./target/test-classes/client.jks"));
+            try {
+                trustStore.load(instream, "clientpass".toCharArray());
+            } finally {
+                try {
+                    instream.close();
+                } catch (Exception ex) {
+                    ex.printStackTrace();
+                }
+            }
+
+            SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
+            sslContextBuilder.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy());
+            sslContextBuilder.loadKeyMaterial(trustStore, "clientpass".toCharArray());
+            
+            SSLContext sslContext = sslContextBuilder.build();
+            SSLConnectionSocketFactory sslSocketFactory = 
+                new SSLConnectionSocketFactory(sslContext);
+            
+            HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
+            httpClientBuilder.setSSLSocketFactory(sslSocketFactory);
+            httpClientBuilder.setRedirectStrategy(new LaxRedirectStrategy());
+            
+            httpClient = httpClientBuilder.build();
+            
+            HttpGet httpget = new HttpGet(url);
+            httpget.addHeader("Authorization", "Negotiate " + ticket);
+
+            HttpResponse response = httpClient.execute(httpget);
+            HttpEntity entity = response.getEntity();
+
+            System.out.println(response.getStatusLine());
+            if (entity != null) {
+                System.out.println("Response content length: " + entity.getContentLength());
+            }
+            Assert.assertTrue("IDP HTTP Response code: " + response.getStatusLine().getStatusCode()
+                              + " [Expected: " + returnCodeIDP + "]",
+                              returnCodeIDP == response.getStatusLine().getStatusCode());
+
+            if (response.getStatusLine().getStatusCode() != 200) {
+                return null;
+            }
+
+            //            Redirect to a POST is not supported without user interaction
+            //            http://www.ietf.org/rfc/rfc2616.txt
+            //            If the 301 status code is received in response to a request other
+            //            than GET or HEAD, the user agent MUST NOT automatically redirect the
+            //            request unless it can be confirmed by the user, since this might
+            //            change the conditions under which the request was issued.
+            
+            Source source = new Source(EntityUtils.toString(entity));
+            List <NameValuePair> nvps = new ArrayList <NameValuePair>();
+            FormFields formFields = source.getFormFields();
+            
+            List<Element> forms = source.getAllElements(HTMLElementName.FORM);
+            Assert.assertEquals("Only one form expected but got " + forms.size(), 1, forms.size());
+            String postUrl = forms.get(0).getAttributeValue("action");
+            
+            Assert.assertNotNull("Form field 'wa' not found", formFields.get("wa"));
+            Assert.assertNotNull("Form field 'wresult' not found", formFields.get("wresult"));
+            
+            for (FormField formField : formFields) {
+                if (formField.getUserValueCount() != 0) {
+                    nvps.add(new BasicNameValuePair(formField.getName(),
+                             formField.getValues().get(0)));
+                }
+            } 
+            HttpPost httppost = new HttpPost(postUrl);
+            httppost.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8));
+
+            response = httpClient.execute(httppost);
+
+            entity = response.getEntity();
+            System.out.println(response.getStatusLine());
+            Assert.assertTrue("RP HTTP Response code: " + response.getStatusLine().getStatusCode()
+                              + " [Expected: " + returnCodeRP + "]",
+                              returnCodeRP == response.getStatusLine().getStatusCode());
+
+            if (entity != null) {
+                System.out.println("Response content length: " + entity.getContentLength());
+            }
+
+            return EntityUtils.toString(entity);
+        } finally {
+            // When HttpClient instance is no longer needed,
+            // shut down the connection manager to ensure
+            // immediate deallocation of all system resources
+            if (httpClient != null) {
+                httpClient.close();
+            }
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/72ba368e/systests/tomcat7/src/test/resources/kerberos.jaas
----------------------------------------------------------------------
diff --git a/systests/tomcat7/src/test/resources/kerberos.jaas b/systests/tomcat7/src/test/resources/kerberos.jaas
new file mode 100644
index 0000000..b773cf6
--- /dev/null
+++ b/systests/tomcat7/src/test/resources/kerberos.jaas
@@ -0,0 +1,8 @@
+
+alice {
+    com.sun.security.auth.module.Krb5LoginModule required refreshKrb5Config=true useKeyTab=true keyTab="/etc/alice.keytab" principal="alice";
+};
+
+bob {
+    com.sun.security.auth.module.Krb5LoginModule required refreshKrb5Config=true useKeyTab=true storeKey=true keyTab="/etc/bob.keytab" principal="bob/service.ws.apache.org";
+};


Mime
View raw message