From d83215f757a00a4eac0ca58754fdbb9e8e895a74 Mon Sep 17 00:00:00 2001 From: Star Poon Date: Fri, 10 May 2024 09:50:39 +0900 Subject: [PATCH] Add OIDC integration test --- gateway-ha/pom.xml | 7 + .../trino/gateway/ha/HaGatewayTestUtils.java | 8 +- .../trino/gateway/ha/security/TestOIDC.java | 292 ++++++++++++++++++ .../src/test/resources/auth/localhost.jks | Bin 0 -> 2750 bytes .../auth/login_and_consent_server.py | 75 +++++ .../test/resources/auth/oauth-test-config.yml | 56 ++++ 6 files changed, 436 insertions(+), 2 deletions(-) create mode 100644 gateway-ha/src/test/java/io/trino/gateway/ha/security/TestOIDC.java create mode 100644 gateway-ha/src/test/resources/auth/localhost.jks create mode 100644 gateway-ha/src/test/resources/auth/login_and_consent_server.py create mode 100644 gateway-ha/src/test/resources/auth/oauth-test-config.yml diff --git a/gateway-ha/pom.xml b/gateway-ha/pom.xml index 8df6bf4c0..f91238eb9 100644 --- a/gateway-ha/pom.xml +++ b/gateway-ha/pom.xml @@ -429,6 +429,13 @@ test + + com.squareup.okhttp3 + okhttp-urlconnection + ${dep.okhttp3.version} + test + + org.assertj assertj-core diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/HaGatewayTestUtils.java b/gateway-ha/src/test/java/io/trino/gateway/ha/HaGatewayTestUtils.java index 69f42be65..86c6b43c7 100644 --- a/gateway-ha/src/test/java/io/trino/gateway/ha/HaGatewayTestUtils.java +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/HaGatewayTestUtils.java @@ -30,6 +30,8 @@ import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; +import java.net.URL; +import java.nio.file.Paths; import java.util.Map; import java.util.Random; import java.util.Scanner; @@ -87,19 +89,21 @@ public MockResponse dispatch(RecordedRequest request) } public static TestConfig buildGatewayConfigAndSeedDb(int routerPort, String configFile) - throws IOException + throws Exception { File baseDir = new File(System.getProperty("java.io.tmpdir")); File tempH2DbDir = new File(baseDir, "h2db-" + RANDOM.nextInt() + System.currentTimeMillis()); tempH2DbDir.deleteOnExit(); + URL resource = HaGatewayTestUtils.class.getClassLoader().getResource("auth/localhost.jks"); String configStr = getResourceFileContent(configFile) .replace("REQUEST_ROUTER_PORT", String.valueOf(routerPort)) .replace("DB_FILE_PATH", tempH2DbDir.getAbsolutePath()) .replace( "APPLICATION_CONNECTOR_PORT", String.valueOf(30000 + (int) (Math.random() * 1000))) - .replace("ADMIN_CONNECTOR_PORT", String.valueOf(31000 + (int) (Math.random() * 1000))); + .replace("ADMIN_CONNECTOR_PORT", String.valueOf(31000 + (int) (Math.random() * 1000))) + .replace("LOCALHOST_JKS", Paths.get(resource.toURI()).toFile().getAbsolutePath()); File target = File.createTempFile("config-" + System.currentTimeMillis(), "config.yaml"); diff --git a/gateway-ha/src/test/java/io/trino/gateway/ha/security/TestOIDC.java b/gateway-ha/src/test/java/io/trino/gateway/ha/security/TestOIDC.java new file mode 100644 index 000000000..d6a61173e --- /dev/null +++ b/gateway-ha/src/test/java/io/trino/gateway/ha/security/TestOIDC.java @@ -0,0 +1,292 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.trino.gateway.ha.security; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.trino.gateway.ha.HaGatewayLauncher; +import io.trino.gateway.ha.HaGatewayTestUtils; +import okhttp3.Cookie; +import okhttp3.CookieJar; +import okhttp3.HttpUrl; +import okhttp3.JavaNetCookieJar; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.testcontainers.containers.FixedHostPortGenericContainer; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.containers.wait.strategy.WaitAllStrategy; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import java.net.CookieManager; +import java.net.CookiePolicy; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.List; +import java.util.Optional; + +import static io.trino.gateway.ha.security.OidcCookie.OIDC_COOKIE; +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThat; +import static org.testcontainers.utility.MountableFile.forClasspathResource; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class TestOIDC +{ + private static final int TTL_ACCESS_TOKEN_IN_SECONDS = 5; + private static final int TTL_REFRESH_TOKEN_IN_SECONDS = 15; + + private static final String HYDRA_IMAGE = "oryd/hydra:v1.10.6"; + private static final String DSN = "postgres://hydra:mysecretpassword@hydra-db:5432/hydra?sslmode=disable"; + private static final int ROUTER_PORT = 21001 + (int) (Math.random() * 1000); + + @BeforeAll + public void setup() + throws Exception + { + Network network = Network.newNetwork(); + + PostgreSQLContainer databaseContainer = new PostgreSQLContainer<>("postgres:16") + .withNetwork(network) + .withNetworkAliases("hydra-db") + .withUsername("hydra") + .withPassword("mysecretpassword") + .withDatabaseName("hydra") + .waitingFor(Wait.forLogMessage(".*ready to accept connections.*", 1)); + databaseContainer.start(); + + GenericContainer migrationContainer = new GenericContainer(HYDRA_IMAGE) + .withNetwork(network) + .withCommand("migrate", "sql", "--yes", DSN) + .dependsOn(databaseContainer) + .withStartupCheckStrategy(new OneShotStartupCheckStrategy()); + migrationContainer.start(); + + FixedHostPortGenericContainer hydraConsent = new FixedHostPortGenericContainer<>("python:3.10.1-alpine") + .withFixedExposedPort(3000, 3000) + .withNetwork(network) + .withNetworkAliases("hydra-consent") + .withExposedPorts(3000) + .withCopyFileToContainer(forClasspathResource("auth/login_and_consent_server.py"), "/") + .withCommand("python", "/login_and_consent_server.py") + .waitingFor(Wait.forHttp("/healthz").forPort(3000).forStatusCode(200)); + hydraConsent.start(); + + FixedHostPortGenericContainer hydra = new FixedHostPortGenericContainer<>(HYDRA_IMAGE) + .withFixedExposedPort(4444, 4444) + .withFixedExposedPort(4445, 4445) + .withNetwork(network) + .withNetworkAliases("hydra") + .withEnv("LOG_LEVEL", "debug") + .withEnv("LOG_LEAK_SENSITIVE_VALUES", "true") + .withEnv("OAUTH2_EXPOSE_INTERNAL_ERRORS", "1") + .withEnv("GODEBUG", "http2debug=1") + .withEnv("DSN", DSN) + .withEnv("URLS_SELF_ISSUER", "http://localhost:4444/") + .withEnv("URLS_CONSENT", "http://localhost:3000/consent") + .withEnv("URLS_LOGIN", "http://localhost:3000/login") + .withEnv("STRATEGIES_ACCESS_TOKEN", "jwt") + .withEnv("TTL_ACCESS_TOKEN", TTL_ACCESS_TOKEN_IN_SECONDS + "s") + .withEnv("TTL_REFRESH_TOKEN", TTL_REFRESH_TOKEN_IN_SECONDS + "s") + .withEnv("OAUTH2_ALLOWED_TOP_LEVEL_CLAIMS", "groups") + .withCommand("serve", "all", "--dangerous-force-http") + .dependsOn(hydraConsent, migrationContainer) + .waitingFor(new WaitAllStrategy() + .withStrategy(Wait.forLogMessage(".*Setting up http server on :4444.*", 1)) + .withStrategy(Wait.forLogMessage(".*Setting up http server on :4445.*", 1))) + .withStartupTimeout(java.time.Duration.ofMinutes(3)); + + String clientId = "trino_client_id"; + String clientSecret = "trino_client_secret"; + String tokenEndpointAuthMethod = "client_secret_basic"; + String audience = "trino_client_id"; + String callbackUrl = format("https://localhost:%s/oidc/callback", ROUTER_PORT); + GenericContainer clientCreatingContainer = new GenericContainer(HYDRA_IMAGE) + .withNetwork(network) + .dependsOn(hydra) + .withCommand("clients", "create", + "--endpoint", "http://hydra:4445", + "--skip-tls-verify", + "--id", clientId, + "--secret", clientSecret, + "--audience", audience, + "-g", "authorization_code,refresh_token,client_credentials", + "-r", "token,code,id_token", + "--scope", "openid,offline", + "--token-endpoint-auth-method", tokenEndpointAuthMethod, + "--callbacks", callbackUrl); + clientCreatingContainer.start(); + + HaGatewayTestUtils.TestConfig testConfig = + HaGatewayTestUtils.buildGatewayConfigAndSeedDb(ROUTER_PORT, "auth/oauth-test-config.yml"); + String[] args = {"server", testConfig.configFilePath()}; + System.out.println(ROUTER_PORT); + HaGatewayLauncher.main(args); + } + + @Test + public void testNormalFlow() + throws Exception + { + OkHttpClient httpClient = createOkHttpClient(Optional.empty()); + String redirectURL; + try (Response response = httpClient.newCall(uiCall().build()).execute()) { + assertThat(response.header("Set-Cookie")).isNotNull(); + assertThat(response.header("Set-Cookie")).contains(OIDC_COOKIE); + redirectURL = extractRedirectURL(response.body().string()); + assertThat(redirectURL).contains("http://localhost:4444/"); + } + Request oidcRequest = new Request.Builder() + .url(redirectURL) + .get() + .build(); + try (Response response = httpClient.newCall(oidcRequest).execute()) { + assertThat(response.request().url().host()).isEqualTo("localhost"); + assertThat(response.request().url().port()).isEqualTo(ROUTER_PORT); + assertThat(response.request().url().encodedPath()).isEqualTo("/"); + assertThat(response.code()).isEqualTo(200); + } + } + + @Test + public void testInvalidFlow() + throws Exception + { + OkHttpClient httpClient = createOkHttpClient(Optional.empty()); + + String redirectURL; + try (Response response = httpClient.newCall(uiCall().build()).execute()) { + redirectURL = extractRedirectURL(response.body().string()); + assertThat(redirectURL).contains("http://localhost:4444/"); + } + + Request oidcRequest = new Request.Builder() + .url(redirectURL) + .get() + .build(); + OkHttpClient httpClientBadCookie = createOkHttpClient(Optional.of(new BadCookieJar())); + try (Response response = httpClientBadCookie.newCall(oidcRequest).execute()) { + assertThat(response.request().url().host()).isEqualTo("localhost"); + assertThat(response.request().url().port()).isEqualTo(ROUTER_PORT); + assertThat(response.code()).isEqualTo(401); + } + } + + private Request.Builder uiCall() + { + return new Request.Builder() + .url(format("https://localhost:%s/sso", ROUTER_PORT)) + .post(RequestBody.create("", null)); + } + + public static void setupInsecureSsl(OkHttpClient.Builder clientBuilder) + throws Exception + { + X509TrustManager trustAllCerts = new X509TrustManager() + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + throw new UnsupportedOperationException("checkClientTrusted should not be called"); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + // skip validation of server certificate + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[0]; + } + }; + + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, new TrustManager[] {trustAllCerts}, new SecureRandom()); + + clientBuilder.sslSocketFactory(sslContext.getSocketFactory(), trustAllCerts); + clientBuilder.hostnameVerifier((hostname, session) -> true); + } + + public class BadCookieJar + implements CookieJar + { + private JavaNetCookieJar cookieJar; + + public BadCookieJar() + { + CookieManager cookieManager = new CookieManager(); + cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); + cookieJar = new JavaNetCookieJar(cookieManager); + } + + @Override + public void saveFromResponse(HttpUrl url, List cookies) + { + cookieJar.saveFromResponse(url, cookies); + } + + @Override + public List loadForRequest(HttpUrl url) + { + if (url.host().equals("localhost") && url.port() == ROUTER_PORT) { + Cookie cookie = new Cookie.Builder() + .name(OIDC_COOKIE) + .value("BAD_STATE|BAD_NONCE") + .domain("localhost") + .build(); + return List.of(cookie); + } + else { + return cookieJar.loadForRequest(url); + } + } + } + + private static String extractRedirectURL(String body) + throws JsonProcessingException + { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(body); + return jsonNode.get("data").asText(); + } + + private static OkHttpClient createOkHttpClient(Optional cookieJar) + throws Exception + { + OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder() + .followRedirects(true) + .cookieJar(cookieJar.orElseGet(() -> { + CookieManager cookieManager = new CookieManager(); + cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); + return new JavaNetCookieJar(cookieManager); + })); + setupInsecureSsl(httpClientBuilder); + return httpClientBuilder.build(); + } +} diff --git a/gateway-ha/src/test/resources/auth/localhost.jks b/gateway-ha/src/test/resources/auth/localhost.jks new file mode 100644 index 0000000000000000000000000000000000000000..0ecba65865275ab849157324ba8f01e621651106 GIT binary patch literal 2750 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 bBoHYg4wIY$hox-hM8*_}b0yC1UrPELN?PJQ literal 0 HcmV?d00001 diff --git a/gateway-ha/src/test/resources/auth/login_and_consent_server.py b/gateway-ha/src/test/resources/auth/login_and_consent_server.py new file mode 100644 index 000000000..bcf9a37ed --- /dev/null +++ b/gateway-ha/src/test/resources/auth/login_and_consent_server.py @@ -0,0 +1,75 @@ +import json +import os +import ssl +from http.server import BaseHTTPRequestHandler, HTTPServer +from typing import Dict, Any +from urllib.parse import urlparse, parse_qs +from urllib.request import Request, urlopen + +HYDRA_ADMIN_URL = os.getenv("HYDRA_ADMIN_URL", "http://hydra:4445") +PORT = os.getenv("PORT", 3000) +SSL_CONTEXT = ssl.create_default_context() +SSL_CONTEXT.check_hostname = False +SSL_CONTEXT.verify_mode = ssl.CERT_NONE + + +class LoginAndConsentServer(BaseHTTPRequestHandler): + def do_GET(self): + params = parse_qs(urlparse(self.path).query) + if self.path == "/healthz": + self.send_response(200) + if self.path.startswith("/login"): + self.accept_login(params) + return + if self.path.startswith("/consent"): + self.accept_consent(params) + return + self.send_error(404, "Not found") + + def accept_login(self, params: Dict[str, list[str]]) -> None: + login_challenge = params["login_challenge"][0] + with urlopen(Request( + method="PUT", + url=HYDRA_ADMIN_URL + "/oauth2/auth/requests/login/accept?login_challenge=" + login_challenge, + headers={"Content-Type": "application/json; charset=UTF-8"}, + data=json.dumps({"subject": "foo@bar.com"}).encode()), + context=SSL_CONTEXT) as response: + self.send_redirect(response) + + def accept_consent(self, params: Dict[str, list[str]]) -> None: + consent_challenge = params["consent_challenge"][0] + consent_request = self.get_consent_request(consent_challenge) + with urlopen(Request( + method="PUT", + url=HYDRA_ADMIN_URL + "/oauth2/auth/requests/consent/accept?consent_challenge=" + consent_challenge, + headers={"Content-Type": "application/json; charset=UTF-8"}, + data=json.dumps({ + "grant_scope": consent_request["requested_scope"], + "grant_access_token_audience": consent_request["requested_access_token_audience"], + "session": { + "access_token": { + "groups": ["admin", "public"] + } + }}).encode()), + context=SSL_CONTEXT) as response: + self.send_redirect(response) + + @staticmethod + def get_consent_request(consent_challenge: str) -> Dict[str, Any]: + with urlopen(Request( + method="GET", + url=HYDRA_ADMIN_URL + "/oauth2/auth/requests/consent?consent_challenge=" + consent_challenge), + context=SSL_CONTEXT) as response: + return json.load(response) + + def send_redirect(self, response): + body = json.load(response) + self.send_response(302) + self.send_header("Location", body["redirect_to"]) + self.end_headers() + + +if __name__ == "__main__": + server_address = ("", PORT) + httpd = HTTPServer(server_address, LoginAndConsentServer) + httpd.serve_forever() diff --git a/gateway-ha/src/test/resources/auth/oauth-test-config.yml b/gateway-ha/src/test/resources/auth/oauth-test-config.yml new file mode 100644 index 000000000..ca415261e --- /dev/null +++ b/gateway-ha/src/test/resources/auth/oauth-test-config.yml @@ -0,0 +1,56 @@ +requestRouter: + port: REQUEST_ROUTER_PORT + name: testTrinoRouter + ssl: true + keystorePath: LOCALHOST_JKS + keystorePass: 123456 + +server: + applicationConnectors: + - type: http + port: APPLICATION_CONNECTOR_PORT + adminConnectors: + - type: http + port: ADMIN_CONNECTOR_PORT + +dataStore: + jdbcUrl: jdbc:h2:DB_FILE_PATH + user: sa + password: sa + driver: org.h2.Driver + +modules: + - io.trino.gateway.ha.module.HaGatewayProviderModule + +managedApps: + - io.trino.gateway.ha.GatewayManagedApp + +extraWhitelistPaths: + - "/v1/custom" + +logging: + type: external + +authorization: + admin: .*FOO.* + user: .*BAR.* + api: .*BAZ.* + +presetUsers: + foo@bar.com: + privileges: FOO_BAR + +authentication: + defaultType: "oauth" + oauth: + issuer: http://localhost:4444/ + clientId: trino_client_id + clientSecret: trino_client_secret + tokenEndpoint: http://localhost:4444/oauth2/token + authorizationEndpoint: http://localhost:4444/oauth2/auth + jwkEndpoint: http://localhost:4444/.well-known/jwks.json + redirectUrl: https://localhost:REQUEST_ROUTER_PORT/oidc/callback + redirectWebUrl: https://localhost:REQUEST_ROUTER_PORT/ + userIdField: sub + scopes: + - openid