package io.kubernetes.client.util.credentials; import static com.github.tomakehurst.wiremock.client.WireMock.*; import static org.junit.Assert.*; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import com.github.tomakehurst.wiremock.junit.WireMockRule; import com.google.common.io.Resources; import io.kubernetes.client.util.TestUtils; import io.kubernetes.client.util.authenticators.OpenIDConnectAuthenticator; import java.io.FileInputStream; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.util.HashMap; import java.util.Map; import org.apache.commons.codec.binary.Base64; import org.jose4j.json.internal.json_simple.JSONObject; import org.junit.Rule; import org.junit.Test; /** OpenIDConnectAuthenticationTest */ public class OpenIDConnectAuthenticationTest { private static final String OIDC_KS_PATH = Resources.getResource("oidc-signing.p12").getPath(); private static final String OIDC_SERVER_KS_PATH = Resources.getResource("oidc-server.jks").getPath(); private static final char[] OIDC_KS_PASSWORD = "changeme".toCharArray(); private static final int PORT = 8043; @Rule public WireMockRule wireMockRule = new WireMockRule( WireMockConfiguration.options() .httpsPort(PORT) .keystoreType("JKS") .keystorePath(OIDC_SERVER_KS_PATH) .keystorePassword("changeme")); @Test public void testTokenExpiredNotExpired() throws InvalidKeySpecException, NoSuchAlgorithmException, Exception { OpenIDConnectAuthenticator oidcAuth = new OpenIDConnectAuthenticator(); Map<String, Object> config = new HashMap<String, Object>(); KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(new FileInputStream(OIDC_KS_PATH), OIDC_KS_PASSWORD); String jwt = TestUtils.generateJWT( "someuser", "https://some.domain.nowhere", (PrivateKey) ks.getKey("oidc-sig", OIDC_KS_PASSWORD), TestUtils.DateOptions.Now); config.put(OpenIDConnectAuthenticator.OIDC_ID_TOKEN, jwt); assertFalse(oidcAuth.isExpired(config)); } @Test public void testTokenExpiredHasExpired() throws InvalidKeySpecException, NoSuchAlgorithmException, Exception { OpenIDConnectAuthenticator oidcAuth = new OpenIDConnectAuthenticator(); Map<String, Object> config = new HashMap<String, Object>(); KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(new FileInputStream(OIDC_KS_PATH), OIDC_KS_PASSWORD); String jwt = TestUtils.generateJWT( "someuser", "https://some.domain.nowhere", (PrivateKey) ks.getKey("oidc-sig", OIDC_KS_PASSWORD), TestUtils.DateOptions.Past); config.put(OpenIDConnectAuthenticator.OIDC_ID_TOKEN, jwt); assertTrue(oidcAuth.isExpired(config)); } public void testTokenExpiredNull() throws InvalidKeySpecException, NoSuchAlgorithmException, Exception { OpenIDConnectAuthenticator oidcAuth = new OpenIDConnectAuthenticator(); Map<String, Object> config = new HashMap<String, Object>(); // no id_token assertTrue(oidcAuth.isExpired(config)); } @Test public void testLoadToken() throws InvalidKeySpecException, NoSuchAlgorithmException, Exception { OpenIDConnectAuthenticator oidcAuth = new OpenIDConnectAuthenticator(); Map<String, Object> config = new HashMap<String, Object>(); KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(new FileInputStream(OIDC_KS_PATH), OIDC_KS_PASSWORD); String jwt = TestUtils.generateJWT( "someuser", "https://some.domain.nowhere", (PrivateKey) ks.getKey("oidc-sig", OIDC_KS_PASSWORD), TestUtils.DateOptions.Now); config.put(OpenIDConnectAuthenticator.OIDC_ID_TOKEN, jwt); assertEquals(oidcAuth.getToken(config), jwt); } @Test public void testLoadNullToken() throws InvalidKeySpecException, NoSuchAlgorithmException, Exception { OpenIDConnectAuthenticator oidcAuth = new OpenIDConnectAuthenticator(); Map<String, Object> config = new HashMap<String, Object>(); assertNull(oidcAuth.getToken(config)); } @Test public void testRefreshSuccess() throws Exception { KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(new FileInputStream(OIDC_KS_PATH), OIDC_KS_PASSWORD); String refreshedJWT = TestUtils.generateJWT( "someuser", "https://localhost:" + PORT, (PrivateKey) ks.getKey("oidc-sig", OIDC_KS_PASSWORD), TestUtils.DateOptions.Now); stubFor( get("/.well-known/openid-configuration") .willReturn( aResponse() .withStatus(200) .withBody( "{\"issuer\":\"https://localhost:8043\",\"authorization_endpoint\":\"https://localhost:8043/auth\",\"token_endpoint\":\"https://localhost:8043/token\",\"userinfo_endpoint\":\"https://localhost:8043/userinfo\",\"revocation_endpoint\":\"https://localhost:8043/revoke\",\"jwks_uri\":\"https://localhost:8043/certs\",\"response_types_supported\":[\"code\",\"token\",\"id_token\",\"code token\",\"code id_token\",\"token id_token\",\"code token id_token\",\"none\"],\"subject_types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"scopes_supported\":[\"openid\",\"email\",\"profile\"],\"token_endpoint_auth_methods_supported\":[\"client_secret_post\"],\"claims_supported\":[\"sub\",\"aud\",\"iss\",\"exp\",\"sub\",\"name\",\"groups\",\"preferred_username\",\"email\"],\"code_challenge_methods_supported\":[\"plain\",\"S256\"]}"))); JSONObject respToken = new JSONObject(); respToken.put("id_token", refreshedJWT); respToken.put("refresh_token", "new_refresh_token"); stubFor( post("/token") .withBasicAuth("kubernetes", "") .withRequestBody(matching("refresh_token=refresh-me-please&grant_type=refresh_token")) .willReturn(aResponse().withStatus(200).withBody(respToken.toString()))); OpenIDConnectAuthenticator oidcAuth = new OpenIDConnectAuthenticator(); Map<String, Object> config = new HashMap<String, Object>(); KeyStore serverKs = KeyStore.getInstance("JKS"); serverKs.load(new FileInputStream(OIDC_SERVER_KS_PATH), OIDC_KS_PASSWORD); String jwt = TestUtils.generateJWT( "someuser", "https://localhost:" + PORT, (PrivateKey) ks.getKey("oidc-sig", OIDC_KS_PASSWORD), TestUtils.DateOptions.Past); config.put(OpenIDConnectAuthenticator.OIDC_ID_TOKEN, jwt); config.put(OpenIDConnectAuthenticator.OIDC_ISSUER, "https://localhost:" + PORT); config.put(OpenIDConnectAuthenticator.OIDC_CLIENT_ID, "kubernetes"); config.put(OpenIDConnectAuthenticator.OIDC_REFRESH_TOKEN, "refresh-me-please"); config.put( OpenIDConnectAuthenticator.OIDC_IDP_CERT_DATA, Base64.encodeBase64String( exportCert((X509Certificate) serverKs.getCertificate("mykey")).getBytes("UTF-8"))); Map<String, Object> respMap = oidcAuth.refresh(config); assertEquals(refreshedJWT, respMap.get(OpenIDConnectAuthenticator.OIDC_ID_TOKEN)); assertEquals("new_refresh_token", respMap.get(OpenIDConnectAuthenticator.OIDC_REFRESH_TOKEN)); } @Test(expected = RuntimeException.class) public void testRefreshUnauthorized() throws Exception { KeyStore ks = KeyStore.getInstance("PKCS12"); ks.load(new FileInputStream(OIDC_KS_PATH), OIDC_KS_PASSWORD); String refreshedJWT = TestUtils.generateJWT( "someuser", "https://localhost:" + PORT, (PrivateKey) ks.getKey("oidc-sig", OIDC_KS_PASSWORD), TestUtils.DateOptions.Now); stubFor( get("/.well-known/openid-configuration") .willReturn( aResponse() .withStatus(200) .withBody( "{\"issuer\":\"https://localhost:8043\",\"authorization_endpoint\":\"https://localhost:8043/auth\",\"token_endpoint\":\"https://localhost:8043/token\",\"userinfo_endpoint\":\"https://localhost:8043/userinfo\",\"revocation_endpoint\":\"https://localhost:8043/revoke\",\"jwks_uri\":\"https://localhost:8043/certs\",\"response_types_supported\":[\"code\",\"token\",\"id_token\",\"code token\",\"code id_token\",\"token id_token\",\"code token id_token\",\"none\"],\"subject_types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"scopes_supported\":[\"openid\",\"email\",\"profile\"],\"token_endpoint_auth_methods_supported\":[\"client_secret_post\"],\"claims_supported\":[\"sub\",\"aud\",\"iss\",\"exp\",\"sub\",\"name\",\"groups\",\"preferred_username\",\"email\"],\"code_challenge_methods_supported\":[\"plain\",\"S256\"]}"))); JSONObject respToken = new JSONObject(); respToken.put("id_token", refreshedJWT); respToken.put("refresh_token", "new_refresh_token"); stubFor(post("/token").willReturn(aResponse().withStatus(401))); OpenIDConnectAuthenticator oidcAuth = new OpenIDConnectAuthenticator(); Map<String, Object> config = new HashMap<String, Object>(); KeyStore serverKs = KeyStore.getInstance("JKS"); serverKs.load(new FileInputStream(OIDC_SERVER_KS_PATH), OIDC_KS_PASSWORD); String jwt = TestUtils.generateJWT( "someuser", "https://localhost:" + PORT, (PrivateKey) ks.getKey("oidc-sig", OIDC_KS_PASSWORD), TestUtils.DateOptions.Past); config.put(OpenIDConnectAuthenticator.OIDC_ID_TOKEN, jwt); config.put(OpenIDConnectAuthenticator.OIDC_ISSUER, "https://localhost:" + PORT); config.put(OpenIDConnectAuthenticator.OIDC_CLIENT_ID, "kubernetes"); config.put(OpenIDConnectAuthenticator.OIDC_REFRESH_TOKEN, "refresh-me-please"); config.put( OpenIDConnectAuthenticator.OIDC_IDP_CERT_DATA, Base64.encodeBase64String( exportCert((X509Certificate) serverKs.getCertificate("mykey")).getBytes("UTF-8"))); Map<String, Object> respMap = oidcAuth.refresh(config); } private static String exportCert(X509Certificate cert) throws Exception { Base64 encoder = new Base64(64); String b64 = encoder.encodeToString(cert.getEncoded()); b64 = "-----BEGIN CERTIFICATE-----\n" + b64 + "-----END CERTIFICATE-----\n"; return b64; } }