diff --git a/client/src/main/java/io/split/service/HTTPKerberosAuthInterceptor.java b/client/src/main/java/io/split/service/HTTPKerberosAuthInterceptor.java index 74c0160f..34fe0eaa 100644 --- a/client/src/main/java/io/split/service/HTTPKerberosAuthInterceptor.java +++ b/client/src/main/java/io/split/service/HTTPKerberosAuthInterceptor.java @@ -55,7 +55,7 @@ public HTTPKerberosAuthInterceptor(String host, Map krbOptions) t * Login Module to be used for authentication. * */ - private static class KerberosLoginConfiguration extends Configuration { + protected static class KerberosLoginConfiguration extends Configuration { Map krbOptions = null; public KerberosLoginConfiguration() {} @@ -66,7 +66,9 @@ public KerberosLoginConfiguration() {} } @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { - + if (krbOptions == null) { + throw new IllegalStateException("Cannot create AppConfigurationEntry without Kerberos Options"); + } return new AppConfigurationEntry[] { new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, krbOptions) }; } @@ -121,6 +123,12 @@ protected Subject getContextSubject() { return subject; } + protected CreateAuthorizationHeaderAction getAuthorizationHeaderAction(String clientPrincipal, + String serverPrincipalName) { + return new CreateAuthorizationHeaderAction(clientPrincipal, + serverPrincipalName); + } + /** * This method builds the Authorization header for Kerberos. It * generates a request token based on the service ticket, client principal name and @@ -137,7 +145,7 @@ protected String buildAuthorizationHeader(String serverPrincipalName) throws Log * client and server principal name for the GSS API */ final String clientPrincipal = getClientPrincipalName(); - final CreateAuthorizationHeaderAction action = new CreateAuthorizationHeaderAction(clientPrincipal, + final CreateAuthorizationHeaderAction action = getAuthorizationHeaderAction(clientPrincipal, serverPrincipalName); /* @@ -176,18 +184,18 @@ protected String buildAuthorizationHeader(String serverPrincipalName) throws Log * Subject.doAs() method. We do this in order to create a context of the user * who has the service ticket and reuse this context for subsequent requests */ - private static class CreateAuthorizationHeaderAction implements PrivilegedExceptionAction { + protected static class CreateAuthorizationHeaderAction implements PrivilegedExceptionAction { String clientPrincipalName; String serverPrincipalName; private StringBuilder outputToken = new StringBuilder(); - private CreateAuthorizationHeaderAction(final String clientPrincipalName, final String serverPrincipalName) { + protected CreateAuthorizationHeaderAction(final String clientPrincipalName, final String serverPrincipalName) { this.clientPrincipalName = clientPrincipalName; this.serverPrincipalName = serverPrincipalName; } - private String getNegotiateToken() { + protected String getNegotiateToken() { return outputToken.toString(); } diff --git a/client/src/test/java/io/split/service/HTTPKerberosAuthIntercepterTest.java b/client/src/test/java/io/split/service/HTTPKerberosAuthIntercepterTest.java index 353a765f..62bc453e 100644 --- a/client/src/test/java/io/split/service/HTTPKerberosAuthIntercepterTest.java +++ b/client/src/test/java/io/split/service/HTTPKerberosAuthIntercepterTest.java @@ -1,6 +1,5 @@ package io.split.service; -import org.glassfish.grizzly.http.server.Request; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; @@ -9,7 +8,9 @@ import javax.security.auth.Subject; import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.login.AppConfigurationEntry; import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; @@ -19,7 +20,10 @@ import static org.mockito.internal.verification.VerificationModeFactory.times; import static org.powermock.api.mockito.PowerMockito.*; +import java.security.PrivilegedActionException; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; @RunWith(PowerMockRunner.class) @@ -60,4 +64,50 @@ public void testBasicFlow() throws Exception { okhttp3.Request request = kerberosAuthInterceptor.authenticate(null, response); assertThat(request.headers("Proxy-authorization"), is(equalTo(Arrays.asList("Negotiate secured-token")))); } + + @Test + public void testKerberosLoginConfiguration() { + Map kerberosOptions = new HashMap(); + kerberosOptions.put("com.sun.security.auth.module.Krb5LoginModule", "required"); + kerberosOptions.put("refreshKrb5Config", "false"); + kerberosOptions.put("doNotPrompt", "false"); + kerberosOptions.put("useTicketCache", "true"); + + HTTPKerberosAuthInterceptor.KerberosLoginConfiguration kerberosConfig = new HTTPKerberosAuthInterceptor.KerberosLoginConfiguration(kerberosOptions); + AppConfigurationEntry[] appConfig = kerberosConfig.getAppConfigurationEntry(""); + assertThat("com.sun.security.auth.module.Krb5LoginModule", is(equalTo(appConfig[0].getLoginModuleName()))); + assertThat(AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, is(equalTo(appConfig[0].getControlFlag()))); + } + + @Test(expected = IllegalStateException.class) + public void testKerberosLoginConfigurationException() { + HTTPKerberosAuthInterceptor.KerberosLoginConfiguration kerberosConfig = new HTTPKerberosAuthInterceptor.KerberosLoginConfiguration(); + AppConfigurationEntry[] appConfig = kerberosConfig.getAppConfigurationEntry(""); + } + + @Test + public void testBuildAuthorizationHeader() throws LoginException, PrivilegedActionException { + System.setProperty("java.security.krb5.conf", "src/test/resources/krb5.conf"); + + HTTPKerberosAuthInterceptor kerberosAuthInterceptor = mock(HTTPKerberosAuthInterceptor.class); + HTTPKerberosAuthInterceptor.CreateAuthorizationHeaderAction ahh = mock(HTTPKerberosAuthInterceptor.CreateAuthorizationHeaderAction.class); + when(ahh.getNegotiateToken()).thenReturn("secret-token"); + when(kerberosAuthInterceptor.getAuthorizationHeaderAction(any(), any())).thenReturn(ahh); + + LoginContext loginContext = PowerMockito.mock(LoginContext.class); + doCallRealMethod().when(kerberosAuthInterceptor).buildAuthorizationHeader("bilal"); + Subject subject = new Subject(); + when(loginContext.getSubject()).thenReturn(subject); + when(kerberosAuthInterceptor.getContextSubject()).thenReturn(subject); + when(kerberosAuthInterceptor.getLoginContext(subject)).thenReturn((loginContext)); + doCallRealMethod().when(kerberosAuthInterceptor).buildSubjectCredentials(); + kerberosAuthInterceptor.buildSubjectCredentials(); + + subject.getPrincipals().add(new KerberosPrincipal("bilal")); + subject.getPublicCredentials().add(new KerberosPrincipal("name")); + subject.getPrivateCredentials().add(new KerberosPrincipal("name")); + doCallRealMethod().when(kerberosAuthInterceptor).getClientPrincipalName(); + + assertThat("secret-token", is(equalTo(kerberosAuthInterceptor.buildAuthorizationHeader("bilal")))); + } } diff --git a/client/src/test/java/io/split/service/HttpSplitClientKerberosTest.java b/client/src/test/java/io/split/service/HttpSplitClientKerberosTest.java index 25898e24..3ddf5c68 100644 --- a/client/src/test/java/io/split/service/HttpSplitClientKerberosTest.java +++ b/client/src/test/java/io/split/service/HttpSplitClientKerberosTest.java @@ -24,10 +24,8 @@ import org.apache.hc.core5.http.io.entity.EntityUtils; import org.junit.Assert; import org.junit.Test; -import org.mockito.Mockito; import java.io.*; -import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URISyntaxException; import java.net.HttpURLConnection; @@ -137,7 +135,7 @@ public void testGetErrors() throws IOException, InterruptedException { @Test - public void testGetParameters() throws URISyntaxException, IOException, InterruptedException { + public void testGetParameters() throws IOException, InterruptedException { class MyCustomHeaders implements CustomHeaderDecorator { public MyCustomHeaders() {} @Override @@ -192,8 +190,7 @@ public Map> getHeaderOverrides(RequestContext context) { } @Test(expected = IllegalStateException.class) - public void testException() throws URISyntaxException, InvocationTargetException, NoSuchMethodException, - IllegalAccessException, IOException { + public void testException() throws URISyntaxException, IOException { URI uri = new URI("https://api.split.io/splitChanges?since=1234567"); RequestDecorator decorator = null; @@ -211,7 +208,7 @@ public void testException() throws URISyntaxException, InvocationTargetException } @Test - public void testPost() throws URISyntaxException, IOException, ParseException, InterruptedException { + public void testPost() throws IOException, ParseException, InterruptedException { MockWebServer server = new MockWebServer(); server.enqueue(new MockResponse().addHeader(HttpHeaders.VIA, "HTTP/1.1 s_proxy_rio1")); @@ -286,7 +283,7 @@ public void testPostErrors() throws IOException, InterruptedException { } @Test(expected = IllegalStateException.class) - public void testPosttException() throws URISyntaxException, IOException { + public void testPosttException() throws URISyntaxException { RequestDecorator decorator = null; URI uri = new URI("https://kubernetesturl.com/split/api/testImpressions/bulk");