Migrate kdc testing

- Migrate test kdc system to org.apache.kerby:kerb-simplekdc
- Add simple tests for KerberosRestTemplate
- Fixes #175
This commit is contained in:
Janne Valkealahti
2023-05-09 08:17:19 +01:00
parent 1f932ed5eb
commit 2660eb4649
15 changed files with 381 additions and 1224 deletions

View File

@@ -7,3 +7,5 @@ mockitoVersion=4.8.1
assertjVersion=3.23.1
servletApiVersion=6.0.0
httpclient5Version=5.1.4
kerbyVersion=2.0.3
okhttp3Version=3.14.9

View File

@@ -30,6 +30,7 @@ include 'spring-security-kerberos-bom'
include 'spring-security-kerberos-core'
include 'spring-security-kerberos-client'
include 'spring-security-kerberos-web'
include 'spring-security-kerberos-test'
include 'spring-security-kerberos-samples:sec-client-rest-template'
include 'spring-security-kerberos-samples:sec-server-client-auth'
include 'spring-security-kerberos-samples:sec-server-spnego-form-auth'

View File

@@ -14,9 +14,11 @@ dependencies {
optional 'org.springframework.security:spring-security-ldap'
// api('org.springframework.security:spring-security-web')
// api('jakarta.servlet:jakarta.servlet-api')
testImplementation project(':spring-security-kerberos-test')
testImplementation 'org.springframework:spring-test'
testImplementation 'org.springframework.security:spring-security-config'
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.mockito:mockito-junit-jupiter'
testImplementation 'org.assertj:assertj-core'
testImplementation 'com.squareup.okhttp3:mockwebserver'
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2023 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
* 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,
@@ -15,249 +15,115 @@
*/
package org.springframework.security.kerberos.client;
// import static org.hamcrest.CoreMatchers.is;
// import static org.junit.Assert.assertThat;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
// import java.io.File;
// import java.io.IOException;
// import java.lang.annotation.Documented;
// import java.lang.annotation.ElementType;
// import java.lang.annotation.Retention;
// import java.lang.annotation.RetentionPolicy;
// import java.lang.annotation.Target;
// import java.net.InetAddress;
// import java.util.concurrent.CountDownLatch;
// import java.util.concurrent.TimeUnit;
import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import okio.Buffer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
// import org.junit.After;
// import org.junit.Test;
// import org.springframework.boot.SpringApplication;
// import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
// import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration;
// import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
// import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
// import org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration;
// import org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration;
// import org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration;
// import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration;
// import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
// import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
// import org.springframework.context.ApplicationListener;
// import org.springframework.context.ConfigurableApplicationContext;
// import org.springframework.context.annotation.Bean;
// import org.springframework.context.annotation.Configuration;
// import org.springframework.context.annotation.Import;
// import org.springframework.http.client.ClientHttpResponse;
// import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
// import org.springframework.security.kerberos.client.KerberosRestTemplate;
// import org.springframework.security.kerberos.test.KerberosSecurityTestcase;
// import org.springframework.security.kerberos.test.MiniKdc;
// import org.springframework.stereotype.Controller;
// import org.springframework.web.bind.annotation.RequestMapping;
// import org.springframework.web.bind.annotation.RequestMethod;
// import org.springframework.web.bind.annotation.ResponseBody;
// import org.springframework.web.client.DefaultResponseErrorHandler;
// import org.springframework.web.client.RestTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.kerberos.test.KerberosSecurityTestcase;
import org.springframework.security.kerberos.test.MiniKdc;
public class KerberosRestTemplateTests /*extends KerberosSecurityTestcase */{
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
import static org.springframework.http.HttpHeaders.CONTENT_LENGTH;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
import static org.springframework.http.HttpHeaders.WWW_AUTHENTICATE;
// private ConfigurableApplicationContext context;
class KerberosRestTemplateTests extends KerberosSecurityTestcase {
// @After
// public void close() {
// if (context != null) {
// context.close();
// }
// context = null;
// }
private final MockWebServer server = new MockWebServer();
private static final String helloWorld = "Hello World";
private static final MediaType textContentType =
new MediaType("text", "plain", Collections.singletonMap("charset", "UTF-8"));
private int port;
private String baseUrl;
private KerberosRestTemplate restTemplate;
private String clientPrincipal;
private File clientKeytab;
// @Test
// public void testSpnego() throws Exception {
@BeforeEach
void setUp() throws Exception {
this.server.setDispatcher(new TestDispatcher());
this.server.start();
this.port = this.server.getPort();
this.baseUrl = "http://localhost:" + this.port;
// MiniKdc kdc = getKdc();
// File workDir = getWorkDir();
// String host = InetAddress.getLocalHost().getCanonicalHostName();
MiniKdc kdc = getKdc();
File workDir = getWorkDir();
// String serverPrincipal = "HTTP/" + host;
// File serverKeytab = new File(workDir, "server.keytab");
// kdc.createPrincipal(serverKeytab, serverPrincipal);
clientPrincipal = "client/localhost";
clientKeytab = new File(workDir, "client.keytab");
kdc.createPrincipal(clientKeytab, clientPrincipal);
// String clientPrincipal = "client/" + host;
// File clientKeytab = new File(workDir, "client.keytab");
// kdc.createPrincipal(clientKeytab, clientPrincipal);
String serverPrincipal = "HTTP/localhost";
File serverKeytab = new File(workDir, "server.keytab");
kdc.createPrincipal(serverKeytab, serverPrincipal);
}
@AfterEach
void tearDown() throws Exception {
this.server.shutdown();
}
// context = SpringApplication.run(new Object[] { WebSecurityConfig.class, VanillaWebConfiguration.class,
// WebConfiguration.class }, new String[] { "--security.basic.enabled=true",
// "--security.user.name=username", "--security.user.password=password",
// "--serverPrincipal=" + serverPrincipal, "--serverKeytab=" + serverKeytab.getAbsolutePath() });
@Test
void sendsNegotiateHeader() {
setUpClient();
String s = restTemplate.getForObject(baseUrl + "/get", String.class);
assertThat(s).isEqualTo(helloWorld);
}
// PortInitListener portInitListener = context.getBean(PortInitListener.class);
// assertThat(portInitListener.latch.await(10, TimeUnit.SECONDS), is(true));
// int port = portInitListener.port;
private void setUpClient() {
restTemplate = new KerberosRestTemplate(clientKeytab.getAbsolutePath(), clientPrincipal);
}
// KerberosRestTemplate restTemplate = new KerberosRestTemplate(clientKeytab.getAbsolutePath(), clientPrincipal);
private MockResponse getRequest(RecordedRequest request, byte[] body, String contentType) {
if (request.getMethod().equals("OPTIONS")) {
return new MockResponse().setResponseCode(200).setHeader("Allow", "GET, OPTIONS, HEAD, TRACE");
}
Buffer buf = new Buffer();
buf.write(body);
MockResponse response = new MockResponse()
.setHeader(CONTENT_LENGTH, body.length)
.setBody(buf)
.setResponseCode(200);
if (contentType != null) {
response = response.setHeader(CONTENT_TYPE, contentType);
}
return response;
}
// String response = restTemplate.getForObject("http://" + host + ":" + port + "/hello", String.class);
// assertThat(response, is("home"));
// }
protected class TestDispatcher extends Dispatcher {
// @Test
// public void testSpnegoWithPassword() throws Exception {
@Override
public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
try {
byte[] helloWorldBytes = helloWorld.getBytes(StandardCharsets.UTF_8);
// MiniKdc kdc = getKdc();
// File workDir = getWorkDir();
// String host = InetAddress.getLocalHost().getCanonicalHostName();
if (request.getPath().equals("/get")) {
String header = request.getHeader(AUTHORIZATION);
if (header == null) {
return new MockResponse().setResponseCode(401).addHeader(WWW_AUTHENTICATE, "Negotiate");
}
else if (header != null && header.startsWith("Negotiate ")) {
return getRequest(request, helloWorldBytes, textContentType.toString());
}
}
return new MockResponse().setResponseCode(404);
}
catch (Throwable ex) {
return new MockResponse().setResponseCode(500).setBody(ex.toString());
}
// String serverPrincipal = "HTTP/" + host;
// File serverKeytab = new File(workDir, "server.keytab");
// kdc.createPrincipal(serverKeytab, serverPrincipal);
// String userPrincipal = "testuser";
// String password = "testpassword";
// kdc.createPrincipal(userPrincipal, password);
// context = SpringApplication.run(new Object[] { WebSecurityConfig.class, VanillaWebConfiguration.class,
// WebConfiguration.class }, new String[] { "--security.basic.enabled=true",
// "--security.user.name=username", "--security.user.password=password",
// "--serverPrincipal=" + serverPrincipal, "--serverKeytab=" + serverKeytab.getAbsolutePath() });
// PortInitListener portInitListener = context.getBean(PortInitListener.class);
// assertThat(portInitListener.latch.await(10, TimeUnit.SECONDS), is(true));
// int port = portInitListener.port;
// KerberosRestTemplate restTemplate = new KerberosRestTemplate(null, userPrincipal, password, null);
// String response = restTemplate.getForObject("http://" + host + ":" + port + "/hello", String.class);
// assertThat(response, is("home"));
// }
// @Test
// public void testSpnegoWithForward() throws Exception {
// MiniKdc kdc = getKdc();
// File workDir = getWorkDir();
// String host = InetAddress.getLocalHost().getCanonicalHostName();
// String serverPrincipal = "HTTP/" + host;
// File serverKeytab = new File(workDir, "server.keytab");
// kdc.createPrincipal(serverKeytab, serverPrincipal);
// context = SpringApplication.run(new Object[] { WebSecurityConfigSpnegoForward.class, VanillaWebConfiguration.class,
// WebConfiguration.class }, new String[] { "--security.basic.enabled=true",
// "--security.user.name=username", "--security.user.password=password",
// "--serverPrincipal=" + serverPrincipal, "--serverKeytab=" + serverKeytab.getAbsolutePath() });
// PortInitListener portInitListener = context.getBean(PortInitListener.class);
// assertThat(portInitListener.latch.await(10, TimeUnit.SECONDS), is(true));
// int port = portInitListener.port;
// // TODO: should tweak minikdc so that we can use kerberos principals
// // which are not valid, for now just use plain RestTemplate
// // just checking that we get 401 which we skip and
// // get login page content
// RestTemplate restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
// restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
// @Override
// public void handleError(ClientHttpResponse response) throws IOException {
// }
// });
// String response = restTemplate.getForObject("http://" + host + ":" + port + "/hello", String.class);
// assertThat(response, is("login"));
// }
// @Test
// public void testSpnegoWithSuccessHandler() throws Exception {
// MiniKdc kdc = getKdc();
// File workDir = getWorkDir();
// String host = InetAddress.getLocalHost().getCanonicalHostName();
// String serverPrincipal = "HTTP/" + host;
// File serverKeytab = new File(workDir, "server.keytab");
// kdc.createPrincipal(serverKeytab, serverPrincipal);
// String clientPrincipal = "client/" + host;
// File clientKeytab = new File(workDir, "client.keytab");
// kdc.createPrincipal(clientKeytab, clientPrincipal);
// context = SpringApplication.run(new Object[] { WebSecurityConfigSuccessHandler.class, VanillaWebConfiguration.class,
// WebConfiguration.class }, new String[] { "--security.basic.enabled=true",
// "--security.user.name=username", "--security.user.password=password",
// "--serverPrincipal=" + serverPrincipal, "--serverKeytab=" + serverKeytab.getAbsolutePath() });
// PortInitListener portInitListener = context.getBean(PortInitListener.class);
// assertThat(portInitListener.latch.await(10, TimeUnit.SECONDS), is(true));
// int port = portInitListener.port;
// KerberosRestTemplate restTemplate = new KerberosRestTemplate(clientKeytab.getAbsolutePath(), clientPrincipal);
// String response = restTemplate.getForObject("http://" + host + ":" + port + "/hello", String.class);
// assertThat(response, is("home"));
// }
// protected static class PortInitListener implements ApplicationListener<EmbeddedServletContainerInitializedEvent> {
// public int port;
// public CountDownLatch latch = new CountDownLatch(1);
// @Override
// public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
// port = event.getEmbeddedServletContainer().getPort();
// latch.countDown();
// }
// }
// @Configuration
// protected static class VanillaWebConfiguration {
// @Bean
// public PortInitListener portListener() {
// return new PortInitListener();
// }
// @Bean
// public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
// TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
// factory.setPort(0);
// return factory;
// }
// }
// @MinimalWebConfiguration
// @Import(SecurityAutoConfiguration.class)
// @Controller
// protected static class WebConfiguration {
// @RequestMapping(method = RequestMethod.GET)
// @ResponseBody
// public String home() {
// return "home";
// }
// @RequestMapping(method = RequestMethod.GET, value = "/login")
// @ResponseBody
// public String login() {
// return "login";
// }
// }
// @Configuration
// @Target(ElementType.TYPE)
// @Retention(RetentionPolicy.RUNTIME)
// @Documented
// @Import({ EmbeddedServletContainerAutoConfiguration.class,
// ServerPropertiesAutoConfiguration.class,
// DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
// HttpMessageConvertersAutoConfiguration.class,
// ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class })
// protected static @interface MinimalWebConfiguration {
// }
}
}
}

View File

@@ -1,191 +0,0 @@
/*
* 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
*
* 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.kerberos.client;
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;
/**
* @author Bogdan Mustiata
*/
public class TestMultiTierAuthentication /* extends KerberosSecurityTestcase*/ {
// public static final String REALM_NAME = "EXAMPLE.COM";
// public static final String USER_LOGIN_NAME = "user1";
// public static final String USER_FQDN_NAME = "user1@EXAMPLE.COM";
// public static final String USER_PASSWORD = "secret";
// public static final String WEB_TIER_SPN = "HTTP/webtier@EXAMPLE.COM";
// public static final String WEB_TIER_USER_PASSWORD = "secret";
// public static final String SERVICE_TIER_SPN = "HTTP/servicetier@EXAMPLE.COM";
// public static final String SERVICE_TIER_USER_PASSWORD = "secret";
// @Test
// public void testServer() throws Exception {
// MiniKdc kdc = getKdc();
// File workDir = getWorkDir();
// File webTierKeytabFile = new File(workDir, "webtier.keytab");
// kdc.createKeyabFile(webTierKeytabFile, WEB_TIER_SPN, WEB_TIER_USER_PASSWORD);
// File serviceTierKeytabFile = new File(workDir, "servicetier.keytab");
// kdc.createKeyabFile(serviceTierKeytabFile, SERVICE_TIER_SPN, SERVICE_TIER_USER_PASSWORD);
// //
// // User logs in as user1/secret
// //
// KerberosAuthenticationProvider kerberosAuthProvider =
// createUserPassAuthenticator(/* debug: */ true);
// Authentication authentication = kerberosAuthProvider
// .authenticate(new UsernamePasswordAuthenticationToken(USER_LOGIN_NAME, USER_PASSWORD));
// assertEquals(USER_FQDN_NAME, 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, USER_LOGIN_NAME, 3600, WEB_TIER_SPN);
// byte[] tokenData = KerberosMultiTier
// .getTokenForService(authentication, WEB_TIER_SPN);
// assertNotNull(tokenData);
// assertTrue(tokenData.length != 0);
// //
// // The service HTTP/webtier@EXAMPLE.COM authenticates via tokens.
// //
// KerberosServiceAuthenticationProvider webTierAuthenticatorProvider =
// createServiceAuthenticator(
// true,
// WEB_TIER_SPN,
// REALM_NAME,
// 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(USER_FQDN_NAME, webTierAuthentication.getName());
// webTierAuthentication = KerberosMultiTier.authenticateService(
// webTierAuthentication, USER_FQDN_NAME, 3600, SERVICE_TIER_SPN);
// byte[] workplaceTokenData = KerberosMultiTier.getTokenForService(
// webTierAuthentication, SERVICE_TIER_SPN);
// //
// // The service HTTP/icr@EXAMPLE.COM authenticates via tokens.
// //
// webTierAuthenticatorProvider =
// createServiceAuthenticator(
// true,
// SERVICE_TIER_SPN,
// REALM_NAME,
// 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(USER_FQDN_NAME, 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"));
// }
// };
// }
}

View File

@@ -1,110 +0,0 @@
/*
* 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.kerberos.client;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
// import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
// import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
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.KerberosServiceAuthenticationProvider;
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;
import org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter;
import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
// @Configuration
// @EnableWebMvcSecurity
public class WebSecurityConfig /*extends WebSecurityConfigurerAdapter*/ {
@Value("${serverPrincipal}")
private String serverPrincipal;
@Value("${serverKeytab}")
private String serverKeytab;
// @Override
// protected void configure(HttpSecurity http) throws Exception {
// http
// .exceptionHandling().authenticationEntryPoint(spnegoEntryPoint()).and()
// .authorizeRequests()
// .antMatchers("/", "/home").permitAll()
// .antMatchers("/hello").access("hasRole('ROLE_USER')")
// .anyRequest().authenticated()
// .and()
// .addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManagerBean()), BasicAuthenticationFilter.class);
// }
// @Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.authenticationProvider(kerberosServiceAuthenticationProvider());
// }
// @Bean
// public SpnegoEntryPoint spnegoEntryPoint() {
// return new SpnegoEntryPoint();
// }
// @Bean
// public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
// AuthenticationManager authenticationManager) {
// SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
// filter.setAuthenticationManager(authenticationManager);
// return filter;
// }
// @Bean
// public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
// KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
// provider.setTicketValidator(sunJaasKerberosTicketValidator());
// provider.setUserDetailsService(dummyUserDetailsService());
// return provider;
// }
// @Bean
// public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
// SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
// ticketValidator.setServicePrincipal(serverPrincipal);
// ticketValidator.setKeyTabLocation(new FileSystemResource(serverKeytab));
// ticketValidator.setDebug(true);
// return ticketValidator;
// }
// @Bean
// public DummyUserDetailsService dummyUserDetailsService() {
// return new DummyUserDetailsService();
// }
// static class DummyUserDetailsService implements UserDetailsService {
// public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// return new User(username, "notUsed", true, true, true, true,
// AuthorityUtils.createAuthorityList("ROLE_USER"));
// }
// }
}

View File

@@ -1,110 +0,0 @@
/*
* 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.kerberos.client;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
// import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
// import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
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.KerberosServiceAuthenticationProvider;
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;
import org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter;
import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
// @Configuration
// @EnableWebMvcSecurity
public class WebSecurityConfigSpnegoForward /*extends WebSecurityConfigurerAdapter*/ {
// @Value("${serverPrincipal}")
// private String serverPrincipal;
// @Value("${serverKeytab}")
// private String serverKeytab;
// @Override
// protected void configure(HttpSecurity http) throws Exception {
// http
// .exceptionHandling().authenticationEntryPoint(spnegoEntryPoint()).and()
// .authorizeRequests()
// .antMatchers("/", "/home", "/login").permitAll()
// .antMatchers("/hello").access("hasRole('ROLE_USER')")
// .anyRequest().authenticated()
// .and()
// .addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManagerBean()), BasicAuthenticationFilter.class);
// }
// @Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.authenticationProvider(kerberosServiceAuthenticationProvider());
// }
// @Bean
// public SpnegoEntryPoint spnegoEntryPoint() {
// return new SpnegoEntryPoint("/login");
// }
// @Bean
// public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
// AuthenticationManager authenticationManager) {
// SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
// filter.setAuthenticationManager(authenticationManager);
// return filter;
// }
// @Bean
// public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
// KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
// provider.setTicketValidator(sunJaasKerberosTicketValidator());
// provider.setUserDetailsService(dummyUserDetailsService());
// return provider;
// }
// @Bean
// public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
// SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
// ticketValidator.setServicePrincipal(serverPrincipal);
// ticketValidator.setKeyTabLocation(new FileSystemResource(serverKeytab));
// ticketValidator.setDebug(true);
// return ticketValidator;
// }
// @Bean
// public DummyUserDetailsService dummyUserDetailsService() {
// return new DummyUserDetailsService();
// }
// static class DummyUserDetailsService implements UserDetailsService {
// public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// return new User(username, "notUsed", true, true, true, true,
// AuthorityUtils.createAuthorityList("ROLE_USER"));
// }
// }
}

View File

@@ -1,115 +0,0 @@
/*
* 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.kerberos.client;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
// import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
// import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
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.KerberosServiceAuthenticationProvider;
import org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator;
import org.springframework.security.kerberos.web.authentication.ResponseHeaderSettingKerberosAuthenticationSuccessHandler;
import org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter;
import org.springframework.security.kerberos.web.authentication.SpnegoEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
// @Configuration
// @EnableWebMvcSecurity
public class WebSecurityConfigSuccessHandler /* extends WebSecurityConfigurerAdapter*/ {
// @Value("${serverPrincipal}")
// private String serverPrincipal;
// @Value("${serverKeytab}")
// private String serverKeytab;
// @Override
// protected void configure(HttpSecurity http) throws Exception {
// http
// .exceptionHandling().authenticationEntryPoint(spnegoEntryPoint()).and()
// .authorizeRequests()
// .antMatchers("/", "/home").permitAll()
// .antMatchers("/hello").access("hasRole('ROLE_USER')")
// .anyRequest().authenticated()
// .and()
// .addFilterBefore(spnegoAuthenticationProcessingFilter(authenticationManagerBean()), BasicAuthenticationFilter.class);
// }
// @Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.authenticationProvider(kerberosServiceAuthenticationProvider());
// }
// @Bean
// public SpnegoEntryPoint spnegoEntryPoint() {
// return new SpnegoEntryPoint();
// }
// @Bean
// public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
// AuthenticationManager authenticationManager) {
// SpnegoAuthenticationProcessingFilter filter = new SpnegoAuthenticationProcessingFilter();
// ResponseHeaderSettingKerberosAuthenticationSuccessHandler successHandler = new ResponseHeaderSettingKerberosAuthenticationSuccessHandler();
// filter.setSuccessHandler(successHandler);
// filter.setAuthenticationManager(authenticationManager);
// return filter;
// }
// @Bean
// public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
// KerberosServiceAuthenticationProvider provider = new KerberosServiceAuthenticationProvider();
// provider.setTicketValidator(sunJaasKerberosTicketValidator());
// provider.setUserDetailsService(dummyUserDetailsService());
// return provider;
// }
// @Bean
// public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
// SunJaasKerberosTicketValidator ticketValidator = new SunJaasKerberosTicketValidator();
// ticketValidator.setServicePrincipal(serverPrincipal);
// ticketValidator.setKeyTabLocation(new FileSystemResource(serverKeytab));
// ticketValidator.setDebug(true);
// return ticketValidator;
// }
// @Bean
// public DummyUserDetailsService dummyUserDetailsService() {
// return new DummyUserDetailsService();
// }
// static class DummyUserDetailsService implements UserDetailsService {
// public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// return new User(username, "notUsed", true, true, true, true,
// AuthorityUtils.createAuthorityList("ROLE_USER"));
// }
// }
}

View File

@@ -1,67 +0,0 @@
/*
* 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.kerberos.client.docs;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.FileSystemResource;
import org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig;
import org.springframework.security.kerberos.client.ldap.KerberosLdapContextSource;
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
import org.springframework.security.ldap.userdetails.LdapUserDetailsMapper;
import org.springframework.security.ldap.userdetails.LdapUserDetailsService;
public class KerberosLdapContextSourceConfig {
//tag::snippetA[]
@Value("${app.ad-server}")
private String adServer;
@Value("${app.service-principal}")
private String servicePrincipal;
@Value("${app.keytab-location}")
private String keytabLocation;
@Value("${app.ldap-search-base}")
private String ldapSearchBase;
@Value("${app.ldap-search-filter}")
private String ldapSearchFilter;
@Bean
public KerberosLdapContextSource kerberosLdapContextSource() {
KerberosLdapContextSource contextSource = new KerberosLdapContextSource(adServer);
SunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig();
loginConfig.setKeyTabLocation(new FileSystemResource(keytabLocation));
loginConfig.setServicePrincipal(servicePrincipal);
loginConfig.setDebug(true);
loginConfig.setIsInitiator(true);
contextSource.setLoginConfig(loginConfig);
return contextSource;
}
@Bean
public LdapUserDetailsService ldapUserDetailsService() {
FilterBasedLdapUserSearch userSearch =
new FilterBasedLdapUserSearch(ldapSearchBase, ldapSearchFilter, kerberosLdapContextSource());
LdapUserDetailsService service = new LdapUserDetailsService(userSearch);
service.setUserDetailsMapper(new LdapUserDetailsMapper());
return service;
}
//end::snippetA[]
}

View File

@@ -1,38 +0,0 @@
/*
* 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.kerberos.client.docs;
import org.springframework.security.kerberos.client.KerberosRestTemplate;
public class KerberosRestTemplateConfig {
//tag::snippetA[]
public void doWithTicketCache() {
KerberosRestTemplate restTemplate =
new KerberosRestTemplate();
restTemplate.getForObject("http://neo.example.org:8080/hello", String.class);
}
//end::snippetA[]
//tag::snippetB[]
public void doWithKeytabFile() {
KerberosRestTemplate restTemplate =
new KerberosRestTemplate("/tmp/user2.keytab", "user2@EXAMPLE.ORG");
restTemplate.getForObject("http://neo.example.org:8080/hello", String.class);
}
//end::snippetB[]
}

View File

@@ -14,9 +14,11 @@ dependencies {
api platform("org.junit:junit-bom:$junitVersion")
api platform("org.mockito:mockito-bom:$mockitoVersion")
constraints {
// api "org.apache.httpcomponents:httpclient:$httpclientVersion"
api "org.apache.httpcomponents.client5:httpclient5:$httpclient5Version"
api "org.assertj:assertj-core:$assertjVersion"
api "jakarta.servlet:jakarta.servlet-api:$servletApiVersion"
api "org.apache.kerby:kerb-simplekdc:$kerbyVersion"
api "com.squareup.okhttp3:mockwebserver:$okhttp3Version"
api "com.squareup.okhttp3:okhttp:$okhttp3Version"
}
}

View File

@@ -0,0 +1,14 @@
plugins {
id 'org.springframework.security.kerberos.module'
}
description = 'Spring Security Kerberos Test'
dependencies {
management platform(project(":spring-security-kerberos-management"))
api 'org.apache.kerby:kerb-simplekdc'
api 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.springframework:spring-test'
testImplementation 'org.mockito:mockito-junit-jupiter'
testImplementation 'org.assertj:assertj-core'
}

View File

@@ -1,11 +1,13 @@
/*
* Copyright 2015 the original author or authors.
/**
* 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
*
* 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
* 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,
@@ -15,20 +17,21 @@
*/
package org.springframework.security.kerberos.test;
import org.junit.After;
import org.junit.Before;
import java.io.File;
import java.util.Properties;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
/**
* KerberosSecurityTestcase provides a base class for using MiniKdc with other
* testcases. KerberosSecurityTestcase starts the MiniKdc (@Before) before
* running tests, and stop the MiniKdc (@After) after the testcases, using
* default settings (working dir and kdc configurations).
*
* @author Original Hadoop MiniKdc Authors
* @author Janne Valkealahti
* <p>
* Users can directly inherit this class and implement their own test functions
* using the default settings, or override functions getTestDir() and
* createMiniKdcConf() to provide new settings.
*
*/
public class KerberosSecurityTestcase {
@@ -36,7 +39,7 @@ public class KerberosSecurityTestcase {
private File workDir;
private Properties conf;
@Before
@BeforeEach
public void startMiniKdc() throws Exception {
createTestDir();
createMiniKdcConf();
@@ -46,9 +49,9 @@ public class KerberosSecurityTestcase {
}
/**
* Create a working directory, it should be the build directory. Under this
* directory an ApacheDS working directory will be created, this directory
* will be deleted when the MiniKdc stops.
* Create a working directory, it should be the build directory. Under
* this directory an ApacheDS working directory will be created, this
* directory will be deleted when the MiniKdc stops.
*/
public void createTestDir() {
workDir = new File(System.getProperty("test.dir", "target"));
@@ -61,7 +64,7 @@ public class KerberosSecurityTestcase {
conf = MiniKdc.createConf();
}
@After
@AfterEach
public void stopMiniKdc() {
if (kdc != null) {
kdc.stop();
@@ -79,5 +82,4 @@ public class KerberosSecurityTestcase {
public Properties getConf() {
return conf;
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2023 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
* 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,
@@ -15,79 +15,43 @@
*/
package org.springframework.security.kerberos.test;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.apache.directory.api.ldap.model.schema.SchemaManager;
import org.apache.directory.api.ldap.schemaextractor.SchemaLdifExtractor;
import org.apache.directory.api.ldap.schemaextractor.impl.DefaultSchemaLdifExtractor;
import org.apache.directory.api.ldap.schemaloader.LdifSchemaLoader;
import org.apache.directory.api.ldap.schemamanager.impl.DefaultSchemaManager;
import org.apache.directory.server.constants.ServerDNConstants;
import org.apache.directory.server.core.DefaultDirectoryService;
import org.apache.directory.server.core.api.CacheService;
import org.apache.directory.server.core.api.DirectoryService;
import org.apache.directory.server.core.api.InstanceLayout;
import org.apache.directory.server.core.api.schema.SchemaPartition;
import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor;
import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex;
import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition;
import org.apache.directory.server.core.partition.ldif.LdifPartition;
import org.apache.directory.server.kerberos.kdc.KdcServer;
import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
import org.apache.directory.server.kerberos.shared.keytab.Keytab;
import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
import org.apache.directory.server.protocol.shared.transport.TcpTransport;
import org.apache.directory.server.protocol.shared.transport.UdpTransport;
import org.apache.directory.server.xdbm.Index;
import org.apache.directory.shared.kerberos.KerberosTime;
import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
import org.apache.directory.shared.kerberos.components.EncryptionKey;
import org.apache.directory.api.ldap.model.entry.DefaultEntry;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.ldif.LdifEntry;
import org.apache.directory.api.ldap.model.ldif.LdifReader;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.api.ldap.model.schema.registries.SchemaLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import org.apache.kerby.kerberos.kerb.KrbException;
import org.apache.kerby.kerberos.kerb.server.KdcConfigKey;
import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer;
import org.apache.kerby.util.IOUtil;
import org.apache.kerby.util.NetworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>Mini KDC based on Apache Directory Server that can be embedded in testcases
* or used from command line as a standalone KDC.</p>
* Mini KDC based on Apache Directory Server that can be embedded in testcases
* or used from command line as a standalone KDC.
* <p>
* <b>From within testcases:</b>
* <p>MiniKdc sets 2 System properties when started and un-sets them when stopped:</p>
* <p>
* MiniKdc sets one System property when started and un-set when stopped:
* <ul>
* <li>java.security.krb5.conf: set to the MiniKDC real/host/port</li>
* <li>sun.security.krb5.debug: set to the debug value provided in the
* configuration</li>
* </ul>
* <p>Because of this, multiple MiniKdc instances cannot be started in parallel.
* Because of this, multiple MiniKdc instances cannot be started in parallel.
* For example, running testcases in parallel that start a KDC each. To
* accomplish this a single MiniKdc should be used for all testcases running in
* parallel.</p>
*
* accomplish this a single MiniKdc should be used for all testcases running
* in parallel.
* <p>
* MiniKdc default configuration values are:
* <ul>
* <li>org.name=EXAMPLE (used to create the REALM)</li>
@@ -105,28 +69,34 @@ import java.util.UUID;
* @author Original Hadoop MiniKdc Authors
* @author Janne Valkealahti
* @author Bogdan Mustiata
*
*/
public class MiniKdc {
public static final String JAVA_SECURITY_KRB5_CONF = "java.security.krb5.conf";
public static final String SUN_SECURITY_KRB5_DEBUG = "sun.security.krb5.debug";
public static void main(String[] args) throws Exception {
if (args.length < 4) {
System.out.println("Arguments: <WORKDIR> <MINIKDCPROPERTIES> " + "<KEYTABFILE> [<PRINCIPALS>]+");
System.out.println("Arguments: <WORKDIR> <MINIKDCPROPERTIES> " +
"<KEYTABFILE> [<PRINCIPALS>]+");
System.exit(1);
}
File workDir = new File(args[0]);
if (!workDir.exists()) {
throw new RuntimeException("Specified work directory does not exists: " + workDir.getAbsolutePath());
throw new RuntimeException("Specified work directory does not exists: "
+ workDir.getAbsolutePath());
}
Properties conf = createConf();
File file = new File(args[1]);
if (!file.exists()) {
throw new RuntimeException("Specified configuration does not exists: " + file.getAbsolutePath());
throw new RuntimeException("Specified configuration does not exists: "
+ file.getAbsolutePath());
}
Properties userConf = new Properties();
InputStreamReader r = null;
try {
r = new InputStreamReader(new FileInputStream(file), Charsets.UTF_8);
r = new InputStreamReader(new FileInputStream(file),
StandardCharsets.UTF_8);
userConf.load(r);
} finally {
if (r != null) {
@@ -148,7 +118,8 @@ public class MiniKdc {
System.out.println("Standalone MiniKdc Running");
System.out.println("---------------------------------------------------");
System.out.println(" Realm : " + miniKdc.getRealm());
System.out.println(" Running at : " + miniKdc.getHost() + ":" + miniKdc.getHost());
System.out.println(" Running at : " + miniKdc.getHost() + ":" +
miniKdc.getHost());
System.out.println(" krb5conf : " + krb5conf);
System.out.println();
System.out.println(" created keytab : " + keytabFile);
@@ -164,7 +135,8 @@ public class MiniKdc {
}
});
} else {
throw new RuntimeException("Cannot rename KDC's krb5conf to " + krb5conf.getAbsolutePath());
throw new RuntimeException("Cannot rename KDC's krb5conf to "
+ krb5conf.getAbsolutePath());
}
}
@@ -176,6 +148,7 @@ public class MiniKdc {
public static final String KDC_PORT = "kdc.port";
public static final String INSTANCE = "instance";
public static final String MAX_TICKET_LIFETIME = "max.ticket.lifetime";
public static final String MIN_TICKET_LIFETIME = "min.ticket.lifetime";
public static final String MAX_RENEWABLE_LIFETIME = "max.renewable.lifetime";
public static final String TRANSPORT = "transport";
public static final String DEBUG = "debug";
@@ -206,9 +179,10 @@ public class MiniKdc {
}
/**
* <p>Convenience method that returns MiniKdc default configuration.</p>
* <p>The returned configuration is a copy, it can be customized before using
* it to create a MiniKdc.</p>
* Convenience method that returns MiniKdc default configuration.
* <p>
* The returned configuration is a copy, it can be customized before using
* it to create a MiniKdc.
*
* @return a MiniKdc default configuration.
*/
@@ -217,50 +191,52 @@ public class MiniKdc {
}
private Properties conf;
private DirectoryService ds;
private KdcServer kdc;
private SimpleKdcServer simpleKdc;
private int port;
private String realm;
private File workDir;
private File krb5conf;
private String transport;
private boolean krb5Debug;
public void setTransport(String transport) {
this.transport = transport;
}
/**
* Creates a MiniKdc.
*
* @param conf MiniKdc configuration.
* @param conf MiniKdc configuration.
* @param workDir working directory, it should be the build directory. Under
* this directory an ApacheDS working directory will be created,
* this directory will be deleted when the MiniKdc stops.
* @throws Exception thrown if the MiniKdc could not be created.
* this
* directory will be deleted when the MiniKdc stops.
* @throws Exception thrown if the MiniKdc could not be created.
*/
public MiniKdc(Properties conf, File workDir) throws Exception {
if (!conf.keySet().containsAll(PROPERTIES)) {
Set<String> missingProperties = new HashSet<String>(PROPERTIES);
missingProperties.removeAll(conf.keySet());
throw new IllegalArgumentException("Missing configuration properties: " + missingProperties);
throw new IllegalArgumentException("Missing configuration properties: "
+ missingProperties);
}
this.workDir = new File(workDir, Long.toString(System.currentTimeMillis()));
if (!workDir.exists() && !workDir.mkdirs()) {
throw new RuntimeException("Cannot create directory " + workDir);
if (!this.workDir.exists()
&& !this.workDir.mkdirs()) {
throw new RuntimeException("Cannot create directory " + this.workDir);
}
LOG.info("Configuration:");
LOG.info("---------------------------------------------------------------");
for (Map.Entry<?, ?> entry : conf.entrySet()) {
LOG.info(" {}: {}", entry.getKey(), entry.getValue());
}
LOG.info(" localhost hostname: {}", InetAddress.getLocalHost().getHostName());
LOG.info(" localhost canonical hostname: {}", InetAddress.getLocalHost().getCanonicalHostName());
LOG.info("---------------------------------------------------------------");
this.conf = conf;
port = Integer.parseInt(conf.getProperty(KDC_PORT));
if (port == 0) {
ServerSocket ss = new ServerSocket(0, 1, InetAddress.getByName(conf.getProperty(KDC_BIND_ADDRESS)));
port = ss.getLocalPort();
ss.close();
}
String orgName = conf.getProperty(ORG_NAME);
String orgDomain = conf.getProperty(ORG_DOMAIN);
realm = orgName.toUpperCase() + "." + orgDomain.toUpperCase();
realm = orgName.toUpperCase(Locale.ENGLISH) + "."
+ orgDomain.toUpperCase(Locale.ENGLISH);
}
/**
@@ -291,6 +267,7 @@ public class MiniKdc {
}
public File getKrb5conf() {
krb5conf = new File(System.getProperty(JAVA_SECURITY_KRB5_CONF));
return krb5conf;
}
@@ -300,182 +277,89 @@ public class MiniKdc {
* @throws Exception thrown if the MiniKdc could not be started.
*/
public synchronized void start() throws Exception {
if (kdc != null) {
if (simpleKdc != null) {
throw new RuntimeException("Already started");
}
initDirectoryService();
initKDCServer();
simpleKdc = new SimpleKdcServer();
prepareKdcServer();
simpleKdc.init();
resetDefaultRealm();
simpleKdc.start();
LOG.info("MiniKdc started.");
}
private void initDirectoryService() throws Exception {
ds = new DefaultDirectoryService();
ds.setInstanceLayout(new InstanceLayout(workDir));
CacheService cacheService = new CacheService();
ds.setCacheService(cacheService);
// first load the schema
InstanceLayout instanceLayout = ds.getInstanceLayout();
File schemaPartitionDirectory = new File(instanceLayout.getPartitionsDirectory(), "schema");
SchemaLdifExtractor extractor = new DefaultSchemaLdifExtractor(instanceLayout.getPartitionsDirectory());
extractor.extractOrCopy();
SchemaLoader loader = new LdifSchemaLoader(schemaPartitionDirectory);
SchemaManager schemaManager = new DefaultSchemaManager(loader);
schemaManager.loadAllEnabled();
ds.setSchemaManager(schemaManager);
// Init the LdifPartition with schema
LdifPartition schemaLdifPartition = new LdifPartition(schemaManager);
schemaLdifPartition.setPartitionPath(schemaPartitionDirectory.toURI());
// The schema partition
SchemaPartition schemaPartition = new SchemaPartition(schemaManager);
schemaPartition.setWrappedPartition(schemaLdifPartition);
ds.setSchemaPartition(schemaPartition);
JdbmPartition systemPartition = new JdbmPartition(ds.getSchemaManager());
systemPartition.setId("system");
systemPartition.setPartitionPath(new File(ds.getInstanceLayout().getPartitionsDirectory(), systemPartition
.getId()).toURI());
systemPartition.setSuffixDn(new Dn(ServerDNConstants.SYSTEM_DN));
systemPartition.setSchemaManager(ds.getSchemaManager());
ds.setSystemPartition(systemPartition);
ds.getChangeLog().setEnabled(false);
ds.setDenormalizeOpAttrsEnabled(true);
ds.addLast(new KeyDerivationInterceptor());
// create one partition
String orgName = conf.getProperty(ORG_NAME).toLowerCase();
String orgDomain = conf.getProperty(ORG_DOMAIN).toLowerCase();
JdbmPartition partition = new JdbmPartition(ds.getSchemaManager());
partition.setId(orgName);
partition.setPartitionPath(new File(ds.getInstanceLayout().getPartitionsDirectory(), orgName).toURI());
partition.setSuffixDn(new Dn("dc=" + orgName + ",dc=" + orgDomain));
ds.addPartition(partition);
// indexes
Set<Index<?, ?, String>> indexedAttributes = new HashSet<Index<?, ?, String>>();
indexedAttributes.add(new JdbmIndex<String, Entry>("objectClass", false));
indexedAttributes.add(new JdbmIndex<String, Entry>("dc", false));
indexedAttributes.add(new JdbmIndex<String, Entry>("ou", false));
partition.setIndexedAttributes(indexedAttributes);
// And start the ds
ds.setInstanceId(conf.getProperty(INSTANCE));
ds.startup();
// context entry, after ds.startup()
Dn dn = new Dn("dc=" + orgName + ",dc=" + orgDomain);
Entry entry = ds.newEntry(dn);
entry.add("objectClass", "top", "domain");
entry.add("dc", orgName);
ds.getAdminSession().add(entry);
private void resetDefaultRealm() throws IOException {
InputStream templateResource = new FileInputStream(
getKrb5conf().getAbsolutePath());
String content = IOUtil.readInput(templateResource);
content = content.replaceAll("default_realm = .*\n",
"default_realm = " + getRealm() + "\n");
IOUtil.writeFile(content, getKrb5conf());
}
private void initKDCServer() throws Exception {
String orgName = conf.getProperty(ORG_NAME);
String orgDomain = conf.getProperty(ORG_DOMAIN);
String bindAddress = conf.getProperty(KDC_BIND_ADDRESS);
final Map<String, String> map = new HashMap<String, String>();
map.put("0", orgName.toLowerCase());
map.put("1", orgDomain.toLowerCase());
map.put("2", orgName.toUpperCase());
map.put("3", orgDomain.toUpperCase());
map.put("4", bindAddress);
ClassLoader cl = Thread.currentThread().getContextClassLoader();
InputStream is1 = cl.getResourceAsStream("minikdc.ldiff");
SchemaManager schemaManager = ds.getSchemaManager();
LdifReader reader = null;
try {
final String content = StrSubstitutor.replace(IOUtils.toString(is1), map);
reader = new LdifReader(new StringReader(content));
for (LdifEntry ldifEntry : reader) {
ds.getAdminSession().add(new DefaultEntry(schemaManager, ldifEntry.getEntry()));
}
} finally {
IOUtils.closeQuietly(reader);
IOUtils.closeQuietly(is1);
}
kdc = new KdcServer();
kdc.setDirectoryService(ds);
private void prepareKdcServer() throws Exception {
// transport
String transport = conf.getProperty(TRANSPORT);
if (transport.trim().equals("TCP")) {
kdc.addTransports(new TcpTransport(bindAddress, port, 3, 50));
} else if (transport.trim().equals("UDP")) {
kdc.addTransports(new UdpTransport(port));
} else {
throw new IllegalArgumentException("Invalid transport: " + transport);
simpleKdc.setWorkDir(workDir);
simpleKdc.setKdcHost(getHost());
simpleKdc.setKdcRealm(realm);
if (transport == null) {
transport = conf.getProperty(TRANSPORT);
}
kdc.setServiceName(conf.getProperty(INSTANCE));
kdc.getConfig().setMaximumRenewableLifetime(Long.parseLong(conf.getProperty(MAX_RENEWABLE_LIFETIME)));
kdc.getConfig().setMaximumTicketLifetime(Long.parseLong(conf.getProperty(MAX_TICKET_LIFETIME)));
kdc.getConfig().setPaEncTimestampRequired(false);
kdc.start();
StringBuilder sb = new StringBuilder();
InputStream is2 = cl.getResourceAsStream("minikdc-krb5.conf");
BufferedReader r = null;
try {
r = new BufferedReader(new InputStreamReader(is2, Charsets.UTF_8));
String line = r.readLine();
while (line != null) {
sb.append(line).append("{3}");
line = r.readLine();
if (port == 0) {
port = NetworkUtil.getServerPort();
}
if (transport != null) {
if (transport.trim().equals("TCP")) {
simpleKdc.setKdcTcpPort(port);
simpleKdc.setAllowUdp(false);
} else if (transport.trim().equals("UDP")) {
simpleKdc.setKdcUdpPort(port);
simpleKdc.setAllowTcp(false);
} else {
throw new IllegalArgumentException("Invalid transport: " + transport);
}
} finally {
IOUtils.closeQuietly(r);
IOUtils.closeQuietly(is2);
}
krb5conf = new File(workDir, "krb5.conf").getAbsoluteFile();
FileUtils.writeStringToFile(
krb5conf,
MessageFormat.format(sb.toString(), getRealm(), getHost(), Integer.toString(getPort()),
System.getProperty("line.separator")));
System.setProperty("java.security.krb5.conf", krb5conf.getAbsolutePath());
System.setProperty("sun.security.krb5.debug", conf.getProperty(DEBUG, "false"));
// refresh the config
Class<?> classRef;
if (System.getProperty("java.vendor").contains("IBM")) {
classRef = Class.forName("com.ibm.security.krb5.internal.Config");
} else {
classRef = Class.forName("sun.security.krb5.Config");
throw new IllegalArgumentException("Need to set transport!");
}
simpleKdc.getKdcConfig().setString(KdcConfigKey.KDC_SERVICE_NAME,
conf.getProperty(INSTANCE));
if (conf.getProperty(DEBUG) != null) {
krb5Debug = getAndSet(SUN_SECURITY_KRB5_DEBUG, conf.getProperty(DEBUG));
}
if (conf.getProperty(MIN_TICKET_LIFETIME) != null) {
simpleKdc.getKdcConfig().setLong(KdcConfigKey.MINIMUM_TICKET_LIFETIME,
Long.parseLong(conf.getProperty(MIN_TICKET_LIFETIME)));
}
if (conf.getProperty(MAX_TICKET_LIFETIME) != null) {
simpleKdc.getKdcConfig().setLong(KdcConfigKey.MAXIMUM_TICKET_LIFETIME,
Long.parseLong(conf.getProperty(MiniKdc.MAX_TICKET_LIFETIME)));
}
Method refreshMethod = classRef.getMethod("refresh", new Class[0]);
refreshMethod.invoke(classRef, new Object[0]);
LOG.info("MiniKdc listening at port: {}", getPort());
LOG.info("MiniKdc setting JVM krb5.conf to: {}", krb5conf.getAbsolutePath());
}
/**
* Stops the MiniKdc
*/
public synchronized void stop() {
if (kdc != null) {
System.getProperties().remove("java.security.krb5.conf");
System.getProperties().remove("sun.security.krb5.debug");
kdc.stop();
if (simpleKdc != null) {
try {
ds.shutdown();
} catch (Exception ex) {
LOG.error("Could not shutdown ApacheDS properly: {}", ex.toString(), ex);
simpleKdc.stop();
} catch (KrbException e) {
e.printStackTrace();
} finally {
if (conf.getProperty(DEBUG) != null) {
System.setProperty(SUN_SECURITY_KRB5_DEBUG,
Boolean.toString(krb5Debug));
}
}
}
delete(workDir);
try {
// Will be fixed in next Kerby version.
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LOG.info("MiniKdc stopped.");
}
private void delete(File f) {
@@ -484,8 +368,11 @@ public class MiniKdc {
LOG.warn("WARNING: cannot delete file " + f.getAbsolutePath());
}
} else {
for (File c : f.listFiles()) {
delete(c);
File[] fileList = f.listFiles();
if (fileList != null) {
for (File c : fileList) {
delete(c);
}
}
if (!f.delete()) {
LOG.warn("WARNING: cannot delete directory " + f.getAbsolutePath());
@@ -497,71 +384,44 @@ public class MiniKdc {
* Creates a principal in the KDC with the specified user and password.
*
* @param principal principal name, do not include the domain.
* @param password password.
* @param password password.
* @throws Exception thrown if the principal could not be created.
*/
public synchronized void createPrincipal(String principal, String password) throws Exception {
String orgName = conf.getProperty(ORG_NAME);
String orgDomain = conf.getProperty(ORG_DOMAIN);
String baseDn = "ou=users,dc=" + orgName.toLowerCase() + ",dc=" + orgDomain.toLowerCase();
String content = "dn: uid=" + principal + "," + baseDn + "\n" + "objectClass: top\n" + "objectClass: person\n"
+ "objectClass: inetOrgPerson\n" + "objectClass: krb5principal\n" + "objectClass: krb5kdcentry\n"
+ "cn: " + principal + "\n" + "sn: " + principal + "\n" + "uid: " + principal + "\n" + "userPassword: "
+ password + "\n" + "krb5PrincipalName: " + principal + "@" + getRealm() + "\n"
+ "krb5KeyVersionNumber: 0";
for (LdifEntry ldifEntry : new LdifReader(new StringReader(content))) {
ds.getAdminSession().add(new DefaultEntry(ds.getSchemaManager(), ldifEntry.getEntry()));
}
public synchronized void createPrincipal(String principal, String password)
throws Exception {
simpleKdc.createPrincipal(principal, password);
}
/**
* Creates multiple principals in the KDC and adds them to a keytab file.
*
* @param keytabFile keytab file to add the created principal.s
* @param keytabFile keytab file to add the created principals.
* @param principals principals to add to the KDC, do not include the domain.
* @throws Exception thrown if the principals or the keytab file could not be created.
* @throws Exception thrown if the principals or the keytab file could not be
* created.
*/
public void createPrincipal(File keytabFile, String... principals) throws Exception {
String generatedPassword = UUID.randomUUID().toString();
Keytab keytab = new Keytab();
List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
for (String principal : principals) {
createPrincipal(principal, generatedPassword);
principal = principal + "@" + getRealm();
KerberosTime timestamp = new KerberosTime();
for (Map.Entry<EncryptionType, EncryptionKey> entry : KerberosKeyFactory.getKerberosKeys(principal,
generatedPassword).entrySet()) {
EncryptionKey ekey = entry.getValue();
byte keyVersion = (byte) ekey.getKeyVersion();
entries.add(new KeytabEntry(principal, 1L, timestamp, keyVersion, ekey));
}
public synchronized void createPrincipal(File keytabFile,
String... principals)
throws Exception {
simpleKdc.createPrincipals(principals);
if (keytabFile.exists() && !keytabFile.delete()) {
LOG.error("Failed to delete keytab file: " + keytabFile);
}
for (String principal : principals) {
simpleKdc.getKadmin().exportKeytab(keytabFile, principal);
}
keytab.setEntries(entries);
keytab.write(keytabFile);
}
/**
* Creates multiple principals in the KDC and adds them to a keytab file.
* Set the System property; return the old value for caching.
*
* @param keytabFile keytab file to add the created principal.
* @param principal The principal to store in the keytab file.
* @param password The password for the principal.
* @throws Exception thrown if the principals or the keytab file could not be created.
* @param sysprop property
* @param debug true or false
* @return the previous value
*/
public void createKeyabFile(File keytabFile, String principal, String password) throws Exception {
Keytab keytab = new Keytab();
List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
KerberosTime timestamp = new KerberosTime();
for (Map.Entry<EncryptionType, EncryptionKey> entry : KerberosKeyFactory.getKerberosKeys(principal,
password).entrySet()) {
EncryptionKey ekey = entry.getValue();
byte keyVersion = (byte) ekey.getKeyVersion();
entries.add(new KeytabEntry(principal, 1L, timestamp, keyVersion, ekey));
}
keytab.setEntries(entries);
keytab.write(keytabFile);
private boolean getAndSet(String sysprop, String debug) {
boolean old = Boolean.getBoolean(sysprop);
System.setProperty(sysprop, debug);
return old;
}
}

View File

@@ -1,11 +1,11 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2023 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
* 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,
@@ -15,12 +15,14 @@
*/
package org.springframework.security.kerberos.test;
import org.apache.directory.server.kerberos.shared.keytab.Keytab;
import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.security.kerberos.test.KerberosSecurityTestcase;
import org.springframework.security.kerberos.test.MiniKdc;
import java.io.File;
import java.security.Principal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
@@ -28,20 +30,46 @@ import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import java.io.File;
import java.security.Principal;
import java.util.Set;
import java.util.Map;
import java.util.HashSet;
import java.util.HashMap;
import java.util.Arrays;
import org.apache.kerby.kerberos.kerb.keytab.Keytab;
import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class TestMiniKdc extends KerberosSecurityTestcase {
private static final boolean IBM_JAVA = shouldUseIbmPackages();
// duplicated to avoid cycles in the build
private static boolean shouldUseIbmPackages() {
final List<String> ibmTechnologyEditionSecurityModules = Arrays.asList(
"com.ibm.security.auth.module.JAASLoginModule",
"com.ibm.security.auth.module.Win64LoginModule",
"com.ibm.security.auth.module.NTLoginModule",
"com.ibm.security.auth.module.AIX64LoginModule",
"com.ibm.security.auth.module.LinuxLoginModule",
"com.ibm.security.auth.module.Krb5LoginModule");
if (System.getProperty("java.vendor").contains("IBM")) {
return ibmTechnologyEditionSecurityModules
.stream().anyMatch((module) -> isSystemClassAvailable(module));
}
return false;
}
private static boolean isSystemClassAvailable(String className) {
try {
Class.forName(className);
return true;
} catch (Exception ignored) {
return false;
}
}
@Test
public void testMiniKdcStart() {
MiniKdc kdc = getKdc();
Assert.assertNotSame(0, kdc.getPort());
assertThat(kdc.getPort()).isNotEqualTo(0);
}
@Test
@@ -50,18 +78,15 @@ public class TestMiniKdc extends KerberosSecurityTestcase {
File workDir = getWorkDir();
kdc.createPrincipal(new File(workDir, "keytab"), "foo/bar", "bar/foo");
Keytab kt = Keytab.read(new File(workDir, "keytab"));
List<PrincipalName> principalNameList = Keytab.loadKeytab(new File(workDir, "keytab")).getPrincipals();
Set<String> principals = new HashSet<String>();
for (KeytabEntry entry : kt.getEntries()) {
principals.add(entry.getPrincipalName());
for (PrincipalName principalName : principalNameList) {
principals.add(principalName.getName());
}
// here principals use \ instead of /
// because
// org.apache.directory.server.kerberos.shared.keytab.KeytabDecoder
// .getPrincipalName(IoBuffer buffer) use \\ when generates principal
Assert.assertEquals(
new HashSet<String>(Arrays.asList("foo\\bar@" + kdc.getRealm(), "bar\\foo@" + kdc.getRealm())),
principals);
assertThat(principals).containsExactlyInAnyOrder("foo/bar@" + kdc.getRealm(), "bar/foo@" + kdc.getRealm());
}
private static class KerberosConfiguration extends Configuration {
@@ -69,45 +94,56 @@ public class TestMiniKdc extends KerberosSecurityTestcase {
private String keytab;
private boolean isInitiator;
private KerberosConfiguration(String principal, File keytab, boolean client) {
private KerberosConfiguration(String principal, File keytab,
boolean client) {
this.principal = principal;
this.keytab = keytab.getAbsolutePath();
this.isInitiator = client;
}
public static Configuration createClientConfig(String principal, File keytab) {
public static Configuration createClientConfig(String principal,
File keytab) {
return new KerberosConfiguration(principal, keytab, true);
}
public static Configuration createServerConfig(String principal, File keytab) {
public static Configuration createServerConfig(String principal,
File keytab) {
return new KerberosConfiguration(principal, keytab, false);
}
private static String getKrb5LoginModuleName() {
return System.getProperty("java.vendor").contains("IBM") ? "com.ibm.security.auth.module.Krb5LoginModule"
return System.getProperty("java.vendor").contains("IBM")
? "com.ibm.security.auth.module.Krb5LoginModule"
: "com.sun.security.auth.module.Krb5LoginModule";
}
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
Map<String, String> options = new HashMap<String, String>();
options.put("keyTab", keytab);
options.put("principal", principal);
options.put("useKeyTab", "true");
options.put("storeKey", "true");
options.put("doNotPrompt", "true");
options.put("useTicketCache", "true");
options.put("renewTGT", "true");
options.put("refreshKrb5Config", "true");
options.put("isInitiator", Boolean.toString(isInitiator));
if (IBM_JAVA) {
options.put("useKeytab", keytab);
options.put("credsType", "both");
} else {
options.put("keyTab", keytab);
options.put("useKeyTab", "true");
options.put("storeKey", "true");
options.put("doNotPrompt", "true");
options.put("useTicketCache", "true");
options.put("renewTGT", "true");
options.put("isInitiator", Boolean.toString(isInitiator));
}
String ticketCache = System.getenv("KRB5CCNAME");
if (ticketCache != null) {
options.put("ticketCache", ticketCache);
}
options.put("debug", "true");
return new AppConfigurationEntry[] { new AppConfigurationEntry(getKrb5LoginModuleName(),
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) };
return new AppConfigurationEntry[] {
new AppConfigurationEntry(getKrb5LoginModuleName(),
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options) };
}
}
@@ -125,29 +161,32 @@ public class TestMiniKdc extends KerberosSecurityTestcase {
principals.add(new KerberosPrincipal(principal));
// client login
Subject subject = new Subject(false, principals, new HashSet<Object>(), new HashSet<Object>());
loginContext = new LoginContext("", subject, null, KerberosConfiguration.createClientConfig(principal,
keytab));
Subject subject = new Subject(false, principals, new HashSet<Object>(),
new HashSet<Object>());
loginContext = new LoginContext("", subject, null,
KerberosConfiguration.createClientConfig(principal, keytab));
loginContext.login();
subject = loginContext.getSubject();
Assert.assertEquals(1, subject.getPrincipals().size());
Assert.assertEquals(KerberosPrincipal.class, subject.getPrincipals().iterator().next().getClass());
Assert.assertEquals(principal + "@" + kdc.getRealm(), subject.getPrincipals().iterator().next().getName());
assertThat(subject.getPrincipals().size()).isEqualTo(1);
assertThat(subject.getPrincipals().iterator().next().getClass()).isEqualTo(KerberosPrincipal.class);
assertThat(subject.getPrincipals().iterator().next().getName()).isEqualTo(principal + "@" + kdc.getRealm());
loginContext.logout();
// server login
subject = new Subject(false, principals, new HashSet<Object>(), new HashSet<Object>());
loginContext = new LoginContext("", subject, null, KerberosConfiguration.createServerConfig(principal,
keytab));
subject = new Subject(false, principals, new HashSet<Object>(),
new HashSet<Object>());
loginContext = new LoginContext("", subject, null,
KerberosConfiguration.createServerConfig(principal, keytab));
loginContext.login();
subject = loginContext.getSubject();
Assert.assertEquals(1, subject.getPrincipals().size());
Assert.assertEquals(KerberosPrincipal.class, subject.getPrincipals().iterator().next().getClass());
Assert.assertEquals(principal + "@" + kdc.getRealm(), subject.getPrincipals().iterator().next().getName());
assertThat(subject.getPrincipals().size()).isEqualTo(1);
assertThat(subject.getPrincipals().iterator().next().getClass()).isEqualTo(KerberosPrincipal.class);
assertThat(subject.getPrincipals().iterator().next().getName()).isEqualTo(principal + "@" + kdc.getRealm());
loginContext.logout();
} finally {
if (loginContext != null) {
if (loginContext != null && loginContext.getSubject() != null
&& !loginContext.getSubject().getPrivateCredentials().isEmpty()) {
loginContext.logout();
}
}