Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 5 additions & 1 deletion docs/src/main/sphinx/security/opa-access-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ The `context` object contains all other contextual information about the query:
following two fields:
- `user`: username
- `groups`: list of groups this user belongs to
- `queryId`: query_id
- `softwareStack`: Information about the software stack issuing the request to
OPA. The following information is included:
- `trinoVersion`: Version of Trino used
Expand Down Expand Up @@ -157,7 +158,8 @@ Accessing a table results in a query similar to the following example:
"identity": {
"user": "foo",
"groups": ["some-group"]
},
},
"queryId": "20250718_081710_03427_trino",
"softwareStack": {
"trinoVersion": "434"
}
Expand Down Expand Up @@ -190,6 +192,7 @@ The `targetResource` is used in cases where a new resource, distinct from the on
"user": "foo",
"groups": ["some-group"]
},
"queryId": "20250718_081710_03427_trino",
"softwareStack": {
"trinoVersion": "434"
}
Expand Down Expand Up @@ -373,6 +376,7 @@ A batch column masking request is similar to the following example:
"user": "foo",
"groups": ["some-group"]
},
"queryId": "20250718_081710_03427_trino",
"softwareStack": {
"trinoVersion": "434"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -802,11 +802,11 @@ private static Map<String, Optional<Object>> convertProperties(Map<String, Objec

OpaQueryContext buildQueryContext(Identity trinoIdentity)
{
return new OpaQueryContext(TrinoIdentity.fromTrinoIdentity(trinoIdentity), pluginContext);
return new OpaQueryContext(TrinoIdentity.fromTrinoIdentity(trinoIdentity), pluginContext, null);
}

OpaQueryContext buildQueryContext(SystemSecurityContext securityContext)
{
return new OpaQueryContext(TrinoIdentity.fromTrinoIdentity(securityContext.getIdentity()), pluginContext);
return new OpaQueryContext(TrinoIdentity.fromTrinoIdentity(securityContext.getIdentity()), pluginContext, securityContext.getQueryId().toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import static java.util.Objects.requireNonNull;

public record OpaQueryContext(TrinoIdentity identity, OpaPluginContext softwareStack)
public record OpaQueryContext(TrinoIdentity identity, OpaPluginContext softwareStack, String queryId)
Comment thread
ebyhr marked this conversation as resolved.
{
public OpaQueryContext
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.trino.plugin.opa.HttpClientUtils.InstrumentedHttpClient;
import io.trino.plugin.opa.HttpClientUtils.MockResponse;
import io.trino.plugin.opa.schema.OpaViewExpression;
import io.trino.spi.QueryId;
import io.trino.spi.connector.CatalogSchemaName;
import io.trino.spi.connector.CatalogSchemaRoutineName;
import io.trino.spi.connector.CatalogSchemaTableName;
Expand All @@ -37,6 +38,7 @@
import io.trino.spi.type.VarcharType;
import org.junit.jupiter.api.Test;

import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -1016,4 +1018,58 @@ private static void assertAccessControlMethodBehaviour(MethodWrapper method, Set
assertStringRequestsEqual(expectedRequests, permissiveMockClient.getRequests(), "/input/action");
assertAccessControlMethodThrowsForIllegalResponses(method::isAccessAllowed, simpleOpaConfig(), OPA_SERVER_URI);
}

@Test
public void testQueryIdInSystemSecurityContextRequests()
{

String customQueryId = "20250718_081710_03427_trino";

SystemSecurityContext customSecurityContext = new SystemSecurityContext(
TEST_IDENTITY,
new QueryId("20250718_081710_03427_trino"),
Instant.now()
);
Comment thread
harshit2283 marked this conversation as resolved.

FunctionalHelpers.Consumer3<OpaAccessControl, SystemSecurityContext, CatalogSchemaTableName> callable =
OpaAccessControl::checkCanShowCreateTable;
CatalogSchemaTableName tableName = new CatalogSchemaTableName("my_catalog", "my_schema", "my_table");

ThrowingMethodWrapper wrappedMethod = new ThrowingMethodWrapper(
Comment thread
harshit2283 marked this conversation as resolved.
accessControl -> callable.accept(accessControl, customSecurityContext, tableName));

String expectedActionRequest =
"""
{
"operation": "ShowCreateTable",
"resource": {
"table": {
"catalogName": "%s",
"schemaName": "%s",
"tableName": "%s"
}
}
}
""".formatted(
tableName.getCatalogName(),
tableName.getSchemaTableName().getSchemaName(),
tableName.getSchemaTableName().getTableName());

InstrumentedHttpClient mockClient = createMockHttpClient(
OPA_SERVER_URI,
(request) -> {
JsonNode contextNode = request.path("input").path("context");

assertThat(contextNode.path("queryId").asText()).isEqualTo(customQueryId);
assertThat(contextNode.path("identity").path("user").asText()).isEqualTo(TEST_IDENTITY.getUser());
assertThat(contextNode.path("softwareStack").path("trinoVersion").asText()).isEqualTo("trino-version");

return OK_RESPONSE;
});

OpaAccessControl authorizer = createOpaAuthorizer(simpleOpaConfig(), mockClient);

assertThat(wrappedMethod.isAccessAllowed(authorizer)).isTrue();
assertStringRequestsEqual(ImmutableSet.of(expectedActionRequest), mockClient.getRequests(), "/input/action");
}
}