diff --git a/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/credentials/Credentials.java b/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/credentials/Credentials.java index ff4499ac..da5e5c95 100644 --- a/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/credentials/Credentials.java +++ b/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/credentials/Credentials.java @@ -53,4 +53,9 @@ public static Credentials build(Credential emulated, Credential remote, RemoteSe { return new Credentials(emulated, Optional.of(remote), Optional.of(remoteSessionRole), Optional.empty()); } + + public static Credentials build(Credential emulated, Credential remote, Identity identity) + { + return new Credentials(emulated, Optional.of(remote), Optional.empty(), Optional.of(identity)); + } } diff --git a/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/security/S3SecurityFacadeProvider.java b/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/security/S3SecurityFacadeProvider.java index ae90b2fe..2b2eaf12 100644 --- a/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/security/S3SecurityFacadeProvider.java +++ b/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/security/S3SecurityFacadeProvider.java @@ -13,17 +13,20 @@ */ package io.trino.aws.proxy.spi.security; +import io.trino.aws.proxy.spi.credentials.Identity; import io.trino.aws.proxy.spi.rest.ParsedS3Request; import jakarta.ws.rs.WebApplicationException; +import java.util.Optional; + public interface S3SecurityFacadeProvider { - S3SecurityFacadeProvider NOOP = _ -> S3SecurityFacade.NOOP; + S3SecurityFacadeProvider NOOP = (_, _) -> S3SecurityFacade.NOOP; /** * Return a validated/authenticated facade for the given request or * throw {@link jakarta.ws.rs.WebApplicationException} */ - S3SecurityFacade securityFacadeForRequest(ParsedS3Request request) + S3SecurityFacade securityFacadeForRequest(ParsedS3Request request, Optional identity) throws WebApplicationException; } diff --git a/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/security/opa/OpaS3SecurityMapper.java b/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/security/opa/OpaS3SecurityMapper.java index 662741ff..e913f6cd 100644 --- a/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/security/opa/OpaS3SecurityMapper.java +++ b/trino-aws-proxy-spi/src/main/java/io/trino/aws/proxy/spi/security/opa/OpaS3SecurityMapper.java @@ -14,6 +14,7 @@ package io.trino.aws.proxy.spi.security.opa; import com.google.common.collect.ImmutableMap; +import io.trino.aws.proxy.spi.credentials.Identity; import io.trino.aws.proxy.spi.rest.ParsedS3Request; import io.trino.aws.proxy.spi.security.SecurityResponse; @@ -58,7 +59,7 @@ default SecurityResponse toSecurityResponse(Map responseDocument .orElse(FAILURE); } - OpaRequest toRequest(ParsedS3Request request, Optional lowercaseAction, URI baseUri); + OpaRequest toRequest(ParsedS3Request request, Optional lowercaseAction, URI baseUri, Optional identity); static Optional extractBoolean(Map map, String key) { diff --git a/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/rest/S3PresignController.java b/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/rest/S3PresignController.java index 39c9bfc8..6c4cf46f 100644 --- a/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/rest/S3PresignController.java +++ b/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/rest/S3PresignController.java @@ -84,7 +84,7 @@ private Stream> buildPresignedRemoteUrl(String httpMethod request.rawQuery(), request.requestContent()); - return switch (s3SecurityController.apply(checkRequest)) { + return switch (s3SecurityController.apply(checkRequest, signingMetadata.credentials().identity())) { case Success _ -> Stream.of(Map.entry(httpMethod, signingContext.signingUri())); case Failure _ -> Stream.empty(); }; diff --git a/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/rest/TrinoS3ProxyClient.java b/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/rest/TrinoS3ProxyClient.java index d52982f4..5250da8c 100644 --- a/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/rest/TrinoS3ProxyClient.java +++ b/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/rest/TrinoS3ProxyClient.java @@ -105,7 +105,7 @@ public void proxyRequest(SigningMetadata signingMetadata, ParsedS3Request reques { URI remoteUri = remoteS3Facade.buildEndpoint(uriBuilder(request.queryParameters()), request.rawPath(), request.bucketName(), request.requestAuthorization().region()); - SecurityResponse securityResponse = s3SecurityController.apply(request); + SecurityResponse securityResponse = s3SecurityController.apply(request, signingMetadata.credentials().identity()); if (securityResponse instanceof Failure(var error)) { log.debug("SecurityController check failed. AccessKey: %s, Request: %s, SecurityResponse: %s", signingMetadata.credentials().emulated().accessKey(), request, securityResponse); requestLoggingSession.logError("request.security.fail.credentials", signingMetadata.credentials().emulated()); diff --git a/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/security/S3SecurityController.java b/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/security/S3SecurityController.java index 07b73113..73ed1d87 100644 --- a/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/security/S3SecurityController.java +++ b/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/security/S3SecurityController.java @@ -16,6 +16,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Splitter; import com.google.inject.Inject; +import io.trino.aws.proxy.spi.credentials.Identity; import io.trino.aws.proxy.spi.rest.ParsedS3Request; import io.trino.aws.proxy.spi.security.S3SecurityFacade; import io.trino.aws.proxy.spi.security.S3SecurityFacadeProvider; @@ -29,7 +30,7 @@ public class S3SecurityController { - private static final S3SecurityFacadeProvider DEFAULT_SECURITY_FACADE_PROVIDER = _ -> _ -> SUCCESS; + private static final S3SecurityFacadeProvider DEFAULT_SECURITY_FACADE_PROVIDER = (_, _) -> _ -> SUCCESS; private final S3SecurityFacadeProvider s3SecurityFacadeProvider; @@ -39,9 +40,9 @@ public S3SecurityController(S3SecurityFacadeProvider s3SecurityFacadeProvider) this.s3SecurityFacadeProvider = requireNonNull(s3SecurityFacadeProvider, "s3SecurityFacadeProvider is null"); } - public SecurityResponse apply(ParsedS3Request request) + public SecurityResponse apply(ParsedS3Request request, Optional identity) { - S3SecurityFacade s3SecurityFacade = currentProvider().securityFacadeForRequest(request); + S3SecurityFacade s3SecurityFacade = currentProvider().securityFacadeForRequest(request, identity); Optional lowercaseAction = request.rawQuery().flatMap(S3SecurityController::parseAction); diff --git a/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/security/opa/OpaS3SecurityFacadeProvider.java b/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/security/opa/OpaS3SecurityFacadeProvider.java index bfa879cf..60b41142 100644 --- a/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/security/opa/OpaS3SecurityFacadeProvider.java +++ b/trino-aws-proxy/src/main/java/io/trino/aws/proxy/server/security/opa/OpaS3SecurityFacadeProvider.java @@ -17,6 +17,7 @@ import io.airlift.http.client.HttpClient; import io.airlift.http.client.Request; import io.airlift.json.JsonCodec; +import io.trino.aws.proxy.spi.credentials.Identity; import io.trino.aws.proxy.spi.rest.ParsedS3Request; import io.trino.aws.proxy.spi.security.S3SecurityFacade; import io.trino.aws.proxy.spi.security.S3SecurityFacadeProvider; @@ -56,15 +57,15 @@ public OpaS3SecurityFacadeProvider(@ForOpa HttpClient httpClient, OpaS3SecurityM } @Override - public S3SecurityFacade securityFacadeForRequest(ParsedS3Request request) + public S3SecurityFacade securityFacadeForRequest(ParsedS3Request request, Optional identity) throws WebApplicationException { - return lowercaseAction -> facade(request, lowercaseAction); + return lowercaseAction -> facade(request, lowercaseAction, identity); } - private SecurityResponse facade(ParsedS3Request parsedS3Request, Optional lowercaseAction) + private SecurityResponse facade(ParsedS3Request parsedS3Request, Optional lowercaseAction, Optional identity) { - OpaRequest opaRequest = opaS3SecurityMapper.toRequest(parsedS3Request, lowercaseAction, opaServerUri); + OpaRequest opaRequest = opaS3SecurityMapper.toRequest(parsedS3Request, lowercaseAction, opaServerUri, identity); Map inputDocument = opaS3SecurityMapper.toInputDocument(opaRequest.document()); diff --git a/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestDatabaseSecurity.java b/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestDatabaseSecurity.java index 1a73721e..3e34463a 100644 --- a/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestDatabaseSecurity.java +++ b/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestDatabaseSecurity.java @@ -19,6 +19,7 @@ import io.trino.aws.proxy.server.testing.containers.PySparkContainer; import io.trino.aws.proxy.server.testing.harness.BuilderFilter; import io.trino.aws.proxy.server.testing.harness.TrinoAwsProxyTest; +import io.trino.aws.proxy.spi.credentials.Identity; import io.trino.aws.proxy.spi.rest.ParsedS3Request; import io.trino.aws.proxy.spi.security.S3DatabaseSecurityDecorator; import io.trino.aws.proxy.spi.security.S3SecurityFacade; @@ -63,7 +64,7 @@ public static class FacadeProvider final AtomicBoolean disallowGets = new AtomicBoolean(); @Override - public S3SecurityFacade securityFacadeForRequest(ParsedS3Request request) + public S3SecurityFacade securityFacadeForRequest(ParsedS3Request request, Optional identity) throws WebApplicationException { S3SecurityFacade s3SecurityFacade = lowercaseAction -> SUCCESS; diff --git a/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestOpaSecurity.java b/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestOpaSecurity.java index fe168a8d..e091ad55 100644 --- a/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestOpaSecurity.java +++ b/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestOpaSecurity.java @@ -24,6 +24,7 @@ import io.trino.aws.proxy.server.testing.containers.OpaContainer; import io.trino.aws.proxy.server.testing.harness.BuilderFilter; import io.trino.aws.proxy.server.testing.harness.TrinoAwsProxyTest; +import io.trino.aws.proxy.spi.credentials.Identity; import io.trino.aws.proxy.spi.rest.ParsedS3Request; import io.trino.aws.proxy.spi.security.opa.OpaRequest; import io.trino.aws.proxy.spi.security.opa.OpaS3SecurityMapper; @@ -80,8 +81,9 @@ public OpaMapper(OpaContainer container) } @Override - public OpaRequest toRequest(ParsedS3Request request, Optional lowercaseAction, URI baseUri) + public OpaRequest toRequest(ParsedS3Request request, Optional lowercaseAction, URI baseUri, Optional identity) { + assertThat(identity).isPresent(); URI uri = UriBuilder.fromUri(baseUri).port(containerPort).path("test").path("allow").build(); return new OpaRequest(uri, ImmutableMap.of("table", request.keyInBucket())); } diff --git a/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestS3SecurityController.java b/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestS3SecurityController.java index abffa745..162158ed 100644 --- a/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestS3SecurityController.java +++ b/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/TestS3SecurityController.java @@ -63,7 +63,7 @@ public void testDisableListObjects() assertThat(listObjectsResponse.contents()).isEmpty(); // set facade that disallows list objects on bucket "one" - securityController.setDelegate(request -> _ -> { + securityController.setDelegate((request, _) -> _ -> { if ("one".equals(request.bucketName()) && request.httpVerb().equalsIgnoreCase("get")) { return FAILURE; } diff --git a/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/rest/TestPresigningHeaders.java b/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/rest/TestPresigningHeaders.java index 29822dd3..7c61dfcc 100644 --- a/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/rest/TestPresigningHeaders.java +++ b/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/rest/TestPresigningHeaders.java @@ -108,7 +108,7 @@ public void testPresignHeaderSecurity() Presigned presigned = getPresigned("get", "one", "gettest"); assertThat(presigned.presignedHeaderMethods).containsExactlyInAnyOrder("GET", "PUT", "POST", "DELETE"); - securityController.setDelegate(request -> lowercaseAction -> request.httpVerb().equalsIgnoreCase("DELETE") ? FAILURE : SUCCESS); + securityController.setDelegate((request, _) -> lowercaseAction -> request.httpVerb().equalsIgnoreCase("DELETE") ? FAILURE : SUCCESS); presigned = getPresigned("get", "one", "gettest"); assertThat(presigned.presignedHeaderMethods).containsExactlyInAnyOrder("GET", "PUT", "POST"); diff --git a/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/testing/TestingUtil.java b/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/testing/TestingUtil.java index 0d090cb2..f608028d 100644 --- a/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/testing/TestingUtil.java +++ b/trino-aws-proxy/src/test/java/io/trino/aws/proxy/server/testing/TestingUtil.java @@ -56,7 +56,8 @@ private TestingUtil() {} public static final Credentials TESTING_CREDENTIALS = Credentials.build( new Credential(UUID.randomUUID().toString(), UUID.randomUUID().toString()), - new Credential(UUID.randomUUID().toString(), UUID.randomUUID().toString())); + new Credential(UUID.randomUUID().toString(), UUID.randomUUID().toString()), + new TestingIdentity(UUID.randomUUID().toString(), List.of(), UUID.randomUUID().toString())); // Domain name with a wildcard CNAME pointing to localhost - needed to test Virtual Host style addressing public static final String LOCALHOST_DOMAIN = "local.gate0.net";