Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SES-10: Enable server side Kerberos login
- Loading branch information
Mike Wiesner
committed
Dec 23, 2009
1 parent
d91751f
commit 84cb1ab
Showing
4 changed files
with
326 additions
and
0 deletions.
There are no files selected for viewing
72 changes: 72 additions & 0 deletions
72
...java/org/springframework/security/extensions/kerberos/KerberosAuthenticationProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* 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.springframework.security.extensions.kerberos; | ||
|
||
import org.apache.commons.logging.Log; | ||
import org.apache.commons.logging.LogFactory; | ||
import org.springframework.security.authentication.AuthenticationProvider; | ||
import org.springframework.security.authentication.BadCredentialsException; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.AuthenticationException; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.security.core.userdetails.UserDetailsService; | ||
|
||
/** | ||
* @author Mike Wiesner | ||
* @since 1.0 | ||
* @version $Id$ | ||
*/ | ||
public class KerberosAuthenticationProvider implements AuthenticationProvider { | ||
|
||
private static final Log LOG = LogFactory.getLog(KerberosAuthenticationProvider.class); | ||
|
||
private KerberosClient kerberosClient; | ||
private UserDetailsService userDetailsService; | ||
|
||
|
||
|
||
public Authentication authenticate(Authentication authentication) throws AuthenticationException { | ||
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication; | ||
String validatedUsername = kerberosClient.login(auth.getName(), auth.getCredentials().toString()); | ||
if (validatedUsername.equalsIgnoreCase(auth.getName()) == false) { | ||
if (LOG.isDebugEnabled()) { | ||
LOG.info("Username returned from KDC ("+validatedUsername+") doesn't match with supplied username ("+auth.getName()+")"); | ||
} | ||
throw new BadCredentialsException("Username returned from KDC doesn't match with supplied username"); | ||
} | ||
|
||
UserDetails userDetails = this.userDetailsService.loadUserByUsername(auth.getName()); | ||
UsernamePasswordAuthenticationToken output = new UsernamePasswordAuthenticationToken(userDetails, auth.getCredentials(), userDetails.getAuthorities()); | ||
return output; | ||
|
||
} | ||
|
||
public boolean supports(Class<? extends Object> authentication) { | ||
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)); | ||
} | ||
|
||
public void setKerberosClient(KerberosClient kerberosClient) { | ||
this.kerberosClient = kerberosClient; | ||
} | ||
|
||
|
||
public void setUserDetailsService(UserDetailsService detailsService) { | ||
this.userDetailsService = detailsService; | ||
} | ||
|
||
} |
29 changes: 29 additions & 0 deletions
29
...s-core/src/main/java/org/springframework/security/extensions/kerberos/KerberosClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
/* | ||
* 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.springframework.security.extensions.kerberos; | ||
|
||
/** | ||
* | ||
* @author Mike Wiesner | ||
* @since 1.0 | ||
* @version $Id$ | ||
*/ | ||
public interface KerberosClient { | ||
|
||
public String login(String username, String password); | ||
|
||
} |
146 changes: 146 additions & 0 deletions
146
...src/main/java/org/springframework/security/extensions/kerberos/SunJaasKerberosClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
/* | ||
* 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.springframework.security.extensions.kerberos; | ||
|
||
import java.io.IOException; | ||
import java.util.HashMap; | ||
|
||
import javax.security.auth.callback.Callback; | ||
import javax.security.auth.callback.CallbackHandler; | ||
import javax.security.auth.callback.NameCallback; | ||
import javax.security.auth.callback.PasswordCallback; | ||
import javax.security.auth.callback.UnsupportedCallbackException; | ||
import javax.security.auth.login.AppConfigurationEntry; | ||
import javax.security.auth.login.Configuration; | ||
import javax.security.auth.login.LoginContext; | ||
import javax.security.auth.login.LoginException; | ||
|
||
import org.apache.commons.logging.Log; | ||
import org.apache.commons.logging.LogFactory; | ||
import org.springframework.beans.factory.InitializingBean; | ||
import org.springframework.security.authentication.BadCredentialsException; | ||
|
||
/** | ||
* Implementation of {@link KerberosClient} which uses the SUN JAAS | ||
* login module, which is included in the SUN JRE, it will not work with an IBM JRE. | ||
* The whole configuration is done in this class, no additional JAAS configuration | ||
* is needed. | ||
* | ||
* @author Mike Wiesner | ||
* @since 1.0 | ||
* @version $Id$ | ||
*/ | ||
public class SunJaasKerberosClient implements KerberosClient, InitializingBean { | ||
|
||
private boolean debug = false; | ||
private String krbConfLocation; | ||
|
||
|
||
private static final Log LOG = LogFactory.getLog(SunJaasKerberosClient.class); | ||
|
||
@Override | ||
public String login(String username, String password) { | ||
LOG.debug("Trying to authenticate " + username + " with Kerberos"); | ||
String validatedUsername; | ||
|
||
try { | ||
LoginContext loginContext = new LoginContext("", null, new KerberosClientCallbackHandler(username, password), | ||
new LoginConfig(this.debug)); | ||
loginContext.login(); | ||
if (LOG.isDebugEnabled()) { | ||
LOG.debug("Kerberos authenticated user: "+loginContext.getSubject()); | ||
} | ||
validatedUsername = loginContext.getSubject().getPrincipals().iterator().next().toString(); | ||
loginContext.logout(); | ||
} catch (LoginException e) { | ||
throw new BadCredentialsException("Kerberos authentication failed", e); | ||
} | ||
return validatedUsername; | ||
|
||
} | ||
|
||
public void setDebug(boolean debug) { | ||
this.debug = debug; | ||
} | ||
|
||
|
||
public void setKrbConfLocation(String krbConfLocation) { | ||
this.krbConfLocation = krbConfLocation; | ||
} | ||
|
||
public void afterPropertiesSet() throws Exception { | ||
if (krbConfLocation != null) { | ||
System.setProperty("java.security.krb5.conf", krbConfLocation); | ||
} | ||
if (debug) { | ||
System.setProperty("sun.security.krb5.debug", "true"); | ||
} | ||
|
||
|
||
} | ||
|
||
private static class LoginConfig extends Configuration { | ||
private boolean debug; | ||
|
||
public LoginConfig(boolean debug) { | ||
super(); | ||
this.debug = debug; | ||
} | ||
|
||
@Override | ||
public AppConfigurationEntry[] getAppConfigurationEntry(String name) { | ||
HashMap<String, String> options = new HashMap<String, String>(); | ||
options.put("storeKey", "true"); | ||
if (debug) { | ||
options.put("debug", "true"); | ||
} | ||
|
||
return new AppConfigurationEntry[] { new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", | ||
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options), }; | ||
} | ||
|
||
} | ||
|
||
private static class KerberosClientCallbackHandler implements CallbackHandler { | ||
private String username; | ||
private String password; | ||
|
||
public KerberosClientCallbackHandler(String username, String password) { | ||
this.username = username; | ||
this.password = password; | ||
} | ||
|
||
@Override | ||
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { | ||
for (Callback callback : callbacks) { | ||
if (callback instanceof NameCallback) { | ||
NameCallback ncb = (NameCallback) callback; | ||
ncb.setName(username); | ||
} else if (callback instanceof PasswordCallback) { | ||
PasswordCallback pwcb = (PasswordCallback) callback; | ||
pwcb.setPassword(password.toCharArray()); | ||
} else { | ||
throw new UnsupportedCallbackException(callback, "We got a " + callback.getClass().getCanonicalName() | ||
+ ", but only NameCallback and PasswordCallback is supported"); | ||
} | ||
} | ||
|
||
} | ||
|
||
} | ||
|
||
} |
79 changes: 79 additions & 0 deletions
79
.../org/springframework/security/extensions/kerberos/KerberosAuthenticationProviderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* 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.springframework.security.extensions.kerberos; | ||
|
||
import static org.junit.Assert.*; | ||
import static org.mockito.Mockito.*; | ||
|
||
import java.util.List; | ||
|
||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.GrantedAuthority; | ||
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; | ||
|
||
/** | ||
* Test class for {@link KerberosAuthenticationProvider} | ||
* | ||
* @author Mike Wiesner | ||
* @since 1.0 | ||
* @version $Id$ | ||
*/ | ||
public class KerberosAuthenticationProviderTest { | ||
|
||
private KerberosAuthenticationProvider provider; | ||
private KerberosClient kerberosClient; | ||
private UserDetailsService userDetailsService; | ||
|
||
private static final String TEST_USER = "Testuser@SPRINGSOURCE.ORG"; | ||
private static final String TEST_PASSWORD = "password"; | ||
private static final UsernamePasswordAuthenticationToken INPUT_TOKEN = new UsernamePasswordAuthenticationToken(TEST_USER, TEST_PASSWORD); | ||
private static final List<GrantedAuthority> AUTHORITY_LIST = AuthorityUtils.createAuthorityList("ROLE_ADMIN"); | ||
private static final UserDetails USER_DETAILS = new User(TEST_USER, "empty", true, true, true,true, AUTHORITY_LIST); | ||
|
||
@Before | ||
public void before() { | ||
// mocking | ||
this.kerberosClient = mock(KerberosClient.class); | ||
this.userDetailsService = mock(UserDetailsService.class); | ||
this.provider = new KerberosAuthenticationProvider(); | ||
this.provider.setKerberosClient(kerberosClient); | ||
this.provider.setUserDetailsService(userDetailsService); | ||
} | ||
|
||
@Test | ||
public void testLoginOk() throws Exception { | ||
when(userDetailsService.loadUserByUsername(TEST_USER)).thenReturn(USER_DETAILS); | ||
when(kerberosClient.login(TEST_USER, TEST_PASSWORD)).thenReturn(TEST_USER); | ||
|
||
Authentication authenticate = provider.authenticate(INPUT_TOKEN); | ||
|
||
verify(kerberosClient).login(TEST_USER, TEST_PASSWORD); | ||
|
||
assertNotNull(authenticate); | ||
assertEquals(TEST_USER, authenticate.getName()); | ||
assertEquals(USER_DETAILS, authenticate.getPrincipal()); | ||
assertEquals(TEST_PASSWORD, authenticate.getCredentials()); | ||
assertEquals(AUTHORITY_LIST, authenticate.getAuthorities()); | ||
|
||
} | ||
} |