Skip to content

Commit

Permalink
Add Kerberized LdapContextSource
Browse files Browse the repository at this point in the history
  • Loading branch information
nrodrigues authored and jvalkeal committed Mar 24, 2015
1 parent b434708 commit 958c705
Show file tree
Hide file tree
Showing 3 changed files with 242 additions and 0 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ project('spring-security-kerberos-client') {
dependencies {
compile project(":spring-security-kerberos-core")
compile "org.springframework:spring-web:$springVersion"
compile "org.springframework.security:spring-security-ldap:$springSecurityVersion"
compile "org.apache.httpcomponents:httpclient:$httpclientVersion"
testCompile project(":spring-security-kerberos-test")
testCompile "org.springframework.boot:spring-boot-autoconfigure:$springBootVersion"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright 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
*
* https://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.client;

import java.security.PrivilegedAction;
import java.util.Hashtable;
import java.util.List;

import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.security.auth.Subject;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.extensions.kerberos.client.config.SunJaasKrb5LoginConfig;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.util.Assert;

/**
* Implementation of an {@link LdapContextSource} that authenticates with the
* ldap server using Kerberos.
*
* Example usage:
*
* <pre>
* &lt;bean id=&quot;authorizationContextSource&quot; class=&quot;org.springframework.security.extensions.kerberos.ldap.KerberosLdapContextSource&quot;&gt;
* &lt;constructor-arg value=&quot;${authentication.ldap.ldapUrl}&quot; /&gt;
* &lt;property name=&quot;referral&quot; value=&quot;ignore&quot; /&gt;
*
* &lt;property name=&quot;loginConfig&quot;&gt;
* &lt;bean class=&quot;org.springframework.security.extensions.kerberos.client.config.SunJaasKrb5LoginConfig&quot;&gt;
* &lt;property name=&quot;servicePrincipal&quot; value=&quot;${authentication.ldap.servicePrincipal}&quot; /&gt;
* &lt;property name=&quot;useTicketCache&quot; value=&quot;true&quot; /&gt;
* &lt;property name=&quot;isInitiator&quot; value=&quot;true&quot; /&gt;
* &lt;property name=&quot;debug&quot; value=&quot;false&quot; /&gt;
* &lt;/bean&gt;
* &lt;/property&gt;
* &lt;/bean&gt;
*
* &lt;sec:ldap-user-service id=&quot;ldapUserService&quot; server-ref=&quot;authorizationContextSource&quot; user-search-filter=&quot;(| (userPrincipalName={0}) (sAMAccountName={0}))&quot;
* group-search-filter=&quot;(member={0})&quot; group-role-attribute=&quot;cn&quot; role-prefix=&quot;none&quot; /&gt;
* </pre>
*
* @see SunJaasKrb5LoginConfig
* @author Nelson Rodrigues
*
*/
public class KerberosLdapContextSource extends DefaultSpringSecurityContextSource implements
InitializingBean {

private Configuration loginConfig;

public KerberosLdapContextSource(String url) {
super(url);
}

public KerberosLdapContextSource(List<String> urls, String baseDn) {
super(urls, baseDn);
}

/**
* The login configuration to get the serviceSubject from LoginContext
*
* @param loginConfig
*/
public void setLoginConfig(Configuration loginConfig) {
this.loginConfig = loginConfig;
}

@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();

Assert.notNull(this.loginConfig, "loginConfig must be specified");
}

private Subject login() throws AuthenticationException {
try {
LoginContext lc = new LoginContext(KerberosLdapContextSource.class.getSimpleName(),
null, null, this.loginConfig);

lc.login();

return lc.getSubject();
} catch (LoginException e) {
AuthenticationException ae = new AuthenticationException(e.getMessage());
ae.initCause(e);
throw ae;
}
}

@SuppressWarnings("unchecked")
@Override
protected DirContext getDirContextInstance(
final @SuppressWarnings("rawtypes") Hashtable environment) throws NamingException {
environment.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");

Subject serviceSubject = login();

final NamingException[] suppressedException = new NamingException[] { null };
DirContext dirContext = Subject.doAs(serviceSubject, new PrivilegedAction<DirContext>() {

@Override
public DirContext run() {
try {
return KerberosLdapContextSource.super.getDirContextInstance(environment);
} catch (NamingException e) {
suppressedException[0] = e;
return null;
}
}
});

if (suppressedException[0] != null) {
throw suppressedException[0];
}

return dirContext;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright 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
*
* https://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.client.config;

import java.util.HashMap;

import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;

/**
* Implementation of {@link Configuration} which uses Sun's JAAS
* Krb5LoginModule.
*
* @author Nelson Rodrigues
*
*/
public class SunJaasKrb5LoginConfig extends Configuration implements InitializingBean {

private String servicePrincipal;
private Resource keyTabLocation;
private Boolean useTicketCache = false;
private Boolean isInitiator = false;
private Boolean debug = false;

private String keyTabExternalForm;

public void setServicePrincipal(String servicePrincipal) {
this.servicePrincipal = servicePrincipal;
}

public void setKeyTabLocation(Resource keyTabLocation) {
this.keyTabLocation = keyTabLocation;
}

public void setUseTicketCache(Boolean useTicketCache) {
this.useTicketCache = useTicketCache;
}

public void setIsInitiator(Boolean isInitiator) {
this.isInitiator = isInitiator;
}

public void setDebug(Boolean debug) {
this.debug = debug;
}

@Override
public void afterPropertiesSet() throws Exception {
Assert.hasText(servicePrincipal, "servicePrincipal must be specified");

if (!useTicketCache) {
Assert.notNull(keyTabLocation,
"keyTabLocation must be specified when useTicketCache is false");
this.keyTabExternalForm = keyTabLocation.getURL().toExternalForm();
}
}

@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
HashMap<String, String> options = new HashMap<String, String>();

options.put("principal", this.servicePrincipal);

if (this.keyTabLocation != null) {
options.put("useKeyTab", "true");
options.put("keyTab", this.keyTabExternalForm);
options.put("storeKey", "true");
}

options.put("doNotPrompt", "true");

if (useTicketCache) {
options.put("useTicketCache", "true");
options.put("renewTGT", "true");
}

options.put("isInitiator", this.isInitiator.toString());
options.put("debug", this.debug.toString());

return new AppConfigurationEntry[] { new AppConfigurationEntry(
"com.sun.security.auth.module.Krb5LoginModule",
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options), };
}

}

0 comments on commit 958c705

Please sign in to comment.