Skip to content

Commit

Permalink
Added multitier support.
Browse files Browse the repository at this point in the history
  • Loading branch information
bmustiata committed Apr 29, 2016
1 parent aaf8f5c commit e7113fd
Show file tree
Hide file tree
Showing 16 changed files with 732 additions and 36 deletions.
@@ -0,0 +1,180 @@
/*
* Copyright 2009-2015 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.springframework.security.kerberos.client;

import org.junit.Test;
import org.springframework.core.io.FileSystemResource;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider;
import org.springframework.security.kerberos.authentication.KerberosMultiTier;
import org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;
import org.springframework.security.kerberos.authentication.KerberosServiceRequestToken;
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;
import org.springframework.security.kerberos.test.KerberosSecurityTestcase;
import org.springframework.security.kerberos.test.MiniKdc;

import java.io.File;

import static org.junit.Assert.*;

/**
* @author Bogdan Mustiata
*/
public class TestMultiTierAuthentication extends KerberosSecurityTestcase {
@Test
public void testServer() throws Exception {
MiniKdc kdc = getKdc();
File workDir = getWorkDir();

File webTierKeytabFile = new File(workDir, "webtier.keytab");
kdc.createKeyabFile(webTierKeytabFile, "HTTP/webtier@EXAMPLE.COM", "secret");

File serviceTierKeytabFile = new File(workDir, "servicetier.keytab");
kdc.createKeyabFile(serviceTierKeytabFile, "HTTP/servicetier@EXAMPLE.COM", "secret");

//
// User logs in as user1/secret
//
KerberosAuthenticationProvider kerberosAuthProvider =
createUserPassAuthenticator(/* debug: */ true);

Authentication authentication = kerberosAuthProvider
.authenticate(new UsernamePasswordAuthenticationToken("user1", "secret"));

assertEquals("user1@EXAMPLE.COM", authentication.getName());

//
// User creates a ticket for the HTTP/webtier@EXAMPLE.COM, using
// and then calls the service, using the tokenData
//
authentication = KerberosMultiTier.authenticateService(
authentication, "user1", 3600, "HTTP/webtier@EXAMPLE.COM");

byte[] tokenData = KerberosMultiTier
.getTokenForService(authentication, "HTTP/webtier@EXAMPLE.COM");

assertNotNull(tokenData);
assertTrue(tokenData.length != 0);

//
// The service HTTP/webtier@EXAMPLE.COM authenticates via tokens.
//
KerberosServiceAuthenticationProvider webTierAuthenticatorProvider =
createServiceAuthenticator(
true,
"HTTP/webtier@EXAMPLE.COM",
"EXAMPLE.COM",
webTierKeytabFile.getCanonicalPath()
);


//
// The service HTTP/webtier@EXAMPLE.COM authenticates the user1@EXAMPLE.COM
// using the previously stored token, then authenticates itself further as
// user1@EXAMPLE.COM to the HTTP/servicetier@EXAMPLE.COM.
//
Authentication webTierAuthentication = webTierAuthenticatorProvider
.authenticate(new KerberosServiceRequestToken(tokenData));

assertEquals("user1@EXAMPLE.COM", webTierAuthentication.getName());

webTierAuthentication = KerberosMultiTier.authenticateService(
webTierAuthentication, "user1@EXAMPLE.COM", 3600, "HTTP/servicetier@EXAMPLE.COM");

byte[] workplaceTokenData = KerberosMultiTier.getTokenForService(
webTierAuthentication, "HTTP/servicetier@EXAMPLE.COM");

//
// The service HTTP/icr@EXAMPLE.COM authenticates via tokens.
//
webTierAuthenticatorProvider =
createServiceAuthenticator(
true,
"HTTP/servicetier@EXAMPLE.COM",
"EXAMPLE.COM",
serviceTierKeytabFile.getCanonicalPath()
);

//
// The service HTTP/servicetier@EXAMPLE.COM authenticates via the previously saved
// token, received from the HTTP/webtier@EXAMPLE.COM on behalf of user1@EXAMPLE.COM
//
Authentication serviceTierAuthentication = webTierAuthenticatorProvider
.authenticate(new KerberosServiceRequestToken(workplaceTokenData));

assertEquals("user1@EXAMPLE.COM", serviceTierAuthentication.getName());
}

/**
* Create a username/password authenticator.
* @return
*/
private KerberosAuthenticationProvider createUserPassAuthenticator(boolean debug) {
KerberosAuthenticationProvider kerberosAuthenticationProvider =
new KerberosAuthenticationProvider();

SunJaasKerberosClient sunJaasKerberosClient = new SunJaasKerberosClient();

sunJaasKerberosClient.setDebug(debug);
sunJaasKerberosClient.setMultiTier(true);

kerberosAuthenticationProvider.setKerberosClient(sunJaasKerberosClient);
kerberosAuthenticationProvider.setUserDetailsService(userDetailsService());

return kerberosAuthenticationProvider;
}

private KerberosServiceAuthenticationProvider createServiceAuthenticator(boolean debug,
String serviceName,
String realmName,
String keytabFileLocation) throws Exception {
KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider =
new KerberosServiceAuthenticationProvider();

SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
ticketValidator.setDebug(debug);
ticketValidator.setServicePrincipal(serviceName);
ticketValidator.setRealmName(realmName);
ticketValidator.setKeyTabLocation(new FileSystemResource(keytabFileLocation));
ticketValidator.setMultiTier(true);

ticketValidator.afterPropertiesSet();

kerberosServiceAuthenticationProvider.setTicketValidator(ticketValidator);
kerberosServiceAuthenticationProvider.setUserDetailsService(userDetailsService());

return kerberosServiceAuthenticationProvider;
}

private UserDetailsService userDetailsService() {
return new UserDetailsService() {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(username, "notUsed", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_USER"));

}
};
}

}
Expand Up @@ -18,6 +18,7 @@
[libdefaults]
default_realm = {0}
udp_preference_limit = 1
forwardable = true

[realms]
{0} = '{'
Expand Down
41 changes: 40 additions & 1 deletion spring-security-kerberos-client/src/test/resources/minikdc.ldiff
Expand Up @@ -44,4 +44,43 @@ sn: Service
uid: ldap
userPassword: secret
krb5PrincipalName: ldap/${4}@${2}.${3}
krb5KeyVersionNumber: 0
krb5KeyVersionNumber: 0

dn: uid=user1,ou=users,dc=${0},dc=${1}
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: user1
sn: Service
uid: user1
userPassword: secret
krb5PrincipalName: user1@${2}.${3}
krb5KeyVersionNumber: 0

dn: uid=webtier,ou=users,dc=${0},dc=${1}
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: webtier
sn: Service
uid: webtier
userPassword: secret
krb5PrincipalName: HTTP/webtier@${2}.${3}
krb5KeyVersionNumber: 0

dn: uid=servicetier,ou=users,dc=${0},dc=${1}
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: krb5principal
objectClass: krb5kdcentry
cn: servicetier
sn: Service
uid: servicetier
userPassword: secret
krb5PrincipalName: HTTP/servicetier@${2}.${3}
krb5KeyVersionNumber: 0
@@ -0,0 +1,62 @@
/*
* Copyright 2009-2015 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.springframework.security.kerberos.authentication;

import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosClient;

import javax.security.auth.Subject;
import java.util.HashMap;
import java.util.Map;

/**
* <p>Holds the Subject of the currently authenticated user, since this
* Jaas object also has the credentials, and permits creating new
* credentials against other Kerberos services.</p>
* @author Bogdan Mustiata
* @see SunJaasKerberosClient
* @see org.springframework.security.kerberos.authentication.KerberosAuthenticationProvider
*/
public class JaasSubjectHolder {
private Subject jaasSubject;
private String username;

private Map<String, byte[]> savedTokens = new HashMap<String, byte[]>();

public JaasSubjectHolder(Subject jaasSubject) {
this.jaasSubject = jaasSubject;
}

public JaasSubjectHolder(Subject jaasSubject, String username) {
this.jaasSubject = jaasSubject;
this.username = username;
}

public String getUsername() {
return username;
}

public Subject getJaasSubject() {
return jaasSubject;
}

public void addToken(String targetService, byte[] outToken) {
this.savedTokens.put(targetService, outToken);
}

public byte[] getToken(String principalName) {
return savedTokens.get(principalName);
}
}
@@ -0,0 +1,6 @@
package org.springframework.security.kerberos.authentication;

public interface KerberosAuthentication {

JaasSubjectHolder getJaasSubjectHolder();
}
Expand Up @@ -26,6 +26,7 @@
* {@link AuthenticationProvider} for kerberos.
*
* @author Mike Wiesner
* @author Bogdan Mustiata
* @since 1.0
*/
public class KerberosAuthenticationProvider implements AuthenticationProvider {
Expand All @@ -37,10 +38,10 @@ public class KerberosAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
String validatedUsername = kerberosClient.login(auth.getName(), auth.getCredentials().toString());
UserDetails userDetails = this.userDetailsService.loadUserByUsername(validatedUsername);
UsernamePasswordAuthenticationToken output = new UsernamePasswordAuthenticationToken(userDetails,
auth.getCredentials(), userDetails.getAuthorities());
JaasSubjectHolder subjectHolder = kerberosClient.login(auth.getName(), auth.getCredentials().toString());
UserDetails userDetails = this.userDetailsService.loadUserByUsername(subjectHolder.getUsername());
KerberosUsernamePasswordAuthenticationToken output = new KerberosUsernamePasswordAuthenticationToken(
userDetails, auth.getCredentials(), userDetails.getAuthorities(), subjectHolder);
output.setDetails(authentication.getDetails());
return output;

Expand Down
Expand Up @@ -19,11 +19,12 @@
/**
*
* @author Mike Wiesner
* @author Bogdan Mustiata
* @since 1.0
* @version $Id$
*/
public interface KerberosClient {

public String login(String username, String password);
public JaasSubjectHolder login(String username, String password);

}

0 comments on commit e7113fd

Please sign in to comment.