Managing Policy
Policy is the set of rules that govern who can access data and under what conditions. It is made up of namespaces, attributes, subject mappings, and subject condition sets. See Policy for the concept overview. The SDK provides CRUD access to these policy rules through remote gRPC calls powered by the platform service client.
Setup
All examples on this page assume you have created a platform client. See Authentication for full details including DPoP key binding.
- Go
- Java
- JavaScript
package main
import (
"context"
"log"
"github.com/opentdf/platform/sdk"
)
func main() {
client, err := sdk.New("http://localhost:8080",
sdk.WithClientCredentials("opentdf", "secret", nil),
)
if err != nil {
log.Fatal(err)
}
// All Go snippets below use `client` and `context.Background()`.
}
import io.opentdf.platform.sdk.*;
import java.util.Collections;
SDK sdk = SDKBuilder.newBuilder()
.platformEndpoint("http://localhost:8080")
.clientSecret("opentdf", "secret")
.useInsecurePlaintextConnection(true)
.build();
// All Java snippets below use `sdk`.
// Remember to call sdk.close() when done.
import { authTokenInterceptor, clientCredentialsTokenProvider } from '@opentdf/sdk';
import { PlatformClient } from '@opentdf/sdk/platform';
const platform = new PlatformClient({
interceptors: [authTokenInterceptor(clientCredentialsTokenProvider({
clientId: 'opentdf', clientSecret: 'secret',
oidcOrigin: 'http://localhost:8080/auth/realms/opentdf',
}))],
platformUrl: 'http://localhost:8080',
});
// All JavaScript snippets below use `platform`.
The JavaScript SDK is designed for browser applications. The examples on this page use clientCredentialsTokenProvider because it's self-contained and easy to follow, but it requires a client secret and must not be used in browser code.
In production browser apps, complete an OIDC login flow to obtain a refresh token, then use refreshTokenProvider(). See the Authentication Decision Guide for help choosing the right method.
Metadata
All policy objects support optional metadata. When creating or updating a resource, you can pass a metadata object containing labels — a flat map of string key-value pairs:
{
"labels": {
"owner": "platform-team",
"env": "production"
}
}
labels is the only supported field. Other fields are silently ignored. When updating, use metadataUpdateBehavior to control whether labels are replaced or merged (see Update a Namespace for an example).
Pagination
All List operations return a pagination object alongside the results:
| Field | Type | Description |
|---|---|---|
currentOffset | int | The offset that was requested. |
nextOffset | int | The offset to use for the next page. Empty when no more results remain. |
total | int | Total count of results across all pages. |
Example response shape (Go):
resp, err := client.Namespaces.ListNamespaces(ctx, &namespaces.ListNamespacesRequest{
Pagination: &policy.PageRequest{Limit: 10, Offset: 0},
})
// resp.GetPagination().GetCurrentOffset() == 0
// resp.GetPagination().GetNextOffset() == 10
// resp.GetPagination().GetTotal() == 42
Namespaces
A namespace is the top-level organizational unit for attributes and optionally for registered resources. FQNs are scoped to a namespace URI (e.g., https://example.com).
Namespaces are deactivated, not permanently deleted. There is no hard-delete operation and no restore operation exposed via the SDK.
Create a Namespace
Signature
- Go
- Java
- JavaScript
client.Namespaces.CreateNamespace(ctx, &namespaces.CreateNamespaceRequest{...})
sdk.getServices().namespaces().createNamespaceBlocking(req, metadata).execute()
await platform.v1.namespace.createNamespace({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | A valid hostname (e.g., opentdf.io, example.com). |
metadata | Metadata | No | Optional labels, e.g., {"labels": {"owner": "platform-team"}}. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/namespaces"
resp, err := client.Namespaces.CreateNamespace(context.Background(),
&namespaces.CreateNamespaceRequest{
Name: "opentdf.io",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Created namespace: %s\n", resp.GetNamespace().GetId())
With metadata:
import (
"github.com/opentdf/platform/protocol/go/common"
"github.com/opentdf/platform/protocol/go/policy/namespaces"
)
resp, err := client.Namespaces.CreateNamespace(context.Background(),
&namespaces.CreateNamespaceRequest{
Name: "opentdf.io",
Metadata: &common.MetadataMutable{
Labels: map[string]string{
"owner": "platform-team",
"env": "production",
},
},
},
)
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.namespaces.CreateNamespaceRequest;
import io.opentdf.platform.policy.namespaces.CreateNamespaceResponse;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.Objects;
public class CreateNamespace {
private static final Logger logger = LogManager.getLogger(CreateNamespace.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
String namespaceName = "opentdf.io";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
CreateNamespaceRequest createNamespaceRequest =
CreateNamespaceRequest.newBuilder().setName(namespaceName).build();
CreateNamespaceResponse createNamespaceResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.createNamespaceBlocking(createNamespaceRequest, Collections.emptyMap())
.execute());
logger.info(
"Successfully created namespace with ID: {}",
createNamespaceResponse.getNamespace().getId());
} catch (Exception e) {
if (Objects.equals(e.getMessage(), "resource unique field violation")) {
logger.error("Namespace '{}' already exists", namespaceName, e);
} else {
logger.error("Failed to create namespace", e);
}
}
}
}
With metadata:
import io.opentdf.platform.policy.namespaces.CreateNamespaceRequest;
import io.opentdf.platform.common.Common.MetadataMutable;
var req = CreateNamespaceRequest.newBuilder()
.setName("opentdf.io")
.setMetadata(MetadataMutable.newBuilder()
.putLabels("owner", "platform-team")
.putLabels("env", "production")
)
.build();
var resp = sdk.getServices().namespaces()
.createNamespaceBlocking(req, Collections.emptyMap()).execute();
System.out.println("Created namespace: " + resp.getNamespace().getId());
const resp = await platform.v1.namespace.createNamespace({
name: 'opentdf.io',
});
console.log('Created namespace:', resp.namespace?.id);
With metadata:
const resp = await platform.v1.namespace.createNamespace({
name: 'opentdf.io',
metadata: {
labels: { owner: 'platform-team', env: 'production' },
},
});
Returns
The created Namespace object.
List Namespaces
Signature
- Go
- Java
- JavaScript
client.Namespaces.ListNamespaces(ctx, &namespaces.ListNamespacesRequest{...})
sdk.getServices().namespaces().listNamespacesBlocking(req, metadata).execute()
await platform.v1.namespace.listNamespaces({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
state | string | No | Filter by state: ACTIVE_STATE_ENUM_ACTIVE (default), ACTIVE_STATE_ENUM_INACTIVE, or ACTIVE_STATE_ENUM_ANY. |
pagination.limit | int | No | Maximum results per page. Defaults to the platform-configured limit. |
pagination.offset | int | No | Number of results to skip. |
Example
- Go
- Java
- JavaScript
import (
"github.com/opentdf/platform/protocol/go/common"
"github.com/opentdf/platform/protocol/go/policy/namespaces"
)
// List all active namespaces
resp, err := client.Namespaces.ListNamespaces(context.Background(), &namespaces.ListNamespacesRequest{})
if err != nil {
log.Fatal(err)
}
for _, namespace := range resp.GetNamespaces() {
log.Printf("Namespace: %s\n", namespace.GetName())
}
To list inactive namespaces or paginate:
import (
"github.com/opentdf/platform/protocol/go/common"
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/namespaces"
)
resp, err := client.Namespaces.ListNamespaces(context.Background(), &namespaces.ListNamespacesRequest{
State: common.ActiveStateEnum_ACTIVE_STATE_ENUM_INACTIVE,
Pagination: &policy.PageRequest{
Limit: 10,
Offset: 0,
},
})
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.Namespace;
import io.opentdf.platform.policy.namespaces.ListNamespacesRequest;
import io.opentdf.platform.policy.namespaces.ListNamespacesResponse;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class ListNamespaces {
private static final Logger logger = LogManager.getLogger(ListNamespaces.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
ListNamespacesRequest request = ListNamespacesRequest.newBuilder().build();
ListNamespacesResponse listNamespacesResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.listNamespacesBlocking(request, Collections.emptyMap())
.execute());
List<Namespace> namespaces = listNamespacesResponse.getNamespacesList();
logger.info(
"Successfully retrieved namespaces: [{}]",
namespaces.stream().map(Namespace::getFqn).collect(Collectors.joining(", ")));
} catch (Exception e) {
logger.error("Failed to list namespaces", e);
}
}
}
To list inactive namespaces:
import io.opentdf.platform.common.Common.ActiveStateEnum;
var req = ListNamespacesRequest.newBuilder()
.setState(ActiveStateEnum.ACTIVE_STATE_ENUM_INACTIVE)
.build();
var resp = sdk.getServices().namespaces()
.listNamespacesBlocking(req, Collections.emptyMap()).execute();
// List all active namespaces
const resp = await platform.v1.namespace.listNamespaces({});
for (const ns of resp.namespaces) {
console.log('Namespace:', ns.name);
}
To list inactive namespaces or paginate:
const resp = await platform.v1.namespace.listNamespaces({
state: 'ACTIVE_STATE_ENUM_INACTIVE',
pagination: { limit: 10, offset: 0 },
});
Returns
A list of Namespace objects. Includes pagination metadata.
Get a Namespace
Signature
- Go
- Java
- JavaScript
client.Namespaces.GetNamespace(ctx, &namespaces.GetNamespaceRequest{...})
sdk.getServices().namespaces().getNamespaceBlocking(req, metadata).execute()
await platform.v1.namespace.getNamespace({ ... })
Parameters
Provide one of the following (exactly one is required):
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | The namespace UUID. |
fqn | string (URI) | The namespace FQN (e.g., https://example.com). |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/namespaces"
// Look up by UUID
resp, err := client.Namespaces.GetNamespace(context.Background(),
&namespaces.GetNamespaceRequest{
Identifier: &namespaces.GetNamespaceRequest_NamespaceId{
NamespaceId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Namespace: %s (ID: %s)\n", resp.GetNamespace().GetName(), resp.GetNamespace().GetId())
// Alternatively, look up by FQN
respByFqn, err := client.Namespaces.GetNamespace(context.Background(),
&namespaces.GetNamespaceRequest{
Identifier: &namespaces.GetNamespaceRequest_Fqn{
Fqn: "https://example.com",
},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Namespace by FQN: %s\n", respByFqn.GetNamespace().GetName())
import io.opentdf.platform.policy.namespaces.GetNamespaceRequest;
// Look up by UUID
var req = GetNamespaceRequest.newBuilder()
.setNamespaceId("f47ac10b-58cc-4372-a567-0e02b2c3d479")
.build();
var resp = sdk.getServices().namespaces()
.getNamespaceBlocking(req, Collections.emptyMap()).execute();
System.out.println("Namespace: " + resp.getNamespace().getName());
// Alternatively, look up by FQN
var reqByFqn = GetNamespaceRequest.newBuilder()
.setFqn("https://example.com")
.build();
var respByFqn = sdk.getServices().namespaces()
.getNamespaceBlocking(reqByFqn, Collections.emptyMap()).execute();
System.out.println("Namespace by FQN: " + respByFqn.getNamespace().getName());
// Look up by UUID
let resp = await platform.v1.namespace.getNamespace({ id: 'f47ac10b-58cc-4372-a567-0e02b2c3d479' });
console.log(`Namespace: ${resp.namespace?.name} (ID: ${resp.namespace?.id})`);
// Alternatively, look up by FQN
resp = await platform.v1.namespace.getNamespace({ fqn: 'https://example.com' });
console.log(`Namespace by FQN: ${resp.namespace?.name}`);
Returns
A single Namespace object. Namespace names are unique — an FQN always resolves to exactly one namespace or an error.
Update a Namespace
Signature
- Go
- Java
- JavaScript
client.Namespaces.UpdateNamespace(ctx, &namespaces.UpdateNamespaceRequest{...})
sdk.getServices().namespaces().updateNamespaceBlocking(req, metadata).execute()
await platform.v1.namespace.updateNamespace({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The namespace ID to update. |
metadata | Metadata | No | Labels to attach, e.g., {"labels": {"owner": "platform-team"}}. |
metadataUpdateBehavior | string | No | METADATA_UPDATE_ENUM_REPLACE overwrites all existing labels. METADATA_UPDATE_ENUM_EXTEND merges new labels into existing ones. If omitted, defaults to EXTEND. |
Example
- Go
- Java
- JavaScript
import (
"github.com/opentdf/platform/protocol/go/common"
"github.com/opentdf/platform/protocol/go/policy/namespaces"
)
resp, err := client.Namespaces.UpdateNamespace(context.Background(),
&namespaces.UpdateNamespaceRequest{
Id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
Metadata: &common.MetadataMutable{
Labels: map[string]string{
"owner": "platform-team",
"env": "production",
},
},
MetadataUpdateBehavior: common.MetadataUpdateEnum_METADATA_UPDATE_ENUM_REPLACE,
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated namespace ID: %s\n", resp.GetNamespace().GetId())
import io.opentdf.platform.policy.namespaces.UpdateNamespaceRequest;
import io.opentdf.platform.common.Common.MetadataMutable;
import io.opentdf.platform.common.Common.MetadataUpdateEnum;
var metadata = MetadataMutable.newBuilder()
.putLabels("owner", "platform-team")
.putLabels("env", "production")
.build();
var req = UpdateNamespaceRequest.newBuilder()
.setId("f47ac10b-58cc-4372-a567-0e02b2c3d479")
.setMetadata(metadata)
.setMetadataUpdateBehavior(MetadataUpdateEnum.METADATA_UPDATE_ENUM_REPLACE)
.build();
var resp = sdk.getServices().namespaces()
.updateNamespaceBlocking(req, Collections.emptyMap()).execute();
System.out.println("Updated namespace ID: " + resp.getNamespace().getId());
const resp = await platform.v1.namespace.updateNamespace({
id: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
metadata: { labels: { owner: 'platform-team', env: 'production' } },
metadataUpdateBehavior: 'METADATA_UPDATE_ENUM_REPLACE',
});
console.log(`Updated namespace ID: ${resp.namespace?.id}`);
Returns
The updated Namespace object.
Deactivate a Namespace
Signature
- Go
- Java
- JavaScript
client.Namespaces.DeactivateNamespace(ctx, &namespaces.DeactivateNamespaceRequest{...})
sdk.getServices().namespaces().deactivateNamespaceBlocking(req, metadata).execute()
await platform.v1.namespace.deactivateNamespace({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The namespace ID to deactivate. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/namespaces"
_, err = client.Namespaces.DeactivateNamespace(context.Background(),
&namespaces.DeactivateNamespaceRequest{
Id: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
},
)
if err != nil {
log.Fatal(err)
}
log.Println("Namespace deactivated.")
import io.opentdf.platform.policy.namespaces.DeactivateNamespaceRequest;
var req = DeactivateNamespaceRequest.newBuilder()
.setId("f47ac10b-58cc-4372-a567-0e02b2c3d479")
.build();
sdk.getServices().namespaces()
.deactivateNamespaceBlocking(req, Collections.emptyMap()).execute();
System.out.println("Namespace deactivated.");
await platform.v1.namespace.deactivateNamespace({ id: 'f47ac10b-58cc-4372-a567-0e02b2c3d479' });
console.log('Namespace deactivated.');
Returns
Empty response. The namespace is soft-deleted and will no longer appear in ListNamespaces unless you filter by INACTIVE or ANY state.
Namespace Object
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier, generated by the platform. |
name | string | The hostname used to scope attributes (e.g., opentdf.io). |
fqn | string | Fully qualified name, e.g., https://opentdf.io. |
active | bool | true until explicitly deactivated. |
metadata | Metadata | Optional labels. |
createdAt | timestamp | When the namespace was created. |
updatedAt | timestamp | When the namespace was last modified. |
Attributes
An attribute defines a classification dimension (e.g., classification, department). Each attribute belongs to a namespace, has a rule (All Of, Any Of, or Hierarchy), and contains one or more values.
Attributes and attribute values are deactivated, not permanently deleted.
Create an Attribute
Signature
- Go
- Java
- JavaScript
client.Attributes.CreateAttribute(ctx, &attributes.CreateAttributeRequest{...})
sdk.getServices().attributes().createAttributeBlocking(req, metadata).execute()
await platform.v1.attributes.createAttribute({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
namespaceId | string (UUID) | Yes | The parent namespace ID. |
name | string | Yes | Attribute name (e.g., classification, department). |
rule | string | Yes | Access rule: ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, ANY_OF, or HIERARCHY. |
values | []string | No | Initial values to create with the attribute. Can also be added later via Create an Attribute Value. |
metadata | Metadata | No | Optional labels. |
Example
- Go
- Java
- JavaScript
import (
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/attributes"
)
resp, err := client.Attributes.CreateAttribute(context.Background(),
&attributes.CreateAttributeRequest{
NamespaceId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
Name: "classification",
Rule: policy.AttributeRuleTypeEnum_ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF,
Values: []string{"public", "confidential", "secret"},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Created attribute: %s (ID: %s)\n",
resp.GetAttribute().GetName(), resp.GetAttribute().GetId())
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.AttributeRuleTypeEnum;
import io.opentdf.platform.policy.Namespace;
import io.opentdf.platform.policy.attributes.CreateAttributeRequest;
import io.opentdf.platform.policy.attributes.CreateAttributeResponse;
import io.opentdf.platform.policy.namespaces.GetNamespaceRequest;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.Arrays;
import java.util.Objects;
public class CreateAttribute {
private static final Logger logger = LogManager.getLogger(CreateAttribute.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
String namespaceName = "opentdf.io";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
Namespace namespace =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.getNamespaceBlocking(
GetNamespaceRequest.newBuilder()
.setFqn("https://" + namespaceName)
.build(),
Collections.emptyMap())
.execute())
.getNamespace();
CreateAttributeRequest createAttributeRequest =
CreateAttributeRequest.newBuilder()
.setNamespaceId(namespace.getId())
.setName("test-attribute")
.setRule(
AttributeRuleTypeEnum.forNumber(
AttributeRuleTypeEnum.ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF_VALUE))
.addAllValues(Arrays.asList("test1", "test2"))
.build();
CreateAttributeResponse createAttributeResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.createAttributeBlocking(createAttributeRequest, Collections.emptyMap())
.execute());
logger.info(
"Successfully created attribute with ID: {}",
createAttributeResponse.getAttribute().getId());
} catch (Exception e) {
if (Objects.equals(e.getMessage(), "resource not found")) {
logger.error("Namespace '{}' not found", namespaceName, e);
} else if (Objects.equals(e.getMessage(), "resource unique field violation")) {
logger.error("Attribute already exists", e);
} else {
logger.error("Failed to create attribute", e);
}
}
}
}
import { AttributeRuleTypeEnum } from '@opentdf/sdk/platform/policy/objects_pb.js';
const resp = await platform.v1.attributes.createAttribute({
namespaceId: 'f47ac10b-58cc-4372-a567-0e02b2c3d479',
name: 'classification',
rule: AttributeRuleTypeEnum.ANY_OF,
values: ['public', 'confidential', 'secret'],
});
console.log('Created attribute:', resp.attribute?.name, 'ID:', resp.attribute?.id);
Returns
The created Attribute object.
List Attributes
Signature
- Go
- Java
- JavaScript
client.Attributes.ListAttributes(ctx, &attributes.ListAttributesRequest{...})
sdk.getServices().attributes().listAttributesBlocking(req, metadata).execute()
await platform.v1.attributes.listAttributes({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
state | string | No | Filter by state: ACTIVE_STATE_ENUM_ACTIVE (default), ACTIVE_STATE_ENUM_INACTIVE, or ACTIVE_STATE_ENUM_ANY. |
namespaceId | string (UUID) | No | Filter to attributes in a specific namespace. |
pagination.limit | int | No | Maximum results per page. |
pagination.offset | int | No | Number of results to skip. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/attributes"
resp, err := client.Attributes.ListAttributes(context.Background(),
&attributes.ListAttributesRequest{},
)
if err != nil {
log.Fatal(err)
}
for _, attr := range resp.GetAttributes() {
log.Printf("Attribute: %s (ID: %s)\n", attr.GetName(), attr.GetId())
for _, value := range attr.GetValues() {
log.Printf(" Value: %s (ID: %s)\n", value.GetValue(), value.GetId())
}
}
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.Attribute;
import io.opentdf.platform.policy.attributes.ListAttributesRequest;
import io.opentdf.platform.policy.attributes.ListAttributesResponse;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class ListAttributes {
private static final Logger logger = LogManager.getLogger(ListAttributes.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
String namespaceName = "opentdf.io";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
ListAttributesRequest request =
ListAttributesRequest.newBuilder().setNamespace(namespaceName).build();
ListAttributesResponse listAttributesResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.listAttributesBlocking(request, Collections.emptyMap())
.execute());
List<Attribute> attributes = listAttributesResponse.getAttributesList();
logger.info(
"Successfully retrieved attributes: [{}]",
attributes.stream().map(Attribute::getFqn).collect(Collectors.joining(", ")));
} catch (Exception e) {
logger.error("Failed to list attributes", e);
}
}
}
const resp = await platform.v1.attributes.listAttributes({});
for (const attr of resp.attributes) {
console.log('Attribute:', attr.name, 'ID:', attr.id);
for (const value of attr.values) {
console.log(' Value:', value.value, 'ID:', value.id);
}
}
Returns
A list of Attribute objects, each with nested values. Includes pagination metadata.
Get an Attribute
Signature
- Go
- Java
- JavaScript
client.Attributes.GetAttribute(ctx, &attributes.GetAttributeRequest{...})
sdk.getServices().attributes().getAttributeBlocking(req, metadata).execute()
await platform.v1.attributes.getAttribute({ ... })
Parameters
Provide one of the following (exactly one is required):
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | The attribute UUID. |
fqn | string | The attribute FQN (e.g., https://example.com/attr/classification). |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/attributes"
resp, err := client.Attributes.GetAttribute(context.Background(),
&attributes.GetAttributeRequest{
Identifier: &attributes.GetAttributeRequest_Fqn{
Fqn: "https://example.com/attr/classification",
},
},
)
if err != nil {
log.Fatal(err)
}
attr := resp.GetAttribute()
log.Printf("Attribute: %s, Rule: %s\n", attr.GetName(), attr.GetRule())
for _, v := range attr.GetValues() {
log.Printf(" Value: %s (ID: %s)\n", v.GetValue(), v.GetId())
}
import io.opentdf.platform.policy.attributes.GetAttributeRequest;
var req = GetAttributeRequest.newBuilder()
.setFqn("https://example.com/attr/classification")
.build();
var resp = sdk.getServices().attributes()
.getAttributeBlocking(req, Collections.emptyMap()).execute();
var attr = resp.getAttribute();
System.out.println("Attribute: " + attr.getName() + " (" + attr.getFqn() + ")");
for (var value : attr.getValuesList()) {
System.out.println(" Value: " + value.getValue() + " (ID: " + value.getId() + ")");
}
const resp = await platform.v1.attributes.getAttribute({
fqn: 'https://example.com/attr/classification',
});
console.log(`Attribute: ${resp.attribute?.name}, Rule: ${resp.attribute?.rule}`);
for (const v of resp.attribute?.values ?? []) {
console.log(` Value: ${v.value} (ID: ${v.id})`);
}
Returns
A single Attribute object with nested values.
Update an Attribute
Signature
- Go
- Java
- JavaScript
client.Attributes.UpdateAttribute(ctx, &attributes.UpdateAttributeRequest{...})
sdk.getServices().attributes().updateAttributeBlocking(req, metadata).execute()
await platform.v1.attributes.updateAttribute({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The attribute ID to update. |
metadata | Metadata | No | Optional labels. |
metadataUpdateBehavior | string | No | METADATA_UPDATE_ENUM_REPLACE or METADATA_UPDATE_ENUM_EXTEND (default). |
Example
- Go
- Java
- JavaScript
import (
"github.com/opentdf/platform/protocol/go/common"
"github.com/opentdf/platform/protocol/go/policy/attributes"
)
resp, err := client.Attributes.UpdateAttribute(context.Background(),
&attributes.UpdateAttributeRequest{
Id: "a1b2c3d4-0000-0000-0000-000000000001",
Metadata: &common.MetadataMutable{
Labels: map[string]string{"reviewed": "true"},
},
MetadataUpdateBehavior: common.MetadataUpdateEnum_METADATA_UPDATE_ENUM_EXTEND,
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated attribute ID: %s\n", resp.GetAttribute().GetId())
import io.opentdf.platform.policy.attributes.UpdateAttributeRequest;
import io.opentdf.platform.common.Common.MetadataMutable;
import io.opentdf.platform.common.Common.MetadataUpdateEnum;
var req = UpdateAttributeRequest.newBuilder()
.setId("a1b2c3d4-0000-0000-0000-000000000001")
.setMetadata(MetadataMutable.newBuilder()
.putLabels("reviewed", "true")
.build())
.setMetadataUpdateBehavior(MetadataUpdateEnum.METADATA_UPDATE_ENUM_EXTEND)
.build();
var resp = sdk.getServices().attributes()
.updateAttributeBlocking(req, Collections.emptyMap()).execute();
System.out.println("Updated attribute ID: " + resp.getAttribute().getId());
const resp = await platform.v1.attributes.updateAttribute({
id: 'a1b2c3d4-0000-0000-0000-000000000001',
metadata: { labels: { reviewed: 'true' } },
metadataUpdateBehavior: 'METADATA_UPDATE_ENUM_EXTEND',
});
console.log(`Updated attribute ID: ${resp.attribute?.id}`);
Returns
The updated Attribute object.
Deactivate an Attribute
Signature
- Go
- Java
- JavaScript
client.Attributes.DeactivateAttribute(ctx, &attributes.DeactivateAttributeRequest{...})
sdk.getServices().attributes().deactivateAttributeBlocking(req, metadata).execute()
await platform.v1.attributes.deactivateAttribute({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The attribute ID to deactivate. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/attributes"
resp, err := client.Attributes.DeactivateAttribute(context.Background(),
&attributes.DeactivateAttributeRequest{
Id: "a1b2c3d4-0000-0000-0000-000000000001",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deactivated attribute: %s\n", resp.GetAttribute().GetId())
import io.opentdf.platform.policy.attributes.DeactivateAttributeRequest;
var req = DeactivateAttributeRequest.newBuilder()
.setId("a1b2c3d4-0000-0000-0000-000000000001")
.build();
var resp = sdk.getServices().attributes()
.deactivateAttributeBlocking(req, Collections.emptyMap()).execute();
System.out.println("Deactivated attribute: " + resp.getAttribute().getId());
const resp = await platform.v1.attributes.deactivateAttribute({
id: 'a1b2c3d4-0000-0000-0000-000000000001',
});
console.log(`Deactivated attribute: ${resp.attribute?.id}`);
Returns
Empty response. The attribute is soft-deleted and will no longer appear in ListAttributes unless you filter by INACTIVE or ANY state.
Attribute Values
Attribute values are the individual members of an attribute (e.g., secret, top-secret under classification). They can be managed independently after the parent attribute is created.
Create an Attribute Value
Signature
- Go
- Java
- JavaScript
client.Attributes.CreateAttributeValue(ctx, &attributes.CreateAttributeValueRequest{...})
sdk.getServices().attributes().createAttributeValueBlocking(req, metadata).execute()
await platform.v1.attributes.createAttributeValue({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
attributeId | string (UUID) | Yes | The parent attribute ID. |
value | string | Yes | The value string (e.g., top-secret). |
metadata | Metadata | No | Optional labels. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/attributes"
resp, err := client.Attributes.CreateAttributeValue(context.Background(),
&attributes.CreateAttributeValueRequest{
AttributeId: "a1b2c3d4-0000-0000-0000-000000000001",
Value: "top-secret",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Created value: %s (ID: %s)\n", resp.GetValue().GetValue(), resp.GetValue().GetId())
import io.opentdf.platform.policy.attributes.CreateAttributeValueRequest;
var req = CreateAttributeValueRequest.newBuilder()
.setAttributeId("a1b2c3d4-0000-0000-0000-000000000001")
.setValue("top-secret")
.build();
var resp = sdk.getServices().attributes()
.createAttributeValueBlocking(req, Collections.emptyMap()).execute();
System.out.println("Created value: " + resp.getValue().getValue() + " (ID: " + resp.getValue().getId() + ")");
const resp = await platform.v1.attributes.createAttributeValue({
attributeId: 'a1b2c3d4-0000-0000-0000-000000000001',
value: 'top-secret',
});
console.log(`Created value: ${resp.value?.value} (ID: ${resp.value?.id})`);
Returns
The created Attribute Value object.
List Attribute Values
Signature
- Go
- Java
- JavaScript
client.Attributes.ListAttributeValues(ctx, &attributes.ListAttributeValuesRequest{...})
sdk.getServices().attributes().listAttributeValuesBlocking(req, metadata).execute()
await platform.v1.attributes.listAttributeValues({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
attributeId | string (UUID) | Yes | The parent attribute ID. |
state | string | No | Filter by state: ACTIVE_STATE_ENUM_ACTIVE (default), ACTIVE_STATE_ENUM_INACTIVE, or ACTIVE_STATE_ENUM_ANY. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/attributes"
resp, err := client.Attributes.ListAttributeValues(context.Background(),
&attributes.ListAttributeValuesRequest{
AttributeId: "a1b2c3d4-0000-0000-0000-000000000001",
},
)
if err != nil {
log.Fatal(err)
}
for _, v := range resp.GetValues() {
log.Printf("Value: %s (ID: %s)\n", v.GetValue(), v.GetId())
}
import io.opentdf.platform.policy.attributes.ListAttributeValuesRequest;
var req = ListAttributeValuesRequest.newBuilder()
.setAttributeId("a1b2c3d4-0000-0000-0000-000000000001")
.build();
var resp = sdk.getServices().attributes()
.listAttributeValuesBlocking(req, Collections.emptyMap()).execute();
for (var value : resp.getValuesList()) {
System.out.println("Value: " + value.getValue() + " (ID: " + value.getId() + ")");
}
const resp = await platform.v1.attributes.listAttributeValues({
attributeId: 'a1b2c3d4-0000-0000-0000-000000000001',
});
for (const v of resp.values) {
console.log(`Value: ${v.value} (ID: ${v.id})`);
}
Returns
A list of Attribute Value objects.
Get an Attribute Value
Signature
- Go
- Java
- JavaScript
client.Attributes.GetAttributeValue(ctx, &attributes.GetAttributeValueRequest{...})
sdk.getServices().attributes().getAttributeValueBlocking(req, metadata).execute()
await platform.v1.attributes.getAttributeValue({ ... })
Parameters
Provide one of the following (exactly one is required):
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | The value UUID. |
fqn | string | The value FQN (e.g., https://example.com/attr/classification/value/secret). |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/attributes"
resp, err := client.Attributes.GetAttributeValue(context.Background(),
&attributes.GetAttributeValueRequest{
Identifier: &attributes.GetAttributeValueRequest_Fqn{
Fqn: "https://example.com/attr/classification/value/secret",
},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Value: %s (FQN: %s)\n", resp.GetValue().GetValue(), resp.GetValue().GetFqn())
import io.opentdf.platform.policy.attributes.GetAttributeValueRequest;
var req = GetAttributeValueRequest.newBuilder()
.setFqn("https://example.com/attr/classification/value/secret")
.build();
var resp = sdk.getServices().attributes()
.getAttributeValueBlocking(req, Collections.emptyMap()).execute();
System.out.println("Value: " + resp.getValue().getValue() + " (FQN: " + resp.getValue().getFqn() + ")");
const resp = await platform.v1.attributes.getAttributeValue({
fqn: 'https://example.com/attr/classification/value/secret',
});
console.log(`Value: ${resp.value?.value} (FQN: ${resp.value?.fqn})`);
Returns
A single Attribute Value object.
Get Attribute Values by FQNs
Batch-fetch multiple attribute values by their FQNs in a single request. Returns a map keyed by FQN, each entry containing both the parent attribute definition and the specific value.
Signature
- Go
- Java
- JavaScript
client.Attributes.GetAttributeValuesByFqns(ctx, &attributes.GetAttributeValuesByFqnsRequest{...})
sdk.getServices().attributes().getAttributeValuesByFqnsBlocking(req, metadata).execute()
await platform.v1.attributes.getAttributeValuesByFqns({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
fqns | []string | Yes | Attribute value FQNs to look up. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/attributes"
resp, err := client.Attributes.GetAttributeValuesByFqns(context.Background(),
&attributes.GetAttributeValuesByFqnsRequest{
Fqns: []string{
"https://example.com/attr/classification/value/secret",
"https://example.com/attr/department/value/engineering",
},
},
)
if err != nil {
log.Fatal(err)
}
for fqn, entry := range resp.GetFqnAttributeValues() {
log.Printf("FQN: %s -> Attribute: %s, Value: %s\n",
fqn, entry.GetAttribute().GetName(), entry.GetValue().GetValue())
}
import io.opentdf.platform.policy.attributes.GetAttributeValuesByFqnsRequest;
import java.util.List;
var req = GetAttributeValuesByFqnsRequest.newBuilder()
.addAllFqns(List.of(
"https://example.com/attr/classification/value/secret",
"https://example.com/attr/department/value/engineering"
))
.build();
var resp = sdk.getServices().attributes()
.getAttributeValuesByFqnsBlocking(req, Collections.emptyMap()).execute();
resp.getFqnAttributeValuesMap().forEach((fqn, entry) ->
System.out.println("FQN: " + fqn + " -> Attribute: " + entry.getAttribute().getName() +
", Value: " + entry.getValue().getValue())
);
const resp = await platform.v1.attributes.getAttributeValuesByFqns({
fqns: [
'https://example.com/attr/classification/value/secret',
'https://example.com/attr/department/value/engineering',
],
});
for (const [fqn, entry] of Object.entries(resp.fqnAttributeValues)) {
console.log(`FQN: ${fqn} -> Attribute: ${entry.attribute?.name}, Value: ${entry.value?.value}`);
}
Returns
A map of FQN to {attribute, value} pairs. Each entry contains the parent Attribute object and the specific Attribute Value object.
Update an Attribute Value
Signature
- Go
- Java
- JavaScript
client.Attributes.UpdateAttributeValue(ctx, &attributes.UpdateAttributeValueRequest{...})
sdk.getServices().attributes().updateAttributeValueBlocking(req, metadata).execute()
await platform.v1.attributes.updateAttributeValue({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The value ID to update. |
metadata | Metadata | No | Optional labels. |
metadataUpdateBehavior | string | No | METADATA_UPDATE_ENUM_REPLACE or METADATA_UPDATE_ENUM_EXTEND (default). |
Example
- Go
- Java
- JavaScript
import (
"github.com/opentdf/platform/protocol/go/common"
"github.com/opentdf/platform/protocol/go/policy/attributes"
)
resp, err := client.Attributes.UpdateAttributeValue(context.Background(),
&attributes.UpdateAttributeValueRequest{
Id: "v1b2c3d4-0000-0000-0000-000000000001",
Metadata: &common.MetadataMutable{
Labels: map[string]string{"deprecated": "false"},
},
MetadataUpdateBehavior: common.MetadataUpdateEnum_METADATA_UPDATE_ENUM_EXTEND,
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated value ID: %s\n", resp.GetValue().GetId())
import io.opentdf.platform.policy.attributes.UpdateAttributeValueRequest;
import io.opentdf.platform.common.Common.MetadataMutable;
import io.opentdf.platform.common.Common.MetadataUpdateEnum;
var req = UpdateAttributeValueRequest.newBuilder()
.setId("v1b2c3d4-0000-0000-0000-000000000001")
.setMetadata(MetadataMutable.newBuilder()
.putLabels("deprecated", "false")
.build())
.setMetadataUpdateBehavior(MetadataUpdateEnum.METADATA_UPDATE_ENUM_EXTEND)
.build();
var resp = sdk.getServices().attributes()
.updateAttributeValueBlocking(req, Collections.emptyMap()).execute();
System.out.println("Updated value ID: " + resp.getValue().getId());
const resp = await platform.v1.attributes.updateAttributeValue({
id: 'v1b2c3d4-0000-0000-0000-000000000001',
metadata: { labels: { deprecated: 'false' } },
metadataUpdateBehavior: 'METADATA_UPDATE_ENUM_EXTEND',
});
console.log(`Updated value ID: ${resp.value?.id}`);
Returns
The updated Attribute Value object.
Deactivate an Attribute Value
Signature
- Go
- Java
- JavaScript
client.Attributes.DeactivateAttributeValue(ctx, &attributes.DeactivateAttributeValueRequest{...})
sdk.getServices().attributes().deactivateAttributeValueBlocking(req, metadata).execute()
await platform.v1.attributes.deactivateAttributeValue({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The value ID to deactivate. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/attributes"
resp, err := client.Attributes.DeactivateAttributeValue(context.Background(),
&attributes.DeactivateAttributeValueRequest{
Id: "v1b2c3d4-0000-0000-0000-000000000001",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deactivated value: %s\n", resp.GetValue().GetId())
import io.opentdf.platform.policy.attributes.DeactivateAttributeValueRequest;
var req = DeactivateAttributeValueRequest.newBuilder()
.setId("v1b2c3d4-0000-0000-0000-000000000001")
.build();
var resp = sdk.getServices().attributes()
.deactivateAttributeValueBlocking(req, Collections.emptyMap()).execute();
System.out.println("Deactivated value: " + resp.getValue().getId());
const resp = await platform.v1.attributes.deactivateAttributeValue({
id: 'v1b2c3d4-0000-0000-0000-000000000001',
});
console.log(`Deactivated value: ${resp.value?.id}`);
Returns
Empty response. The value is soft-deleted and will no longer appear in listings unless you filter by INACTIVE or ANY state.
Attribute Object
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier, generated by the platform. |
name | string | The attribute name (e.g., department, clearance). |
fqn | string | Fully qualified name, e.g., https://opentdf.io/attr/department. |
rule | string | Access rule: ATTRIBUTE_RULE_TYPE_ENUM_ALL_OF, ATTRIBUTE_RULE_TYPE_ENUM_ANY_OF, or ATTRIBUTE_RULE_TYPE_ENUM_HIERARCHY. |
values | []Value | The attribute values defined under this attribute. |
namespace | Namespace | The parent Namespace. |
active | bool | true until explicitly deactivated. |
metadata | Metadata | Optional labels. |
createdAt | timestamp | When the attribute was created. |
updatedAt | timestamp | When the attribute was last modified. |
Attribute Value Object
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier, generated by the platform. |
value | string | The value string (e.g., finance, confidential). |
fqn | string | Fully qualified name, e.g., https://opentdf.io/attr/department/value/finance. |
attribute | Attribute | The parent Attribute. |
active | bool | true until explicitly deactivated. |
metadata | Metadata | Optional labels. |
createdAt | timestamp | When the value was created. |
updatedAt | timestamp | When the value was last modified. |
Subject Condition Sets
A subject condition set (SCS) is a reusable, named set of conditions that evaluate claims from an entity's token. Multiple subject mappings can reference the same SCS.
Subject condition sets are permanently deleted (not deactivated).
Create a Subject Condition Set
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.CreateSubjectConditionSet(ctx, &subjectmapping.CreateSubjectConditionSetRequest{...})
sdk.getServices().subjectMappings().createSubjectConditionSetBlocking(req, metadata).execute()
await platform.v1.subjectMapping.createSubjectConditionSet({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
subjectConditionSet.subjectSets | []SubjectSet | Yes | One or more subject sets. Each contains condition groups with conditions that match against entity token claims. Subject sets are evaluated with AND logic. See Condition Structure. |
metadata | Metadata | No | Optional labels. |
Example
- Go
- Java
- JavaScript
import (
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
)
// Create Subject Condition Set
conditionset := &subjectmapping.CreateSubjectConditionSetRequest{
SubjectConditionSet: &subjectmapping.SubjectConditionSetCreate{
SubjectSets: []*policy.SubjectSet{
{
ConditionGroups: []*policy.ConditionGroup{
{
BooleanOperator: policy.ConditionBooleanTypeEnum_CONDITION_BOOLEAN_TYPE_ENUM_AND,
Conditions: []*policy.Condition{
{
SubjectExternalSelectorValue: ".clientId",
Operator: policy.SubjectMappingOperatorEnum_SUBJECT_MAPPING_OPERATOR_ENUM_IN,
SubjectExternalValues: []string{"opentdf"},
},
},
},
},
},
},
},
}
resp, err := client.SubjectMapping.CreateSubjectConditionSet(context.Background(), conditionset)
if err != nil {
log.Fatal(err)
}
log.Printf("Created Subject Condition Set with ID: %s\n", resp.GetSubjectConditionSet().GetId())
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.Condition;
import io.opentdf.platform.policy.ConditionBooleanTypeEnum;
import io.opentdf.platform.policy.ConditionGroup;
import io.opentdf.platform.policy.SubjectConditionSet;
import io.opentdf.platform.policy.SubjectMappingOperatorEnum;
import io.opentdf.platform.policy.SubjectSet;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectConditionSetRequest;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectConditionSetResponse;
import io.opentdf.platform.policy.subjectmapping.SubjectConditionSetCreate;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
public class CreateSubjectConditionSet {
private static final Logger logger = LogManager.getLogger(CreateSubjectConditionSet.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
SubjectSet.Builder subjectSetBuilder =
SubjectSet.newBuilder()
.addConditionGroups(
ConditionGroup.newBuilder()
.setBooleanOperator(ConditionBooleanTypeEnum.CONDITION_BOOLEAN_TYPE_ENUM_AND)
.addConditions(
Condition.newBuilder()
.setSubjectExternalSelectorValue(".myfield")
.setOperator(
SubjectMappingOperatorEnum.SUBJECT_MAPPING_OPERATOR_ENUM_IN)
.addSubjectExternalValues("myvalue")));
CreateSubjectConditionSetRequest createSubjectConditionSetRequest =
CreateSubjectConditionSetRequest.newBuilder()
.setSubjectConditionSet(
SubjectConditionSetCreate.newBuilder().addSubjectSets(subjectSetBuilder))
.build();
CreateSubjectConditionSetResponse createSubjectConditionSetResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.createSubjectConditionSetBlocking(
createSubjectConditionSetRequest, Collections.emptyMap())
.execute());
SubjectConditionSet subjectConditionSet =
createSubjectConditionSetResponse.getSubjectConditionSet();
logger.info(
"Successfully created subject condition set with ID: {}", subjectConditionSet.getId());
} catch (Exception e) {
logger.error("Failed to create subject condition set", e);
}
}
}
import { create } from '@bufbuild/protobuf';
import {
ConditionBooleanTypeEnum,
SubjectMappingOperatorEnum,
} from '@opentdf/sdk/platform/policy/objects_pb.js';
import {
CreateSubjectConditionSetRequestSchema,
} from '@opentdf/sdk/platform/policy/subjectmapping/subject_mapping_pb.js';
// Create Subject Condition Set
// Using create() from @bufbuild/protobuf for proper type handling
const request = create(CreateSubjectConditionSetRequestSchema, {
subjectConditionSet: {
subjectSets: [
{
conditionGroups: [
{
booleanOperator: ConditionBooleanTypeEnum.AND,
conditions: [
{
subjectExternalSelectorValue: '.clientId',
operator: SubjectMappingOperatorEnum.IN,
subjectExternalValues: ['opentdf'],
},
],
},
],
},
],
},
});
const resp = await platform.v1.subjectMapping.createSubjectConditionSet(request);
console.log(
'Created Subject Condition Set with ID:',
resp.subjectConditionSet?.id
);
Returns
The created Subject Condition Set object.
List Subject Condition Sets
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.ListSubjectConditionSets(ctx, &subjectmapping.ListSubjectConditionSetsRequest{...})
sdk.getServices().subjectMappings().listSubjectConditionSetsBlocking(req, metadata).execute()
await platform.v1.subjectMapping.listSubjectConditionSets({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
namespaceId | string (UUID) | No | Filter to condition sets in this namespace. |
namespaceFqn | string (URI) | No | Filter by namespace FQN (e.g., https://example.com). Alternative to namespaceId. |
pagination.limit | int | No | Maximum results per page. |
pagination.offset | int | No | Number of results to skip. |
Without a namespace filter, returns all subject condition sets across all namespaces.
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/subjectmapping"
resp, err := client.SubjectMapping.ListSubjectConditionSets(context.Background(),
&subjectmapping.ListSubjectConditionSetsRequest{},
)
if err != nil {
log.Fatal(err)
}
for _, scs := range resp.GetSubjectConditionSets() {
log.Printf("SCS ID: %s\n", scs.GetId())
}
import io.opentdf.platform.policy.subjectmapping.ListSubjectConditionSetsRequest;
var req = ListSubjectConditionSetsRequest.newBuilder().build();
var resp = sdk.getServices().subjectMappings()
.listSubjectConditionSetsBlocking(req, Collections.emptyMap()).execute();
for (var scs : resp.getSubjectConditionSetsList()) {
System.out.println("SCS ID: " + scs.getId());
}
const resp = await platform.v1.subjectMapping.listSubjectConditionSets({});
for (const scs of resp.subjectConditionSets) {
console.log(`SCS ID: ${scs.id}`);
}
Returns
A list of Subject Condition Set objects. Includes pagination metadata.
Get a Subject Condition Set
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.GetSubjectConditionSet(ctx, &subjectmapping.GetSubjectConditionSetRequest{...})
sdk.getServices().subjectMappings().getSubjectConditionSetBlocking(req, metadata).execute()
await platform.v1.subjectMapping.getSubjectConditionSet({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The subject condition set ID. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/subjectmapping"
resp, err := client.SubjectMapping.GetSubjectConditionSet(context.Background(),
&subjectmapping.GetSubjectConditionSetRequest{
Id: "a0b1c2d3-0000-0000-0000-000000000099",
},
)
if err != nil {
log.Fatal(err)
}
scs := resp.GetSubjectConditionSet()
log.Printf("SCS ID: %s\n", scs.GetId())
log.Printf("Associated subject mappings: %d\n", len(resp.GetAssociatedSubjectMappings()))
import io.opentdf.platform.policy.subjectmapping.GetSubjectConditionSetRequest;
var req = GetSubjectConditionSetRequest.newBuilder()
.setId("a0b1c2d3-0000-0000-0000-000000000099")
.build();
var resp = sdk.getServices().subjectMappings()
.getSubjectConditionSetBlocking(req, Collections.emptyMap()).execute();
System.out.println("SCS ID: " + resp.getSubjectConditionSet().getId());
System.out.println("Associated mappings: " + resp.getAssociatedSubjectMappingsList().size());
const resp = await platform.v1.subjectMapping.getSubjectConditionSet({
id: 'a0b1c2d3-0000-0000-0000-000000000099',
});
console.log(`SCS ID: ${resp.subjectConditionSet?.id}`);
console.log(`Associated subject mappings: ${resp.associatedSubjectMappings.length}`);
Returns
The Subject Condition Set object, plus associatedSubjectMappings — all subject mappings currently referencing this condition set.
Update a Subject Condition Set
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.UpdateSubjectConditionSet(ctx, &subjectmapping.UpdateSubjectConditionSetRequest{...})
sdk.getServices().subjectMappings().updateSubjectConditionSetBlocking(req, metadata).execute()
await platform.v1.subjectMapping.updateSubjectConditionSet({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The subject condition set ID to update. |
subjectSets | []SubjectSet | No | Replaces the entire condition tree. Omit to update metadata only. |
metadata | Metadata | No | Optional labels. |
metadataUpdateBehavior | string | No | METADATA_UPDATE_ENUM_REPLACE or METADATA_UPDATE_ENUM_EXTEND (default). |
Example
- Go
- Java
- JavaScript
Providing SubjectSets replaces the entire condition tree. Omit it to update metadata only.
import (
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
)
resp, err := client.SubjectMapping.UpdateSubjectConditionSet(context.Background(),
&subjectmapping.UpdateSubjectConditionSetRequest{
Id: "a0b1c2d3-0000-0000-0000-000000000099",
SubjectSets: []*policy.SubjectSet{
{
ConditionGroups: []*policy.ConditionGroup{
{
BooleanOperator: policy.ConditionBooleanTypeEnum_CONDITION_BOOLEAN_TYPE_ENUM_AND,
Conditions: []*policy.Condition{
{
SubjectExternalSelectorValue: ".clientId",
Operator: policy.SubjectMappingOperatorEnum_SUBJECT_MAPPING_OPERATOR_ENUM_IN,
SubjectExternalValues: []string{"my-service", "my-other-service"},
},
},
},
},
},
},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated SCS ID: %s\n", resp.GetSubjectConditionSet().GetId())
import io.opentdf.platform.policy.*;
import io.opentdf.platform.policy.subjectmapping.UpdateSubjectConditionSetRequest;
var condition = Condition.newBuilder()
.setSubjectExternalSelectorValue(".clientId")
.setOperator(SubjectMappingOperatorEnum.SUBJECT_MAPPING_OPERATOR_ENUM_IN)
.addSubjectExternalValues("my-service")
.addSubjectExternalValues("my-other-service")
.build();
var conditionGroup = ConditionGroup.newBuilder()
.setBooleanOperator(ConditionBooleanTypeEnum.CONDITION_BOOLEAN_TYPE_ENUM_AND)
.addConditions(condition)
.build();
var subjectSet = SubjectSet.newBuilder()
.addConditionGroups(conditionGroup)
.build();
var req = UpdateSubjectConditionSetRequest.newBuilder()
.setId("a0b1c2d3-0000-0000-0000-000000000099")
.addSubjectSets(subjectSet)
.build();
var resp = sdk.getServices().subjectMappings()
.updateSubjectConditionSetBlocking(req, Collections.emptyMap()).execute();
System.out.println("Updated SCS ID: " + resp.getSubjectConditionSet().getId());
import { authTokenInterceptor, clientCredentialsTokenProvider } from '@opentdf/sdk';
const resp = await platform.v1.subjectMapping.updateSubjectConditionSet({
id: 'a0b1c2d3-0000-0000-0000-000000000099',
subjectSets: [
{
conditionGroups: [
{
booleanOperator: 'CONDITION_BOOLEAN_TYPE_ENUM_AND',
conditions: [
{
subjectExternalSelectorValue: '.clientId',
operator: 'SUBJECT_MAPPING_OPERATOR_ENUM_IN',
subjectExternalValues: ['my-service', 'my-other-service'],
},
],
},
],
},
],
});
console.log(`Updated SCS ID: ${resp.subjectConditionSet?.id}`);
Returns
The updated Subject Condition Set object.
Delete a Subject Condition Set
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.DeleteSubjectConditionSet(ctx, &subjectmapping.DeleteSubjectConditionSetRequest{...})
sdk.getServices().subjectMappings().deleteSubjectConditionSetBlocking(req, metadata).execute()
await platform.v1.subjectMapping.deleteSubjectConditionSet({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The subject condition set ID to delete. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/subjectmapping"
resp, err := client.SubjectMapping.DeleteSubjectConditionSet(context.Background(),
&subjectmapping.DeleteSubjectConditionSetRequest{
Id: "a0b1c2d3-0000-0000-0000-000000000099",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deleted SCS ID: %s\n", resp.GetSubjectConditionSet().GetId())
import io.opentdf.platform.policy.subjectmapping.DeleteSubjectConditionSetRequest;
var req = DeleteSubjectConditionSetRequest.newBuilder()
.setId("a0b1c2d3-0000-0000-0000-000000000099")
.build();
var resp = sdk.getServices().subjectMappings()
.deleteSubjectConditionSetBlocking(req, Collections.emptyMap()).execute();
System.out.println("Deleted SCS ID: " + resp.getSubjectConditionSet().getId());
import { authTokenInterceptor, clientCredentialsTokenProvider } from '@opentdf/sdk';
const resp = await platform.v1.subjectMapping.deleteSubjectConditionSet({
id: 'a0b1c2d3-0000-0000-0000-000000000099',
});
console.log(`Deleted SCS ID: ${resp.subjectConditionSet?.id}`);
Returns
The deleted Subject Condition Set object. This is a hard delete — the object is permanently removed.
Delete All Unmapped Subject Condition Sets
Permanently deletes all subject condition sets that are not referenced by any subject mapping. Use this as a maintenance operation to clean up orphaned condition sets.
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.DeleteAllUnmappedSubjectConditionSets(ctx, &subjectmapping.DeleteAllUnmappedSubjectConditionSetsRequest{})
sdk.getServices().subjectMappings().deleteAllUnmappedSubjectConditionSetsBlocking(req, metadata).execute()
await platform.v1.subjectMapping.deleteAllUnmappedSubjectConditionSets({})
Parameters
None.
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/subjectmapping"
resp, err := client.SubjectMapping.DeleteAllUnmappedSubjectConditionSets(context.Background(),
&subjectmapping.DeleteAllUnmappedSubjectConditionSetsRequest{},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deleted %d unmapped subject condition sets.\n", len(resp.GetSubjectConditionSets()))
import io.opentdf.platform.policy.subjectmapping.DeleteAllUnmappedSubjectConditionSetsRequest;
var req = DeleteAllUnmappedSubjectConditionSetsRequest.newBuilder().build();
var resp = sdk.getServices().subjectMappings()
.deleteAllUnmappedSubjectConditionSetsBlocking(req, Collections.emptyMap()).execute();
System.out.println("Deleted " + resp.getSubjectConditionSetsList().size() + " unmapped subject condition sets.");
import { authTokenInterceptor, clientCredentialsTokenProvider } from '@opentdf/sdk';
const resp = await platform.v1.subjectMapping.deleteAllUnmappedSubjectConditionSets({});
console.log(`Deleted ${resp.subjectConditionSets.length} unmapped subject condition sets.`);
Returns
A list of all deleted Subject Condition Set objects.
Subject Condition Set Object
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier, generated by the platform. |
subjectSets | []SubjectSet | One or more subject sets, evaluated with AND logic — all must match. |
namespace | Namespace | The parent Namespace, if assigned. |
metadata | Metadata | Optional labels. |
createdAt | timestamp | When the condition set was created. |
updatedAt | timestamp | When the condition set was last modified. |
Condition Structure
A Subject Condition Set contains a tree of nested objects that define which entities match. Here's how they fit together:
SubjectConditionSet
└── subjectSets[] ← AND across sets
└── conditionGroups[] ← AND across groups
├── booleanOperator ← AND or OR across conditions within this group
└── conditions[]
├── subjectExternalSelectorValue ← claim path (e.g., ".clientId")
├── operator ← IN, NOT_IN, or IN_CONTAINS
└── subjectExternalValues[] ← expected values
Evaluation logic: All subject sets must match (AND). Within each set, all condition groups must match (AND). Within each group, conditions are combined by the group's booleanOperator (AND or OR).
SubjectSet
| Field | Type | Description |
|---|---|---|
conditionGroups | []ConditionGroup | One or more condition groups. All groups must match (AND logic). |
ConditionGroup
| Field | Type | Description |
|---|---|---|
booleanOperator | string | How to combine conditions: CONDITION_BOOLEAN_TYPE_ENUM_AND or CONDITION_BOOLEAN_TYPE_ENUM_OR. |
conditions | []Condition | One or more conditions to evaluate. |
Condition
A single claim match against an entity's token.
| Field | Type | Description |
|---|---|---|
subjectExternalSelectorValue | string | A selector path into the entity's flattened token claims (e.g., .clientId, .realm_access.roles). |
operator | string | Comparison operator (see below). |
subjectExternalValues | []string | The values to compare against. |
Operators:
| Operator | Description |
|---|---|
SUBJECT_MAPPING_OPERATOR_ENUM_IN | The claim value must exactly match one of the subjectExternalValues. |
SUBJECT_MAPPING_OPERATOR_ENUM_NOT_IN | The claim value must not match any of the subjectExternalValues. |
SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS | The claim value must contain (substring match) one of the subjectExternalValues. |
Example: "Match entities whose .clientId is my-service AND whose .realm_access.roles contains developer":
{
"subjectSets": [
{
"conditionGroups": [
{
"booleanOperator": "CONDITION_BOOLEAN_TYPE_ENUM_AND",
"conditions": [
{
"subjectExternalSelectorValue": ".clientId",
"operator": "SUBJECT_MAPPING_OPERATOR_ENUM_IN",
"subjectExternalValues": ["my-service"]
},
{
"subjectExternalSelectorValue": ".realm_access.roles",
"operator": "SUBJECT_MAPPING_OPERATOR_ENUM_IN_CONTAINS",
"subjectExternalValues": ["developer"]
}
]
}
]
}
]
}
SubjectProperty
Used in MatchSubjectMappings to test which subject mappings match a given set of entity claims.
| Field | Type | Description |
|---|---|---|
externalSelectorValue | string | A selector path into the entity's token claims (e.g., .clientId, .realm_access.roles). |
externalValue | string | The claim value to test against (e.g., my-service, developer). |
Subject Mappings
A subject mapping links a subject condition set to a specific attribute value and a set of permitted actions (e.g., DECRYPT). It answers the question: "which entities are allowed to do what with data carrying this attribute value?"
Subject mappings are permanently deleted (not deactivated). There is no restore operation.
Create a Subject Mapping
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.CreateSubjectMapping(ctx, &subjectmapping.CreateSubjectMappingRequest{...})
sdk.getServices().subjectMappings().createSubjectMappingBlocking(req, metadata).execute()
await platform.v1.subjectMapping.createSubjectMapping({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
attributeValueId | string (UUID) | Yes | The attribute value this mapping grants access to. |
actions | []Action | Yes | Permitted actions (e.g., read, create). |
existingSubjectConditionSetId | string (UUID) | Yes* | ID of an existing subject condition set. Required unless newSubjectConditionSet is provided. |
newSubjectConditionSet | object | Yes* | Create a new condition set inline. Required unless existingSubjectConditionSetId is provided. Contains a subjectSets array — see Condition Structure. |
metadata | Metadata | No | Optional labels. |
Example
- Go
- Java
- JavaScript
import (
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
)
// Create Subject Mapping
subjectMapping := &subjectmapping.CreateSubjectMappingRequest{
AttributeValueId: "224c9d29-2cd4-4a38-b6ad-5f025ca93a8c",
Actions: []*policy.Action{
{
Value: &policy.Action_Standard{
Standard: policy.Action_STANDARD_ACTION_DECRYPT,
},
},
},
ExistingSubjectConditionSetId: "890b26db-4ee4-447f-ae8a-2862d922eeef",
}
_, err = client.SubjectMapping.CreateSubjectMapping(context.Background(), subjectMapping)
if err != nil {
log.Fatal(err)
}
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.Action;
import io.opentdf.platform.policy.Attribute;
import io.opentdf.platform.policy.Condition;
import io.opentdf.platform.policy.ConditionBooleanTypeEnum;
import io.opentdf.platform.policy.ConditionGroup;
import io.opentdf.platform.policy.Namespace;
import io.opentdf.platform.policy.SubjectConditionSet;
import io.opentdf.platform.policy.SubjectMapping;
import io.opentdf.platform.policy.SubjectMappingOperatorEnum;
import io.opentdf.platform.policy.SubjectSet;
import io.opentdf.platform.policy.attributes.GetAttributeRequest;
import io.opentdf.platform.policy.namespaces.GetNamespaceRequest;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectConditionSetRequest;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectConditionSetResponse;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectMappingRequest;
import io.opentdf.platform.policy.subjectmapping.CreateSubjectMappingResponse;
import io.opentdf.platform.policy.subjectmapping.SubjectConditionSetCreate;
import io.opentdf.platform.sdk.SDK;
import io.opentdf.platform.sdk.SDKBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.Objects;
public class CreateSubjectMapping {
private static final Logger logger = LogManager.getLogger(CreateSubjectMapping.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
String namespaceName = "opentdf.io";
String attributeName = "test-attribute";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
Namespace namespace;
try {
namespace =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.namespaces()
.getNamespaceBlocking(
GetNamespaceRequest.newBuilder()
.setFqn("https://" + namespaceName)
.build(),
Collections.emptyMap())
.execute())
.getNamespace();
} catch (Exception e) {
if (Objects.equals(e.getMessage(), "resource not found")) {
logger.error("Namespace '{}' not found", namespaceName, e);
} else {
logger.error("Failed to retrieve namespace '{}'", namespaceName, e);
}
return;
}
Attribute attribute;
String attributeFqn = namespace.getFqn() + "/attr/" + attributeName;
try {
GetAttributeRequest getAttributeRequest =
GetAttributeRequest.newBuilder().setFqn(attributeFqn).build();
attribute =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.attributes()
.getAttributeBlocking(getAttributeRequest, Collections.emptyMap())
.execute())
.getAttribute();
} catch (Exception e) {
if (Objects.equals(e.getMessage(), "resource not found")) {
logger.error("Attribute '{}' not found", attributeFqn, e);
} else {
logger.error("Failed to retrieve attribute '{}'", attributeFqn, e);
}
return;
}
CreateSubjectConditionSetRequest subjectConditionSetRequest =
CreateSubjectConditionSetRequest.newBuilder()
.setSubjectConditionSet(
SubjectConditionSetCreate.newBuilder()
.addSubjectSets(
SubjectSet.newBuilder()
.addConditionGroups(
ConditionGroup.newBuilder()
.setBooleanOperator(
ConditionBooleanTypeEnum.CONDITION_BOOLEAN_TYPE_ENUM_AND)
.addConditions(
Condition.newBuilder()
.setSubjectExternalSelectorValue(".myfield")
.setOperator(
SubjectMappingOperatorEnum
.SUBJECT_MAPPING_OPERATOR_ENUM_IN)
.addSubjectExternalValues("myvalue")))))
.build();
CreateSubjectConditionSetResponse subjectConditionSetResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.createSubjectConditionSetBlocking(
subjectConditionSetRequest, Collections.emptyMap())
.execute());
SubjectConditionSet subjectConditionSet =
subjectConditionSetResponse.getSubjectConditionSet();
CreateSubjectMappingRequest request =
CreateSubjectMappingRequest.newBuilder()
.setAttributeValueId(attribute.getValues(0).getId())
.addActions(Action.newBuilder().setName("read"))
.setExistingSubjectConditionSetId(subjectConditionSet.getId())
.build();
CreateSubjectMappingResponse resp =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.createSubjectMappingBlocking(request, Collections.emptyMap())
.execute());
SubjectMapping subjectMapping = resp.getSubjectMapping();
logger.info("Successfully created subject mapping with ID: {}", subjectMapping.getId());
} catch (Exception e) {
logger.error("Failed to create subject mapping", e);
}
}
}
// Create Subject Mapping
await platform.v1.subjectMapping.createSubjectMapping({
attributeValueId: '224c9d29-2cd4-4a38-b6ad-5f025ca93a8c',
actions: [{ name: 'decrypt' }],
existingSubjectConditionSetId: '890b26db-4ee4-447f-ae8a-2862d922eeef',
});
Returns
The created Subject Mapping object.
List Subject Mappings
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.ListSubjectMappings(ctx, &subjectmapping.ListSubjectMappingsRequest{...})
sdk.getServices().subjectMappings().listSubjectMappingsBlocking(req, metadata).execute()
await platform.v1.subjectMapping.listSubjectMappings({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
namespaceId | string (UUID) | No | Filter to mappings in this namespace. |
namespaceFqn | string (URI) | No | Filter by namespace FQN. Alternative to namespaceId. |
pagination.limit | int | No | Maximum results per page. |
pagination.offset | int | No | Number of results to skip. |
Without a namespace filter, returns all subject mappings across all namespaces.
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/subjectmapping"
// List Subject Mapping
subjectmappings, err := client.SubjectMapping.ListSubjectMappings(context.Background(), &subjectmapping.ListSubjectMappingsRequest{})
if err != nil {
log.Fatal(err)
}
for _, sm := range subjectmappings.GetSubjectMappings() {
log.Printf("Subject Mapping: %s", sm.GetId())
}
package io.opentdf.platform;
import com.connectrpc.ResponseMessageKt;
import io.opentdf.platform.policy.SubjectMapping;
import io.opentdf.platform.policy.subjectmapping.ListSubjectMappingsRequest;
import io.opentdf.platform.policy.subjectmapping.ListSubjectMappingsResponse;
import io.opentdf.platform.sdk.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class ListSubjectMappings {
private static final Logger logger = LogManager.getLogger(ListSubjectMappings.class);
public static void main(String[] args) {
String clientId = "opentdf";
String clientSecret = "secret";
String platformEndpoint = "localhost:8080";
SDKBuilder builder = new SDKBuilder();
try (SDK sdk =
builder
.platformEndpoint(platformEndpoint)
.clientSecret(clientId, clientSecret)
.useInsecurePlaintextConnection(true)
.build()) {
ListSubjectMappingsRequest listSubjectMappingsRequest =
ListSubjectMappingsRequest.newBuilder().build();
ListSubjectMappingsResponse listSubjectMappingsResponse =
ResponseMessageKt.getOrThrow(
sdk.getServices()
.subjectMappings()
.listSubjectMappingsBlocking(listSubjectMappingsRequest, Collections.emptyMap())
.execute());
List<SubjectMapping> subjectMappings = listSubjectMappingsResponse.getSubjectMappingsList();
logger.info(
"Successfully retrieved subject mappings: [{}]",
subjectMappings.stream().map(SubjectMapping::getId).collect(Collectors.joining(", ")));
} catch (Exception e) {
logger.error("Failed to list subject mappings", e);
}
}
}
// List Subject Mappings
const resp = await platform.v1.subjectMapping.listSubjectMappings({});
for (const sm of resp.subjectMappings) {
console.log('Subject Mapping:', sm.id);
}
Returns
A list of Subject Mapping objects. Includes pagination metadata.
Get a Subject Mapping
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.GetSubjectMapping(ctx, &subjectmapping.GetSubjectMappingRequest{...})
sdk.getServices().subjectMappings().getSubjectMappingBlocking(req, metadata).execute()
await platform.v1.subjectMapping.getSubjectMapping({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The subject mapping ID. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/subjectmapping"
resp, err := client.SubjectMapping.GetSubjectMapping(context.Background(),
&subjectmapping.GetSubjectMappingRequest{
Id: "890b26db-4ee4-447f-ae8a-2862d922eeef",
},
)
if err != nil {
log.Fatal(err)
}
sm := resp.GetSubjectMapping()
log.Printf("Subject Mapping ID: %s, Attribute Value ID: %s\n",
sm.GetId(), sm.GetAttributeValue().GetId())
import io.opentdf.platform.policy.subjectmapping.GetSubjectMappingRequest;
var req = GetSubjectMappingRequest.newBuilder()
.setId("890b26db-4ee4-447f-ae8a-2862d922eeef")
.build();
var resp = sdk.getServices().subjectMappings()
.getSubjectMappingBlocking(req, Collections.emptyMap()).execute();
System.out.println("Subject Mapping ID: " + resp.getSubjectMapping().getId());
const resp = await platform.v1.subjectMapping.getSubjectMapping({
id: '890b26db-4ee4-447f-ae8a-2862d922eeef',
});
console.log(`Subject Mapping ID: ${resp.subjectMapping?.id}`);
Returns
A single Subject Mapping object.
Update a Subject Mapping
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.UpdateSubjectMapping(ctx, &subjectmapping.UpdateSubjectMappingRequest{...})
sdk.getServices().subjectMappings().updateSubjectMappingBlocking(req, metadata).execute()
await platform.v1.subjectMapping.updateSubjectMapping({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The subject mapping ID to update. |
actions | []Action | No | Replaces the entire actions list. Omit to leave unchanged. |
subjectConditionSetId | string (UUID) | No | Swap the linked condition set. |
metadata | Metadata | No | Optional labels. |
metadataUpdateBehavior | string | No | METADATA_UPDATE_ENUM_REPLACE or METADATA_UPDATE_ENUM_EXTEND (default). |
Example
- Go
- Java
- JavaScript
import (
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
)
resp, err := client.SubjectMapping.UpdateSubjectMapping(context.Background(),
&subjectmapping.UpdateSubjectMappingRequest{
Id: "890b26db-4ee4-447f-ae8a-2862d922eeef",
Actions: []*policy.Action{
{
Value: &policy.Action_Standard{
Standard: policy.Action_STANDARD_ACTION_DECRYPT,
},
},
},
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Updated subject mapping ID: %s\n", resp.GetSubjectMapping().GetId())
import io.opentdf.platform.policy.Action;
import io.opentdf.platform.policy.subjectmapping.UpdateSubjectMappingRequest;
var decryptAction = Action.newBuilder()
.setStandard(Action.StandardAction.STANDARD_ACTION_DECRYPT)
.build();
var req = UpdateSubjectMappingRequest.newBuilder()
.setId("890b26db-4ee4-447f-ae8a-2862d922eeef")
.addActions(decryptAction)
.build();
var resp = sdk.getServices().subjectMappings()
.updateSubjectMappingBlocking(req, Collections.emptyMap()).execute();
System.out.println("Updated subject mapping ID: " + resp.getSubjectMapping().getId());
const resp = await platform.v1.subjectMapping.updateSubjectMapping({
id: '890b26db-4ee4-447f-ae8a-2862d922eeef',
actions: [{ standard: 'STANDARD_ACTION_DECRYPT' }],
});
console.log(`Updated subject mapping ID: ${resp.subjectMapping?.id}`);
Returns
The updated Subject Mapping object.
Delete a Subject Mapping
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.DeleteSubjectMapping(ctx, &subjectmapping.DeleteSubjectMappingRequest{...})
sdk.getServices().subjectMappings().deleteSubjectMappingBlocking(req, metadata).execute()
await platform.v1.subjectMapping.deleteSubjectMapping({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string (UUID) | Yes | The subject mapping ID to delete. |
Example
- Go
- Java
- JavaScript
import "github.com/opentdf/platform/protocol/go/policy/subjectmapping"
resp, err := client.SubjectMapping.DeleteSubjectMapping(context.Background(),
&subjectmapping.DeleteSubjectMappingRequest{
Id: "890b26db-4ee4-447f-ae8a-2862d922eeef",
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Deleted subject mapping ID: %s\n", resp.GetSubjectMapping().GetId())
import io.opentdf.platform.policy.subjectmapping.DeleteSubjectMappingRequest;
var req = DeleteSubjectMappingRequest.newBuilder()
.setId("890b26db-4ee4-447f-ae8a-2862d922eeef")
.build();
var resp = sdk.getServices().subjectMappings()
.deleteSubjectMappingBlocking(req, Collections.emptyMap()).execute();
System.out.println("Deleted subject mapping ID: " + resp.getSubjectMapping().getId());
const resp = await platform.v1.subjectMapping.deleteSubjectMapping({
id: '890b26db-4ee4-447f-ae8a-2862d922eeef',
});
console.log(`Deleted subject mapping ID: ${resp.subjectMapping?.id}`);
Returns
The deleted Subject Mapping object. This is a hard delete — the object is permanently removed.
Match Subject Mappings
Given a list of subject properties (key-value pairs from an entity's token claims), returns all subject mappings whose condition sets match. Use this to determine which access grants apply to a given entity.
Signature
- Go
- Java
- JavaScript
client.SubjectMapping.MatchSubjectMappings(ctx, &subjectmapping.MatchSubjectMappingsRequest{...})
sdk.getServices().subjectMappings().matchSubjectMappingsBlocking(req, metadata).execute()
await platform.v1.subjectMapping.matchSubjectMappings({ ... })
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
subjectProperties | []SubjectProperty | Yes | Key-value pairs from an entity's claims. Each has externalSelectorValue (the claim path, e.g., .clientId) and externalValue (the claim value). |
Example
- Go
- Java
- JavaScript
import (
"github.com/opentdf/platform/protocol/go/policy"
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
)
resp, err := client.SubjectMapping.MatchSubjectMappings(context.Background(),
&subjectmapping.MatchSubjectMappingsRequest{
SubjectProperties: []*policy.SubjectProperty{
{ExternalSelectorValue: ".clientId", ExternalValue: "my-service"},
{ExternalSelectorValue: ".realm_access.roles", ExternalValue: "developer"},
},
},
)
if err != nil {
log.Fatal(err)
}
for _, sm := range resp.GetSubjectMappings() {
log.Printf("Matched subject mapping ID: %s\n", sm.GetId())
}
import io.opentdf.platform.policy.SubjectProperty;
import io.opentdf.platform.policy.subjectmapping.MatchSubjectMappingsRequest;
var req = MatchSubjectMappingsRequest.newBuilder()
.addSubjectProperties(SubjectProperty.newBuilder()
.setExternalSelectorValue(".clientId")
.setExternalValue("my-service")
.build())
.addSubjectProperties(SubjectProperty.newBuilder()
.setExternalSelectorValue(".realm_access.roles")
.setExternalValue("developer")
.build())
.build();
var resp = sdk.getServices().subjectMappings()
.matchSubjectMappingsBlocking(req, Collections.emptyMap()).execute();
for (var sm : resp.getSubjectMappingsList()) {
System.out.println("Matched subject mapping ID: " + sm.getId());
}
const resp = await platform.v1.subjectMapping.matchSubjectMappings({
subjectProperties: [
{ externalSelectorValue: '.clientId', externalValue: 'my-service' },
{ externalSelectorValue: '.realm_access.roles', externalValue: 'developer' },
],
});
for (const sm of resp.subjectMappings) {
console.log(`Matched subject mapping ID: ${sm.id}`);
}
Returns
A list of Subject Mapping objects whose condition sets matched the provided subject properties.
Action
An action represents what an entity is permitted to do with data. The platform ships with four standard actions that cannot be deleted:
| Standard Action | Description |
|---|---|
read | Read/decrypt data. Used in all TDF decrypt flows. |
create | Create new data. |
update | Update existing data. |
delete | Delete data. |
You can also define custom actions (e.g., download, queue-to-print, send_email). Custom actions are globally unique, not namespaced, and are lowercased when stored. See Actions for more.
Actions are specified by name in the SDK:
// Go
&policy.Action{Name: "read"}
// Java
Action.newBuilder().setName("read").build();
// JavaScript
{ name: 'read' }
Subject Mapping Object
| Field | Type | Description |
|---|---|---|
id | string (UUID) | Unique identifier, generated by the platform. |
attributeValue | Value | The Attribute Value this mapping grants access to. |
subjectConditionSet | SubjectConditionSet | The Subject Condition Set that determines which entities match. |
actions | []Action | Permitted actions (e.g., read, create). |
namespace | Namespace | The parent Namespace, if assigned. |
metadata | Metadata | Optional labels. |
createdAt | timestamp | When the mapping was created. |
updatedAt | timestamp | When the mapping was last modified. |