From 66cc2b95f23554f99e2520a13e99b7322dc0cab8 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Fri, 6 Mar 2026 14:45:47 -0500 Subject: [PATCH 1/4] make obligation trigger uniqueness client-aware --- .../integration/obligation_triggers_test.go | 62 +++++++++++++++++++ ...igation_trigger_uniqueness_client_aware.md | 31 ++++++++++ ...gation_trigger_uniqueness_client_aware.sql | 30 +++++++++ 3 files changed, 123 insertions(+) create mode 100644 service/policy/db/migrations/20260306000000_make_obligation_trigger_uniqueness_client_aware.md create mode 100644 service/policy/db/migrations/20260306000000_make_obligation_trigger_uniqueness_client_aware.sql diff --git a/service/integration/obligation_triggers_test.go b/service/integration/obligation_triggers_test.go index 504010f455..74efce3448 100644 --- a/service/integration/obligation_triggers_test.go +++ b/service/integration/obligation_triggers_test.go @@ -27,6 +27,7 @@ const ( obligationName = "test-obligation" obligationValue = "test-obligation-value" clientID = "test-client-id" + secondClientID = "test-client-id-2" ) type ObligationTriggersSuite struct { @@ -171,6 +172,67 @@ func (s *ObligationTriggersSuite) Test_CreateObligationTrigger_WithIDs_Success() s.Require().Equal("test", trigger.GetMetadata().GetLabels()["source"]) } +func (s *ObligationTriggersSuite) Test_CreateObligationTrigger_SameTupleDifferentClients_Success() { + firstTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, &obligations.AddObligationTriggerRequest{ + ObligationValue: &common.IdFqnIdentifier{Id: s.obligationValue.GetId()}, + AttributeValue: &common.IdFqnIdentifier{Id: s.attributeValue.GetId()}, + Action: &common.IdNameIdentifier{Id: s.action.GetId()}, + Context: &policy.RequestContext{ + Pep: &policy.PolicyEnforcementPoint{ + ClientId: clientID, + }, + }, + }) + s.Require().NoError(err) + s.triggerIDsToClean = append(s.triggerIDsToClean, firstTrigger.GetId()) + s.validateTriggerWithDefaults(firstTrigger, true) + + secondTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, &obligations.AddObligationTriggerRequest{ + ObligationValue: &common.IdFqnIdentifier{Id: s.obligationValue.GetId()}, + AttributeValue: &common.IdFqnIdentifier{Id: s.attributeValue.GetId()}, + Action: &common.IdNameIdentifier{Id: s.action.GetId()}, + Context: &policy.RequestContext{ + Pep: &policy.PolicyEnforcementPoint{ + ClientId: secondClientID, + }, + }, + }) + s.Require().NoError(err) + s.triggerIDsToClean = append(s.triggerIDsToClean, secondTrigger.GetId()) + s.Require().NotEqual(firstTrigger.GetId(), secondTrigger.GetId()) + s.Require().Len(secondTrigger.GetContext(), 1) + s.Require().Equal(secondClientID, secondTrigger.GetContext()[0].GetPep().GetClientId()) +} + +func (s *ObligationTriggersSuite) Test_CreateObligationTrigger_SameTupleSameClient_Fails() { + firstTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, &obligations.AddObligationTriggerRequest{ + ObligationValue: &common.IdFqnIdentifier{Id: s.obligationValue.GetId()}, + AttributeValue: &common.IdFqnIdentifier{Id: s.attributeValue.GetId()}, + Action: &common.IdNameIdentifier{Id: s.action.GetId()}, + Context: &policy.RequestContext{ + Pep: &policy.PolicyEnforcementPoint{ + ClientId: clientID, + }, + }, + }) + s.Require().NoError(err) + s.triggerIDsToClean = append(s.triggerIDsToClean, firstTrigger.GetId()) + + duplicateTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, &obligations.AddObligationTriggerRequest{ + ObligationValue: &common.IdFqnIdentifier{Id: s.obligationValue.GetId()}, + AttributeValue: &common.IdFqnIdentifier{Id: s.attributeValue.GetId()}, + Action: &common.IdNameIdentifier{Id: s.action.GetId()}, + Context: &policy.RequestContext{ + Pep: &policy.PolicyEnforcementPoint{ + ClientId: clientID, + }, + }, + }) + s.Require().Error(err) + s.Require().ErrorIs(err, db.ErrUniqueConstraintViolation) + s.Nil(duplicateTrigger) +} + func (s *ObligationTriggersSuite) Test_CreateObligationTrigger_NoCtx_Success() { trigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, &obligations.AddObligationTriggerRequest{ ObligationValue: &common.IdFqnIdentifier{Id: s.obligationValue.GetId()}, diff --git a/service/policy/db/migrations/20260306000000_make_obligation_trigger_uniqueness_client_aware.md b/service/policy/db/migrations/20260306000000_make_obligation_trigger_uniqueness_client_aware.md new file mode 100644 index 0000000000..ff768c0a11 --- /dev/null +++ b/service/policy/db/migrations/20260306000000_make_obligation_trigger_uniqueness_client_aware.md @@ -0,0 +1,31 @@ +# Make Obligation Trigger Uniqueness Client-Aware + +This migration updates uniqueness semantics for `obligation_triggers` after the +introduction of optional `client_id` scoping. + +## Why + +The previous unique constraint only considered: + +- `obligation_value_id` +- `action_id` +- `attribute_value_id` + +That prevented creating multiple triggers for different PEP clients when all +other fields were the same. + +## Changes + +1. Drop the existing table-level unique constraint on: + - `(obligation_value_id, action_id, attribute_value_id)` + - Includes handling historical truncated constraint names in Postgres. +2. Add partial unique index for unscoped triggers: + - `(obligation_value_id, action_id, attribute_value_id)` where `client_id IS NULL` +3. Add partial unique index for client-scoped triggers: + - `(obligation_value_id, action_id, attribute_value_id, client_id)` where `client_id IS NOT NULL` + +## Resulting Behavior + +- Allows one unscoped trigger per obligation/action/attribute tuple. +- Allows one scoped trigger per unique `client_id` for the same tuple. +- Prevents duplicate scoped triggers for the same `client_id`. diff --git a/service/policy/db/migrations/20260306000000_make_obligation_trigger_uniqueness_client_aware.sql b/service/policy/db/migrations/20260306000000_make_obligation_trigger_uniqueness_client_aware.sql new file mode 100644 index 0000000000..0f95bc22e3 --- /dev/null +++ b/service/policy/db/migrations/20260306000000_make_obligation_trigger_uniqueness_client_aware.sql @@ -0,0 +1,30 @@ +-- +goose Up +-- +goose StatementBegin +-- Make trigger uniqueness aware of optional client_id scoping. +ALTER TABLE IF EXISTS obligation_triggers +DROP CONSTRAINT IF EXISTS obligation_triggers_obligation_value_id_action_id_attribute_value_id_key; + +ALTER TABLE IF EXISTS obligation_triggers +DROP CONSTRAINT IF EXISTS obligation_triggers_obligation_value_id_action_id_attribute_key; + +ALTER TABLE IF EXISTS obligation_triggers +DROP CONSTRAINT IF EXISTS obligation_triggers_obligation_value_id_action_id_attribute_val; + +CREATE UNIQUE INDEX IF NOT EXISTS obligation_triggers_unscoped_unique_idx +ON obligation_triggers (obligation_value_id, action_id, attribute_value_id) +WHERE client_id IS NULL; + +CREATE UNIQUE INDEX IF NOT EXISTS obligation_triggers_scoped_unique_idx +ON obligation_triggers (obligation_value_id, action_id, attribute_value_id, client_id) +WHERE client_id IS NOT NULL; +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP INDEX IF EXISTS obligation_triggers_scoped_unique_idx; +DROP INDEX IF EXISTS obligation_triggers_unscoped_unique_idx; + +ALTER TABLE IF EXISTS obligation_triggers +ADD CONSTRAINT obligation_triggers_obligation_value_id_action_id_attribute_key +UNIQUE (obligation_value_id, action_id, attribute_value_id); +-- +goose StatementEnd From e757315cb399f4c461151b9fca25c38b47693c87 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Fri, 6 Mar 2026 16:05:14 -0500 Subject: [PATCH 2/4] update tests --- .../integration/obligation_triggers_test.go | 35 ++++++------------- 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/service/integration/obligation_triggers_test.go b/service/integration/obligation_triggers_test.go index 74efce3448..785b1ec599 100644 --- a/service/integration/obligation_triggers_test.go +++ b/service/integration/obligation_triggers_test.go @@ -173,7 +173,7 @@ func (s *ObligationTriggersSuite) Test_CreateObligationTrigger_WithIDs_Success() } func (s *ObligationTriggersSuite) Test_CreateObligationTrigger_SameTupleDifferentClients_Success() { - firstTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, &obligations.AddObligationTriggerRequest{ + req := &obligations.AddObligationTriggerRequest{ ObligationValue: &common.IdFqnIdentifier{Id: s.obligationValue.GetId()}, AttributeValue: &common.IdFqnIdentifier{Id: s.attributeValue.GetId()}, Action: &common.IdNameIdentifier{Id: s.action.GetId()}, @@ -182,21 +182,15 @@ func (s *ObligationTriggersSuite) Test_CreateObligationTrigger_SameTupleDifferen ClientId: clientID, }, }, - }) + } + + firstTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, req) s.Require().NoError(err) s.triggerIDsToClean = append(s.triggerIDsToClean, firstTrigger.GetId()) s.validateTriggerWithDefaults(firstTrigger, true) - secondTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, &obligations.AddObligationTriggerRequest{ - ObligationValue: &common.IdFqnIdentifier{Id: s.obligationValue.GetId()}, - AttributeValue: &common.IdFqnIdentifier{Id: s.attributeValue.GetId()}, - Action: &common.IdNameIdentifier{Id: s.action.GetId()}, - Context: &policy.RequestContext{ - Pep: &policy.PolicyEnforcementPoint{ - ClientId: secondClientID, - }, - }, - }) + req.Context.Pep.ClientId = secondClientID + secondTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, req) s.Require().NoError(err) s.triggerIDsToClean = append(s.triggerIDsToClean, secondTrigger.GetId()) s.Require().NotEqual(firstTrigger.GetId(), secondTrigger.GetId()) @@ -205,7 +199,7 @@ func (s *ObligationTriggersSuite) Test_CreateObligationTrigger_SameTupleDifferen } func (s *ObligationTriggersSuite) Test_CreateObligationTrigger_SameTupleSameClient_Fails() { - firstTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, &obligations.AddObligationTriggerRequest{ + req := &obligations.AddObligationTriggerRequest{ ObligationValue: &common.IdFqnIdentifier{Id: s.obligationValue.GetId()}, AttributeValue: &common.IdFqnIdentifier{Id: s.attributeValue.GetId()}, Action: &common.IdNameIdentifier{Id: s.action.GetId()}, @@ -214,20 +208,13 @@ func (s *ObligationTriggersSuite) Test_CreateObligationTrigger_SameTupleSameClie ClientId: clientID, }, }, - }) + } + + firstTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, req) s.Require().NoError(err) s.triggerIDsToClean = append(s.triggerIDsToClean, firstTrigger.GetId()) - duplicateTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, &obligations.AddObligationTriggerRequest{ - ObligationValue: &common.IdFqnIdentifier{Id: s.obligationValue.GetId()}, - AttributeValue: &common.IdFqnIdentifier{Id: s.attributeValue.GetId()}, - Action: &common.IdNameIdentifier{Id: s.action.GetId()}, - Context: &policy.RequestContext{ - Pep: &policy.PolicyEnforcementPoint{ - ClientId: clientID, - }, - }, - }) + duplicateTrigger, err := s.db.PolicyClient.CreateObligationTrigger(s.ctx, req) s.Require().Error(err) s.Require().ErrorIs(err, db.ErrUniqueConstraintViolation) s.Nil(duplicateTrigger) From e74f151efabc666ed4aa5a41adac569dd679e374 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Fri, 6 Mar 2026 16:16:12 -0500 Subject: [PATCH 3/4] regen the erd --- service/policy/db/schema_erd.md | 64 ++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/service/policy/db/schema_erd.md b/service/policy/db/schema_erd.md index bb35cbd18e..2029905a1a 100644 --- a/service/policy/db/schema_erd.md +++ b/service/policy/db/schema_erd.md @@ -36,13 +36,13 @@ erDiagram attribute_definitions { boolean active "Active/Inactive state" - boolean allow_traversal + boolean allow_traversal "Whether or not to allow platform to return the definition key when encrypting, if the value specified is missing." timestamp_with_time_zone created_at uuid id PK "Primary key for the table" jsonb metadata "Metadata for the attribute definition (see protos for structure)" character_varying name UK "Name of the attribute (i.e. organization or classification), unique within the namespace" uuid namespace_id FK,UK "Foreign key to the parent namespace of the attribute definition" - attribute_definition_rule rule " Rule for the attribute (see protos for options)" + attribute_definition_rule rule " Rule for the attribute (see protos for options)" timestamp_with_time_zone updated_at ARRAY values_order "Order of value ids for the attribute (important for hierarchy rule)" } @@ -55,11 +55,6 @@ erDiagram uuid value_id FK,UK "Foreign key to the attribute value" } - attribute_namespace_certificates { - uuid certificate_id PK,FK "Foreign key to the certificate" - uuid namespace_id PK,FK "Foreign key to the namespace" - } - attribute_namespace_key_access_grants { uuid key_access_server_id PK,FK "Foreign key to the KAS registration" uuid namespace_id PK,FK "Foreign key to the namespace of the KAS grant" @@ -104,14 +99,6 @@ erDiagram uuid key_access_server_key_id FK } - certificates { - timestamp_with_time_zone created_at "Timestamp when the certificate was created" - uuid id PK "Unique identifier for the certificate" - jsonb metadata "Optional metadata for the certificate" - text pem "PEM format - Base64-encoded DER certificate (not PEM; no headers/footers)" - timestamp_with_time_zone updated_at "Timestamp when the certificate was last updated" - } - goose_db_version { integer id PK boolean is_applied @@ -166,13 +153,13 @@ erDiagram } obligation_triggers { - uuid action_id FK,UK - uuid attribute_value_id FK,UK + uuid action_id FK + uuid attribute_value_id FK text client_id "Holds the client_id associated with this trigger." timestamp_with_time_zone created_at uuid id PK jsonb metadata - uuid obligation_value_id FK,UK + uuid obligation_value_id FK timestamp_with_time_zone updated_at } @@ -280,43 +267,80 @@ erDiagram obligation_triggers }o--|| actions : "action_id" registered_resource_action_attribute_values }o--|| actions : "action_id" subject_mapping_actions }o--|| actions : "action_id" + obligation_triggers }o--|| actions : "action_id" + registered_resource_action_attribute_values }o--|| actions : "action_id" + subject_mapping_actions }o--|| actions : "action_id" + asym_key }o--|| provider_config : "provider_config_id" asym_key }o--|| provider_config : "provider_config_id" attribute_definition_key_access_grants }o--|| attribute_definitions : "attribute_definition_id" attribute_definition_key_access_grants }o--|| key_access_servers : "key_access_server_id" + attribute_definition_key_access_grants }o--|| attribute_definitions : "attribute_definition_id" + attribute_definition_key_access_grants }o--|| key_access_servers : "key_access_server_id" attribute_definition_public_key_map }o--|| attribute_definitions : "definition_id" attribute_definition_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" + attribute_definition_public_key_map }o--|| attribute_definitions : "definition_id" + attribute_definition_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" + attribute_definitions }o--|| attribute_namespaces : "namespace_id" + attribute_fqns }o--|| attribute_definitions : "attribute_id" + attribute_values }o--|| attribute_definitions : "attribute_definition_id" attribute_definitions }o--|| attribute_namespaces : "namespace_id" attribute_fqns }o--|| attribute_definitions : "attribute_id" attribute_values }o--|| attribute_definitions : "attribute_definition_id" attribute_fqns }o--|| attribute_namespaces : "namespace_id" attribute_fqns }o--|| attribute_values : "value_id" - attribute_namespace_certificates }o--|| attribute_namespaces : "namespace_id" - attribute_namespace_certificates }o--|| certificates : "certificate_id" + attribute_fqns }o--|| attribute_namespaces : "namespace_id" + attribute_fqns }o--|| attribute_values : "value_id" + attribute_namespace_key_access_grants }o--|| attribute_namespaces : "namespace_id" + attribute_namespace_key_access_grants }o--|| key_access_servers : "key_access_server_id" attribute_namespace_key_access_grants }o--|| attribute_namespaces : "namespace_id" attribute_namespace_key_access_grants }o--|| key_access_servers : "key_access_server_id" attribute_namespace_public_key_map }o--|| attribute_namespaces : "namespace_id" attribute_namespace_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" + attribute_namespace_public_key_map }o--|| attribute_namespaces : "namespace_id" + attribute_namespace_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" obligation_definitions }o--|| attribute_namespaces : "namespace_id" resource_mapping_groups }o--|| attribute_namespaces : "namespace_id" + obligation_definitions }o--|| attribute_namespaces : "namespace_id" + resource_mapping_groups }o--|| attribute_namespaces : "namespace_id" + attribute_value_key_access_grants }o--|| attribute_values : "attribute_value_id" + attribute_value_key_access_grants }o--|| key_access_servers : "key_access_server_id" attribute_value_key_access_grants }o--|| attribute_values : "attribute_value_id" attribute_value_key_access_grants }o--|| key_access_servers : "key_access_server_id" attribute_value_public_key_map }o--|| attribute_values : "value_id" attribute_value_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" + attribute_value_public_key_map }o--|| attribute_values : "value_id" + attribute_value_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" obligation_triggers }o--|| attribute_values : "attribute_value_id" registered_resource_action_attribute_values }o--|| attribute_values : "attribute_value_id" resource_mappings }o--|| attribute_values : "attribute_value_id" subject_mappings }o--|| attribute_values : "attribute_value_id" + obligation_triggers }o--|| attribute_values : "attribute_value_id" + registered_resource_action_attribute_values }o--|| attribute_values : "attribute_value_id" + resource_mappings }o--|| attribute_values : "attribute_value_id" + subject_mappings }o--|| attribute_values : "attribute_value_id" + base_keys }o--|| key_access_server_keys : "key_access_server_key_id" base_keys }o--|| key_access_server_keys : "key_access_server_key_id" key_access_server_keys }o--|| key_access_servers : "key_access_server_id" key_access_server_keys }o--|| provider_config : "provider_config_id" + key_access_server_keys }o--|| key_access_servers : "key_access_server_id" + key_access_server_keys }o--|| provider_config : "provider_config_id" + obligation_values_standard }o--|| obligation_definitions : "obligation_definition_id" obligation_values_standard }o--|| obligation_definitions : "obligation_definition_id" obligation_fulfillers }o--|| obligation_values_standard : "obligation_value_id" + obligation_fulfillers }o--|| obligation_values_standard : "obligation_value_id" + obligation_triggers }o--|| obligation_values_standard : "obligation_value_id" obligation_triggers }o--|| obligation_values_standard : "obligation_value_id" sym_key }o--|| provider_config : "provider_config_id" + sym_key }o--|| provider_config : "provider_config_id" registered_resource_action_attribute_values }o--|| registered_resource_values : "registered_resource_value_id" + registered_resource_action_attribute_values }o--|| registered_resource_values : "registered_resource_value_id" + registered_resource_values }o--|| registered_resources : "registered_resource_id" registered_resource_values }o--|| registered_resources : "registered_resource_id" resource_mappings }o--|| resource_mapping_groups : "group_id" + resource_mappings }o--|| resource_mapping_groups : "group_id" subject_mappings }o--|| subject_condition_set : "subject_condition_set_id" + subject_mappings }o--|| subject_condition_set : "subject_condition_set_id" + subject_mapping_actions }o--|| subject_mappings : "subject_mapping_id" subject_mapping_actions }o--|| subject_mappings : "subject_mapping_id" ``` From 8e4a2b0887814fb4f633e64bde395a0da0c7eb75 Mon Sep 17 00:00:00 2001 From: Elizabeth Healy Date: Fri, 6 Mar 2026 16:54:17 -0500 Subject: [PATCH 4/4] regen again on clean db --- service/policy/db/schema_erd.md | 41 +-------------------------------- 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/service/policy/db/schema_erd.md b/service/policy/db/schema_erd.md index 2029905a1a..78b82f73f9 100644 --- a/service/policy/db/schema_erd.md +++ b/service/policy/db/schema_erd.md @@ -42,7 +42,7 @@ erDiagram jsonb metadata "Metadata for the attribute definition (see protos for structure)" character_varying name UK "Name of the attribute (i.e. organization or classification), unique within the namespace" uuid namespace_id FK,UK "Foreign key to the parent namespace of the attribute definition" - attribute_definition_rule rule " Rule for the attribute (see protos for options)" + attribute_definition_rule rule " Rule for the attribute (see protos for options)" timestamp_with_time_zone updated_at ARRAY values_order "Order of value ids for the attribute (important for hierarchy rule)" } @@ -264,83 +264,44 @@ erDiagram timestamp_with_time_zone updated_at "Timestamp when the key was last updated" } - obligation_triggers }o--|| actions : "action_id" - registered_resource_action_attribute_values }o--|| actions : "action_id" - subject_mapping_actions }o--|| actions : "action_id" obligation_triggers }o--|| actions : "action_id" registered_resource_action_attribute_values }o--|| actions : "action_id" subject_mapping_actions }o--|| actions : "action_id" asym_key }o--|| provider_config : "provider_config_id" - asym_key }o--|| provider_config : "provider_config_id" - attribute_definition_key_access_grants }o--|| attribute_definitions : "attribute_definition_id" - attribute_definition_key_access_grants }o--|| key_access_servers : "key_access_server_id" attribute_definition_key_access_grants }o--|| attribute_definitions : "attribute_definition_id" attribute_definition_key_access_grants }o--|| key_access_servers : "key_access_server_id" attribute_definition_public_key_map }o--|| attribute_definitions : "definition_id" attribute_definition_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" - attribute_definition_public_key_map }o--|| attribute_definitions : "definition_id" - attribute_definition_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" - attribute_definitions }o--|| attribute_namespaces : "namespace_id" - attribute_fqns }o--|| attribute_definitions : "attribute_id" - attribute_values }o--|| attribute_definitions : "attribute_definition_id" attribute_definitions }o--|| attribute_namespaces : "namespace_id" attribute_fqns }o--|| attribute_definitions : "attribute_id" attribute_values }o--|| attribute_definitions : "attribute_definition_id" attribute_fqns }o--|| attribute_namespaces : "namespace_id" attribute_fqns }o--|| attribute_values : "value_id" - attribute_fqns }o--|| attribute_namespaces : "namespace_id" - attribute_fqns }o--|| attribute_values : "value_id" - attribute_namespace_key_access_grants }o--|| attribute_namespaces : "namespace_id" - attribute_namespace_key_access_grants }o--|| key_access_servers : "key_access_server_id" attribute_namespace_key_access_grants }o--|| attribute_namespaces : "namespace_id" attribute_namespace_key_access_grants }o--|| key_access_servers : "key_access_server_id" attribute_namespace_public_key_map }o--|| attribute_namespaces : "namespace_id" attribute_namespace_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" - attribute_namespace_public_key_map }o--|| attribute_namespaces : "namespace_id" - attribute_namespace_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" - obligation_definitions }o--|| attribute_namespaces : "namespace_id" - resource_mapping_groups }o--|| attribute_namespaces : "namespace_id" obligation_definitions }o--|| attribute_namespaces : "namespace_id" resource_mapping_groups }o--|| attribute_namespaces : "namespace_id" attribute_value_key_access_grants }o--|| attribute_values : "attribute_value_id" attribute_value_key_access_grants }o--|| key_access_servers : "key_access_server_id" - attribute_value_key_access_grants }o--|| attribute_values : "attribute_value_id" - attribute_value_key_access_grants }o--|| key_access_servers : "key_access_server_id" - attribute_value_public_key_map }o--|| attribute_values : "value_id" - attribute_value_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" attribute_value_public_key_map }o--|| attribute_values : "value_id" attribute_value_public_key_map }o--|| key_access_server_keys : "key_access_server_key_id" obligation_triggers }o--|| attribute_values : "attribute_value_id" registered_resource_action_attribute_values }o--|| attribute_values : "attribute_value_id" resource_mappings }o--|| attribute_values : "attribute_value_id" subject_mappings }o--|| attribute_values : "attribute_value_id" - obligation_triggers }o--|| attribute_values : "attribute_value_id" - registered_resource_action_attribute_values }o--|| attribute_values : "attribute_value_id" - resource_mappings }o--|| attribute_values : "attribute_value_id" - subject_mappings }o--|| attribute_values : "attribute_value_id" - base_keys }o--|| key_access_server_keys : "key_access_server_key_id" base_keys }o--|| key_access_server_keys : "key_access_server_key_id" key_access_server_keys }o--|| key_access_servers : "key_access_server_id" key_access_server_keys }o--|| provider_config : "provider_config_id" - key_access_server_keys }o--|| key_access_servers : "key_access_server_id" - key_access_server_keys }o--|| provider_config : "provider_config_id" obligation_values_standard }o--|| obligation_definitions : "obligation_definition_id" - obligation_values_standard }o--|| obligation_definitions : "obligation_definition_id" - obligation_fulfillers }o--|| obligation_values_standard : "obligation_value_id" obligation_fulfillers }o--|| obligation_values_standard : "obligation_value_id" obligation_triggers }o--|| obligation_values_standard : "obligation_value_id" - obligation_triggers }o--|| obligation_values_standard : "obligation_value_id" sym_key }o--|| provider_config : "provider_config_id" - sym_key }o--|| provider_config : "provider_config_id" - registered_resource_action_attribute_values }o--|| registered_resource_values : "registered_resource_value_id" registered_resource_action_attribute_values }o--|| registered_resource_values : "registered_resource_value_id" registered_resource_values }o--|| registered_resources : "registered_resource_id" - registered_resource_values }o--|| registered_resources : "registered_resource_id" - resource_mappings }o--|| resource_mapping_groups : "group_id" resource_mappings }o--|| resource_mapping_groups : "group_id" subject_mappings }o--|| subject_condition_set : "subject_condition_set_id" - subject_mappings }o--|| subject_condition_set : "subject_condition_set_id" - subject_mapping_actions }o--|| subject_mappings : "subject_mapping_id" subject_mapping_actions }o--|| subject_mappings : "subject_mapping_id" ```