Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@

import com.dremio.iceberg.authmgr.oauth2.http.HttpClient;
import com.dremio.iceberg.authmgr.oauth2.test.TestEnvironment;
import com.dremio.iceberg.authmgr.oauth2.test.server.UnitTestHttpServer;
import com.dremio.iceberg.authmgr.oauth2.test.TestServer;
import com.nimbusds.oauth2.sdk.ParseException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.junitpioneer.jupiter.cartesian.CartesianTest;
import org.junitpioneer.jupiter.cartesian.CartesianTest.Values;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import org.mockserver.model.JsonBody;
Expand All @@ -50,12 +49,19 @@ void withoutDiscovery() {
}
}

@ParameterizedTest
@ValueSource(booleans = {true, false})
void withDiscovery(boolean includeDeviceAuthEndpoint) {
@CartesianTest
void withDiscovery(
@Values(booleans = {true, false}) boolean includeDeviceAuthEndpoint,
@Values(
strings = {
".well-known/openid-configuration",
".well-known/oauth-authorization-server"
})
String wellKnownPath) {
try (TestEnvironment env =
TestEnvironment.builder()
.includeDeviceAuthEndpointInDiscoveryMetadata(includeDeviceAuthEndpoint)
.wellKnownPath(wellKnownPath)
.build()) {
EndpointProvider endpointProvider =
EndpointProvider.create(env.getOAuth2Config(), HttpClient.DEFAULT);
Expand All @@ -74,33 +80,6 @@ void withDiscovery(boolean includeDeviceAuthEndpoint) {
}
}

@ParameterizedTest
@CsvSource({
"'' , /.well-known/openid-configuration",
"/ , /.well-known/openid-configuration",
"'' , /.well-known/oauth-authorization-server",
"/ , /.well-known/oauth-authorization-server",
"/realms/master , /realms/master/.well-known/openid-configuration",
"/realms/master/ , /realms/master/.well-known/openid-configuration",
"/realms/master , /realms/master/.well-known/oauth-authorization-server",
"/realms/master/ , /realms/master/.well-known/oauth-authorization-server"
})
void fetchOpenIdProviderMetadataSuccess(String contextPath, String wellKnownPath) {
Comment thread
adutra marked this conversation as resolved.
try (TestEnvironment env =
TestEnvironment.builder()
.authorizationServerContextPath(contextPath)
.wellKnownPath(wellKnownPath)
.build()) {
EndpointProvider endpointProvider =
EndpointProvider.create(env.getOAuth2Config(), HttpClient.DEFAULT);
var actual = endpointProvider.getOpenIdProviderMetadata();
assertThat(actual.getTokenEndpointURI()).isEqualTo(env.getTokenEndpoint());
assertThat(actual.getAuthorizationEndpointURI()).isEqualTo(env.getAuthorizationEndpoint());
assertThat(actual.getDeviceAuthorizationEndpointURI())
.isEqualTo(env.getDeviceAuthorizationEndpoint());
}
}

@Test
void fetchOpenIdProviderMetadataWrongEndpoint() {
try (TestEnvironment env = TestEnvironment.builder().createDefaultExpectations(false).build()) {
Expand Down Expand Up @@ -128,9 +107,8 @@ void fetchOpenIdProviderMetadataWrongEndpoint() {
@Test
void fetchOpenIdProviderMetadataWrongData() {
try (TestEnvironment env = TestEnvironment.builder().createDefaultExpectations(false).build()) {
((UnitTestHttpServer) env.getServer())
.getClientAndServer()
.when(HttpRequest.request())
TestServer.getInstance()
.when(HttpRequest.request().withPath(env.getAuthorizationServerUrl().getPath() + ".*"))
.respond(
HttpResponse.response()
.withStatusCode(200)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
import com.dremio.iceberg.authmgr.oauth2.test.expectation.ImmutablePasswordExpectation;
import com.dremio.iceberg.authmgr.oauth2.test.expectation.ImmutableRefreshTokenExpectation;
import com.dremio.iceberg.authmgr.oauth2.test.expectation.ImmutableTokenExchangeExpectation;
import com.dremio.iceberg.authmgr.oauth2.test.server.HttpServer;
import com.dremio.iceberg.authmgr.oauth2.test.server.IntegrationTestHttpServer;
import com.dremio.iceberg.authmgr.oauth2.test.server.UnitTestHttpServer;
import com.dremio.iceberg.authmgr.oauth2.test.user.InteractiveUserEmulator;
import com.dremio.iceberg.authmgr.oauth2.test.user.UserBehavior;
import com.dremio.iceberg.authmgr.oauth2.test.user.UserEmulator;
Expand Down Expand Up @@ -79,6 +76,7 @@
import java.util.OptionalInt;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLContext;
import org.apache.iceberg.CatalogProperties;
import org.apache.iceberg.catalog.SessionCatalog.SessionContext;
Expand All @@ -94,6 +92,8 @@
@Value.Immutable(copy = false)
public abstract class TestEnvironment implements AutoCloseable {

private static final AtomicInteger ID_COUNTER = new AtomicInteger();

public static Builder builder() {
return ImmutableTestEnvironment.builder();
}
Expand All @@ -105,6 +105,11 @@ public void validate() {
}
}

@Value.Default
public String getId() {
return "env" + ID_COUNTER.incrementAndGet();
}

@Value.Default
public GrantType getGrantType() {
return GrantType.CLIENT_CREDENTIALS;
Expand Down Expand Up @@ -140,11 +145,6 @@ public boolean isCreateDefaultExpectations() {
return isUnitTest();
}

@Value.Lazy
public HttpServer getServer() {
return isUnitTest() ? new UnitTestHttpServer(isSsl()) : new IntegrationTestHttpServer();
}

@Value.Default
public boolean isSsl() {
return false;
Expand All @@ -161,7 +161,9 @@ public int getExecutorPoolSize() {
}

public void reset() {
getServer().reset();
if (isUnitTest()) {
TestServer.clear(getId());
}
}

@Override
Expand All @@ -175,24 +177,31 @@ public void close() {
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
getServer().close();
reset();
}

@Value.Default
public URI getServerRootUrl() {
// Note: the default value is for unit tests; integration tests must provide the server root URL
// to avoid circular dependencies when creating the TestEnvironment instance
return getServer().getRootUrl();
if (!isUnitTest()) {
throw new IllegalStateException("Server root URL must be provided for integration tests");
}
return URI.create(
(isSsl() ? "https" : "http")
+ "://localhost:"
+ TestServer.getInstance().getLocalPort()
+ "/"
+ getId()
+ "/");
}

@Value.Default
public String getAuthorizationServerContextPath() {
return "/realms/master/";
return "realms/master/";
}

@Value.Default
public String getCatalogServerContextPath() {
return "/api/catalog/";
return "api/catalog/";
}

@Value.Default
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (C) 2025 Dremio Corporation
*
* 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 com.dremio.iceberg.authmgr.oauth2.test;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.mockserver.configuration.Configuration;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.model.HttpRequest;

public final class TestServer {

private static final class Holder {

private static final ClientAndServer INSTANCE;

static {
Configuration configuration = Configuration.configuration();
String outputDir = System.getProperty("authmgr.test.mockserver.memoryUsageCsvDirectory");
if (outputDir != null) {
Path outputPath = Paths.get(outputDir);
try {
Files.createDirectories(outputPath);
} catch (IOException e) {
throw new RuntimeException(e);
}
configuration.outputMemoryUsageCsv(true);
configuration.memoryUsageCsvDirectory(outputPath.toString());
}
INSTANCE = ClientAndServer.startClientAndServer(configuration);
Runtime.getRuntime().addShutdownHook(new Thread(INSTANCE::close));
}
}

private TestServer() {}

public static ClientAndServer getInstance() {
return Holder.INSTANCE;
}

/** Clears all expectations and responses for the given test environment id. */
@SuppressWarnings("resource")
public static void clear(String testEnvironmentId) {
getInstance().clear(HttpRequest.request().withPath("/" + testEnvironmentId + "/.*"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,12 @@
package com.dremio.iceberg.authmgr.oauth2.test.expectation;

import com.dremio.iceberg.authmgr.oauth2.test.TestEnvironment;
import com.dremio.iceberg.authmgr.oauth2.test.server.UnitTestHttpServer;
import org.immutables.value.Value;
import org.mockserver.integration.ClientAndServer;

public abstract class AbstractExpectation {

@Value.Parameter(order = 1)
protected abstract TestEnvironment getTestEnvironment();

public abstract void create();

protected ClientAndServer getClientAndServer() {
return ((UnitTestHttpServer) getTestEnvironment().getServer()).getClientAndServer();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static com.dremio.iceberg.authmgr.oauth2.test.TestConstants.SCOPE2;
import static com.dremio.iceberg.authmgr.oauth2.test.expectation.ErrorExpectation.AUTHORIZATION_SERVER_ERROR_RESPONSE;

import com.dremio.iceberg.authmgr.oauth2.test.TestServer;
import com.dremio.iceberg.authmgr.tools.immutables.AuthManagerImmutable;
import com.google.common.collect.ImmutableMap;
import com.nimbusds.oauth2.sdk.AuthorizationCode;
Expand Down Expand Up @@ -120,7 +121,7 @@ private void createAuthEndpointExpectation() {
request.withQueryStringParameter(
"code_challenge_method", getTestEnvironment().getCodeChallengeMethod().getValue());
}
getClientAndServer()
TestServer.getInstance()
.when(request)
.respond(
httpRequest -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static com.dremio.iceberg.authmgr.oauth2.test.expectation.ExpectationUtils.getJsonBody;

import com.dremio.iceberg.authmgr.oauth2.test.TestServer;
import com.dremio.iceberg.authmgr.tools.immutables.AuthManagerImmutable;
import com.google.common.collect.ImmutableList;
import java.util.HashMap;
Expand Down Expand Up @@ -61,7 +62,7 @@ public void create() {
.withOverrides(new HashMap<>())
.withEndpoints(endpoints)
.build();
getClientAndServer()
TestServer.getInstance()
.when(
HttpRequest.request()
.withMethod("GET")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static com.dremio.iceberg.authmgr.oauth2.test.expectation.ErrorExpectation.AUTHORIZATION_SERVER_ERROR_RESPONSE;
import static org.mockserver.model.Parameter.param;

import com.dremio.iceberg.authmgr.oauth2.test.TestServer;
import com.dremio.iceberg.authmgr.tools.immutables.AuthManagerImmutable;
import com.google.common.collect.ImmutableMap;
import com.nimbusds.oauth2.sdk.GrantType;
Expand Down Expand Up @@ -82,7 +83,7 @@ protected HttpResponse response(
}

private void createDeviceAuthEndpointExpectation() {
getClientAndServer()
TestServer.getInstance()
.when(
HttpRequest.request()
.withMethod("POST")
Expand Down Expand Up @@ -119,7 +120,7 @@ private void createDeviceAuthEndpointExpectation() {
private void createDeviceVerificationEndpointExpectation() {
String path = getTestEnvironment().getDeviceVerificationEndpoint().getPath();
// Expect the device verification page to be opened in a browser
getClientAndServer()
TestServer.getInstance()
.when(HttpRequest.request().withMethod("GET").withPath(path))
.respond(
HttpResponse.response()
Expand All @@ -137,7 +138,7 @@ private void createDeviceVerificationEndpointExpectation() {
+ "</body></html>",
MediaType.TEXT_HTML)));
// Expect the device verification code to be sent by the user after opening the page
getClientAndServer()
TestServer.getInstance()
.when(
HttpRequest.request()
.withMethod("POST")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.dremio.iceberg.authmgr.oauth2.test.expectation;

import com.dremio.iceberg.authmgr.oauth2.test.TestServer;
import com.dremio.iceberg.authmgr.tools.immutables.AuthManagerImmutable;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
Expand Down Expand Up @@ -42,15 +43,15 @@ public abstract class ErrorExpectation extends AbstractExpectation {

@Override
public void create() {
getClientAndServer()
TestServer.getInstance()
.when(
HttpRequest.request()
.withPath(getTestEnvironment().getAuthorizationServerContextPath() + ".*"))
.withPath(getTestEnvironment().getAuthorizationServerUrl().getPath() + ".*"))
.respond(AUTHORIZATION_SERVER_ERROR_RESPONSE);
getClientAndServer()
TestServer.getInstance()
.when(
HttpRequest.request()
.withPath(getTestEnvironment().getCatalogServerContextPath() + ".*"))
.withPath(getTestEnvironment().getCatalogServerUrl().getPath() + ".*"))
.respond(CATALOG_SERVER_ERROR_RESPONSE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
*/
package com.dremio.iceberg.authmgr.oauth2.test.expectation;

import com.dremio.iceberg.authmgr.oauth2.test.TestServer;

public abstract class InitialTokenFetchExpectation extends AbstractTokenEndpointExpectation {

@Override
public void create() {
getClientAndServer()
TestServer.getInstance()
.when(request())
.respond(httpRequest -> response(httpRequest, "access_initial", "refresh_initial"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static com.dremio.iceberg.authmgr.oauth2.test.expectation.ExpectationUtils.getJsonBody;

import com.dremio.iceberg.authmgr.oauth2.test.TestServer;
import com.dremio.iceberg.authmgr.tools.immutables.AuthManagerImmutable;
import java.util.UUID;
import org.apache.iceberg.PartitionSpec;
Expand Down Expand Up @@ -49,7 +50,7 @@ public void create() {
.withTableMetadata(metadata)
.addAllConfig(getTestEnvironment().getTableProperties())
.build();
getClientAndServer()
TestServer.getInstance()
.when(
HttpRequest.request()
.withMethod("GET")
Expand Down
Loading