diff --git a/build.gradle b/build.gradle
index 69f41ec..8333394 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,9 +17,9 @@ configure(allprojects) {
sourceCompatibility=1.6
targetCompatibility=1.6
- ext.junitVersion = '4.10'
+ ext.junitVersion = '4.11'
ext.springSecurityVersion = '3.1.4.RELEASE'
- ext.springVersion = '3.2.2.RELEASE'
+ ext.springVersion = '3.2.3.RELEASE'
[compileJava, compileTestJava]*.options*.compilerArgs = ['-Xlint:none']
diff --git a/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceAuthenticationProvider.java b/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceAuthenticationProvider.java
index 575919e..f157080 100644
--- a/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceAuthenticationProvider.java
+++ b/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceAuthenticationProvider.java
@@ -26,6 +26,7 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsChecker;
import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.extensions.kerberos.KerberosTicketValidator.KerberosTicketValidation;
import org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter;
import org.springframework.util.Assert;
@@ -82,15 +83,16 @@ public class KerberosServiceAuthenticationProvider implements
KerberosServiceRequestToken auth = (KerberosServiceRequestToken) authentication;
byte[] token = auth.getToken();
LOG.debug("Try to validate Kerberos Token");
- String username = this.ticketValidator.validateTicket(token);
- LOG.debug("Succesfully validated " + username);
- UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
+ KerberosTicketValidation ticketValidation = this.ticketValidator.validateTicket(token);
+ LOG.debug("Succesfully validated " + ticketValidation.username());
+ UserDetails userDetails = this.userDetailsService.loadUserByUsername(ticketValidation.username());
userDetailsChecker.check(userDetails);
additionalAuthenticationChecks(userDetails, auth);
- KerberosServiceRequestToken responseAuth = new KerberosServiceRequestToken(userDetails, userDetails.getAuthorities(), token);
+ KerberosServiceRequestToken responseAuth = new KerberosServiceRequestToken(
+ userDetails, ticketValidation,
+ userDetails.getAuthorities(), token);
responseAuth.setDetails(authentication.getDetails());
- return responseAuth;
-
+ return responseAuth;
}
diff --git a/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceRequestToken.java b/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceRequestToken.java
index 907ea0e..88744bb 100644
--- a/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceRequestToken.java
+++ b/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosServiceRequestToken.java
@@ -16,22 +16,32 @@
package org.springframework.security.extensions.kerberos;
+import java.io.UnsupportedEncodingException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.Collection;
+import javax.security.auth.Subject;
+
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.MessageProp;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.crypto.codec.Base64;
+import org.springframework.security.extensions.kerberos.KerberosTicketValidator.KerberosTicketValidation;
import org.springframework.security.extensions.kerberos.web.SpnegoAuthenticationProcessingFilter;
/**
- * Holds the Kerberos/SPNEGO token for requesting a kerberized service
- * and is also the output of KerberosServiceAuthenticationProvider.
+ * Holds the Kerberos/SPNEGO token for requesting a kerberized service and is
+ * also the output of KerberosServiceAuthenticationProvider.
* Will mostly be created in SpnegoAuthenticationProcessingFilter
* and authenticated in KerberosServiceAuthenticationProvider.
- *
- * This token cannot be re-authenticated, as you will get a Kerberos Reply error.
- *
+ *
+ * This token cannot be re-authenticated, as you will get a Kerberos Reply
+ * error.
+ *
* @author Mike Wiesner
* @since 1.0
* @version $Id$
@@ -42,19 +52,27 @@ import org.springframework.security.extensions.kerberos.web.SpnegoAuthentication
public class KerberosServiceRequestToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 395488921064775014L;
+
private final byte[] token;
+
private final Object principal;
+ private final KerberosTicketValidation ticketValidation;
+
/** Creates an authenticated token, normally used as an output of an authentication provider.
* @param principal the user principal (mostly of instance UserDetails
+ * @param ticketValidation result of ticket validation
* @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) {
+ public KerberosServiceRequestToken(Object principal,
+ KerberosTicketValidation ticketValidation,
+ Collection extends GrantedAuthority> authorities, byte[] token) {
super(authorities);
this.token = token;
this.principal = principal;
+ this.ticketValidation = ticketValidation;
super.setAuthenticated(true);
}
@@ -68,6 +86,7 @@ public class KerberosServiceRequestToken extends AbstractAuthenticationToken {
public KerberosServiceRequestToken(byte[] token) {
super(null);
this.token = token;
+ this.ticketValidation = null;
this.principal = null;
}
@@ -113,10 +132,73 @@ public class KerberosServiceRequestToken extends AbstractAuthenticationToken {
return this.principal;
}
- /** Returns the Kerberos token
+ /**
+ * Returns the Kerberos token
*/
public byte[] getToken() {
return this.token;
}
+ /**
+ * Gets the ticket validation
+ *
+ * @return the ticket validation (which will be null if the token is
+ * unauthenticated)
+ */
+ public KerberosTicketValidation getTicketValidation() {
+ return ticketValidation;
+ }
+
+ /**
+ * Determines whether an authenticated token has a response token
+ *
+ * @return whether a response token is available
+ */
+ public boolean hasResponseToken() {
+ return ticketValidation != null
+ && ticketValidation.responseToken() != null;
+ }
+
+ /**
+ * Gets the (Base64) encoded response token assuming one is available.
+ *
+ * @return encoded response token
+ */
+ public String getEncodedResponseToken() {
+ if (!hasResponseToken())
+ throw new IllegalStateException(
+ "Unauthenticated or no response token");
+
+ try {
+ return new String(Base64.encode(ticketValidation.responseToken()),
+ "UTF-8");
+ }
+ catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException("Unable to encode response token",
+ e);
+ }
+ }
+
+ /**
+ * Unwraps an encrypted message using the gss context
+ *
+ * @param data
+ * @param offset
+ * @param length
+ * @return the decrypted message
+ * @throws PrivilegedActionException
+ */
+ public byte[] decrypt(final byte[] data, final int offset, final int length)
+ throws PrivilegedActionException {
+
+ return Subject.doAs(getTicketValidation().subject(),
+ new PrivilegedExceptionAction() {
+ public byte[] run() throws Exception {
+ final GSSContext context = getTicketValidation()
+ .getGssContext();
+ return context.unwrap(data, offset, length,
+ new MessageProp(true));
+ }
+ });
+ }
}
diff --git a/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosTicketValidator.java b/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosTicketValidator.java
index 9d72d7a..b98a433 100644
--- a/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosTicketValidator.java
+++ b/spring-security-kerberos-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosTicketValidator.java
@@ -16,12 +16,19 @@
package org.springframework.security.extensions.kerberos;
+import java.util.HashSet;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+
+import org.ietf.jgss.GSSContext;
import org.springframework.security.authentication.BadCredentialsException;
/**
* Implementations of this interface are used in
- * {@link KerberosServiceAuthenticationProvider} to validate a Kerberos/SPNEGO Ticket.
- *
+ * {@link KerberosServiceAuthenticationProvider} to validate a Kerberos/SPNEGO
+ * Ticket.
+ *
* @author Mike Wiesner
* @since 1.0
* @version $Id$
@@ -29,11 +36,55 @@ import org.springframework.security.authentication.BadCredentialsException;
*/
public interface KerberosTicketValidator {
- /** Validates a Kerberos/SPNEGO ticket.
+ /**
+ * Validates a Kerberos/SPNEGO ticket.
* @param token Kerbeos/SPNEGO ticket
* @return authenticated kerberos principal
* @throws BadCredentialsException if the ticket is not valid
*/
- public String validateTicket(byte[] token) throws BadCredentialsException;
+ public KerberosTicketValidation validateTicket(byte[] token)
+ throws BadCredentialsException;
-}
\ No newline at end of file
+ /**
+ * Result of ticket validation
+ *
+ * @author Jeremy.Stone
+ */
+ public static class KerberosTicketValidation {
+ private final String username;
+
+ private final byte[] responseToken;
+
+ private final GSSContext gssContext;
+
+ private final String servicePrincipal;
+
+ KerberosTicketValidation(String username, String servicePrincipal,
+ byte[] responseToken, GSSContext gssContext) {
+ this.username = username;
+ this.servicePrincipal = servicePrincipal;
+ this.responseToken = responseToken;
+ this.gssContext = gssContext;
+ }
+
+ public String username() {
+ return username;
+ }
+
+ public byte[] responseToken() {
+ return responseToken;
+ }
+
+ public GSSContext getGssContext() {
+ return gssContext;
+ }
+
+ public Subject subject() {
+ final HashSet princs = new HashSet();
+ princs.add(new KerberosPrincipal(servicePrincipal));
+
+ return new Subject(false, princs, new HashSet