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 <[email protected]>
Authored: Tue Aug 26 16:58:04 2014 +0100
Committer: Colm O hEigeartaigh <[email protected]>
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 = "[email protected]";
+ 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="[email protected]"/>
+ </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.
+ */
[email protected]
+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
'[email protected]'",
+ response.indexOf(claim + "[email protected]") > 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("[email protected]",
+ 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";
+};