-
Notifications
You must be signed in to change notification settings - Fork 37
chore(ers): expand multi-strategy ERS BDD coverage #3791
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| @claims-ldap-fallback-ers @stateless | ||
| Feature: Claims-to-LDAP fallback via condition-based strategy routing | ||
| Validate that condition-based strategy selection correctly routes entity | ||
| resolution: a claims strategy with condition "department exists" is skipped | ||
| when the entity lacks a department claim, and the LDAP strategy with | ||
| condition "userName exists" matches and provides the department from LDAP. | ||
|
|
||
| This demonstrates the fallback pattern where claims serve as a fast path | ||
| (when claims already contain the needed attributes) and LDAP acts as the | ||
| fallback (when claims are incomplete). | ||
|
|
||
| Background: | ||
| Given an LDAP directory with test users | ||
| And an ERS configuration with mode "multi-strategy" and failure strategy "continue" | ||
| And an ERS provider "jwt_claims" of type "claims" | ||
| And an ERS provider "ldap_directory" of type "ldap" connected to the LDAP directory | ||
| And an ERS mapping strategy "claims_with_department" using provider "jwt_claims" | ||
| """ | ||
| entity_type: subject | ||
| conditions: | ||
| jwt_claims: | ||
| - claim: department | ||
| operator: exists | ||
| output_mapping: | ||
| - source_claim: department | ||
| claim_name: department | ||
| - source_claim: userName | ||
| claim_name: username | ||
| """ | ||
| And an ERS mapping strategy "ldap_department_lookup" using provider "ldap_directory" | ||
| """ | ||
| entity_type: subject | ||
| conditions: | ||
| jwt_claims: | ||
| - claim: userName | ||
| operator: exists | ||
| ldap_search: | ||
| base_dn: "ou=users,dc=opentdf,dc=test" | ||
| filter: "(&(objectClass=inetOrgPerson)(uid={username}))" | ||
| scope: subtree | ||
| attributes: ["uid", "mail", "departmentNumber"] | ||
| input_mapping: | ||
| - jwt_claim: userName | ||
| parameter: username | ||
| output_mapping: | ||
| - source_attribute: departmentNumber | ||
| claim_name: department | ||
| - source_attribute: mail | ||
| claim_name: email | ||
| - source_attribute: uid | ||
| claim_name: username | ||
| """ | ||
| And a local platform with inline ERS configuration | ||
|
|
||
| Scenario: Entity without department claim falls back to LDAP — engineering user gets PERMIT | ||
| Given I submit a request to create a namespace with name "fallback.test" and reference id "ns_fb" | ||
| And I send a request to create an attribute with: | ||
| | namespace_id | name | rule | values | | ||
| | ns_fb | department | anyOf | engineering,marketing,security | | ||
| Then the response should be successful | ||
| Given a condition group referenced as "cg_fb" with an "or" operator with conditions: | ||
| | selector_value | operator | values | | ||
| | .department | in | engineering | | ||
| And a subject set referenced as "ss_fb" containing the condition groups "cg_fb" | ||
| And I send a request to create a subject condition set referenced as "scs_fb" containing subject sets "ss_fb" | ||
| And I send a request to create a subject mapping with: | ||
| | reference_id | attribute_value | condition_set_name | standard actions | custom actions | | ||
| | sm_fb | https://fallback.test/attr/department/value/engineering | scs_fb | read | | | ||
| Then the response should be successful | ||
| Given there is a "user_name" subject entity with value "diana" and referenced as "diana_fb" | ||
| When I send a decision request for entity chain "diana_fb" for "read" action on resource "https://fallback.test/attr/department/value/engineering" | ||
| Then the response should be successful | ||
| And I should get a "PERMIT" decision response | ||
|
|
||
| Scenario: Entity without department claim falls back to LDAP — operations user gets DENY | ||
| Given I submit a request to create a namespace with name "fallback-deny.test" and reference id "ns_fb_deny" | ||
| And I send a request to create an attribute with: | ||
| | namespace_id | name | rule | values | | ||
| | ns_fb_deny | department | anyOf | engineering,marketing,security | | ||
| Then the response should be successful | ||
| Given a condition group referenced as "cg_fb_deny" with an "or" operator with conditions: | ||
| | selector_value | operator | values | | ||
| | .department | in | engineering | | ||
| And a subject set referenced as "ss_fb_deny" containing the condition groups "cg_fb_deny" | ||
| And I send a request to create a subject condition set referenced as "scs_fb_deny" containing subject sets "ss_fb_deny" | ||
| And I send a request to create a subject mapping with: | ||
| | reference_id | attribute_value | condition_set_name | standard actions | custom actions | | ||
| | sm_fb_deny | https://fallback-deny.test/attr/department/value/engineering | scs_fb_deny | read | | | ||
| Then the response should be successful | ||
| Given there is a "user_name" subject entity with value "eve" and referenced as "eve_fb" | ||
| When I send a decision request for entity chain "eve_fb" for "read" action on resource "https://fallback-deny.test/attr/department/value/engineering" | ||
| Then the response should be successful | ||
| And I should get a "DENY" decision response |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| @claims-only-ers @stateless | ||
| Feature: Claims-only ERS resolution (no LDAP) | ||
| Validate that Entity_Claims entities carrying claims inline can be resolved | ||
| by a claims-only multi-strategy ERS without needing LDAP. | ||
|
|
||
| NOTE: These scenarios currently FAIL due to a known bug (#3790) where | ||
| ResolveEntities does not populate JWTClaimsContextKey in context, causing | ||
| the claims provider to always error with "JWT claims not found in context". | ||
| Once the bug is fixed, these scenarios should pass. | ||
|
|
||
| Background: | ||
| And an ERS configuration with mode "multi-strategy" and failure strategy "continue" | ||
| And an ERS provider "jwt_claims" of type "claims" | ||
| And an ERS mapping strategy "claims_department" using provider "jwt_claims" | ||
| """ | ||
| entity_type: subject | ||
| conditions: | ||
| jwt_claims: | ||
| - claim: department | ||
| operator: exists | ||
| output_mapping: | ||
| - source_claim: department | ||
| claim_name: department | ||
| - source_claim: userName | ||
| claim_name: username | ||
| """ | ||
| And a local platform with inline ERS configuration | ||
|
|
||
| Scenario: Claims entity with engineering department gets PERMIT | ||
| Given I submit a request to create a namespace with name "claims-only.test" and reference id "ns_claims" | ||
| And I send a request to create an attribute with: | ||
| | namespace_id | name | rule | values | | ||
| | ns_claims | department | anyOf | engineering,marketing,security | | ||
| Then the response should be successful | ||
| Given a condition group referenced as "cg_claims" with an "or" operator with conditions: | ||
| | selector_value | operator | values | | ||
| | .department | in | engineering | | ||
| And a subject set referenced as "ss_claims" containing the condition groups "cg_claims" | ||
| And I send a request to create a subject condition set referenced as "scs_claims" containing subject sets "ss_claims" | ||
| And I send a request to create a subject mapping with: | ||
| | reference_id | attribute_value | condition_set_name | standard actions | custom actions | | ||
| | sm_claims | https://claims-only.test/attr/department/value/engineering | scs_claims | read | | | ||
| Then the response should be successful | ||
| Given there is a "claims" subject entity referenced as "diana_claims" with claims: | ||
| """ | ||
| {"@type":"type.googleapis.com/google.protobuf.Struct","value":{"userName":"diana","department":"engineering"}} | ||
| """ | ||
| When I send a decision request for entity chain "diana_claims" for "read" action on resource "https://claims-only.test/attr/department/value/engineering" | ||
| Then the response should be successful | ||
| And I should get a "PERMIT" decision response | ||
|
|
||
| Scenario: Claims entity with marketing department gets DENY for engineering resource | ||
| Given I submit a request to create a namespace with name "claims-deny.test" and reference id "ns_claims_deny" | ||
| And I send a request to create an attribute with: | ||
| | namespace_id | name | rule | values | | ||
| | ns_claims_deny | department | anyOf | engineering,marketing,security | | ||
| Then the response should be successful | ||
| Given a condition group referenced as "cg_claims_deny" with an "or" operator with conditions: | ||
| | selector_value | operator | values | | ||
| | .department | in | engineering | | ||
| And a subject set referenced as "ss_claims_deny" containing the condition groups "cg_claims_deny" | ||
| And I send a request to create a subject condition set referenced as "scs_claims_deny" containing subject sets "ss_claims_deny" | ||
| And I send a request to create a subject mapping with: | ||
| | reference_id | attribute_value | condition_set_name | standard actions | custom actions | | ||
| | sm_claims_deny | https://claims-deny.test/attr/department/value/engineering | scs_claims_deny | read | | | ||
| Then the response should be successful | ||
| Given there is a "claims" subject entity referenced as "bob_claims" with claims: | ||
| """ | ||
| {"@type":"type.googleapis.com/google.protobuf.Struct","value":{"userName":"bob","department":"marketing"}} | ||
| """ | ||
| When I send a decision request for entity chain "bob_claims" for "read" action on resource "https://claims-deny.test/attr/department/value/engineering" | ||
| Then the response should be successful | ||
| And I should get a "DENY" decision response | ||
|
Comment on lines
+52
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Make the marketing case prove claims resolution.
🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| @multi-strategy-ers-failfast @stateless | ||
| Feature: Multi-strategy ERS fail-fast behavior | ||
| Validate that failure_strategy "fail-fast" stops entity resolution at the first | ||
| strategy error, preventing fallback to subsequent strategies. This contrasts | ||
| with the "continue" behavior tested in multi-strategy-ers.feature where the | ||
| same user (alice) gets PERMIT because LDAP succeeds after claims fails. | ||
|
|
||
| The claims_passthrough strategy always fails in the ResolveEntities path | ||
| because JWTClaimsContextKey is not populated in context (known bug). Under | ||
| "fail-fast", this aborts resolution immediately — LDAP never executes — so | ||
| the entity has no .department claim and no subject mapping matches → DENY. | ||
|
|
||
| Background: | ||
| Given an LDAP directory with test users | ||
| And an ERS configuration with mode "multi-strategy" and failure strategy "fail-fast" | ||
| And an ERS provider "jwt_claims" of type "claims" | ||
| And an ERS provider "ldap_directory" of type "ldap" connected to the LDAP directory | ||
| And an ERS mapping strategy "claims_passthrough" using provider "jwt_claims" | ||
| """ | ||
| entity_type: subject | ||
| conditions: | ||
| jwt_claims: | ||
| - claim: userName | ||
| operator: exists | ||
| output_mapping: | ||
| - source_claim: userName | ||
| claim_name: username | ||
| """ | ||
| And an ERS mapping strategy "ldap_by_username" using provider "ldap_directory" | ||
| """ | ||
| entity_type: subject | ||
| conditions: | ||
| jwt_claims: | ||
| - claim: userName | ||
| operator: exists | ||
| ldap_search: | ||
| base_dn: "ou=users,dc=opentdf,dc=test" | ||
| filter: "(&(objectClass=inetOrgPerson)(uid={username}))" | ||
| scope: subtree | ||
| attributes: ["uid", "mail", "departmentNumber"] | ||
| input_mapping: | ||
| - jwt_claim: userName | ||
| parameter: username | ||
| output_mapping: | ||
| - source_attribute: departmentNumber | ||
| claim_name: department | ||
| - source_attribute: mail | ||
| claim_name: email | ||
| - source_attribute: uid | ||
| claim_name: username | ||
| """ | ||
| And a local platform with inline ERS configuration | ||
|
|
||
| Scenario: Fail-fast prevents LDAP fallback — engineering user gets DENY | ||
| Given I submit a request to create a namespace with name "eng-failfast.test" and reference id "ns_eng_ff" | ||
| And I send a request to create an attribute with: | ||
| | namespace_id | name | rule | values | | ||
| | ns_eng_ff | department | anyOf | engineering,marketing,security | | ||
| Then the response should be successful | ||
| Given a condition group referenced as "cg_eng_ff" with an "or" operator with conditions: | ||
| | selector_value | operator | values | | ||
| | .department | in | engineering | | ||
| And a subject set referenced as "ss_eng_ff" containing the condition groups "cg_eng_ff" | ||
| And I send a request to create a subject condition set referenced as "scs_eng_ff" containing subject sets "ss_eng_ff" | ||
| And I send a request to create a subject mapping with: | ||
| | reference_id | attribute_value | condition_set_name | standard actions | custom actions | | ||
| | sm_eng_ff | https://eng-failfast.test/attr/department/value/engineering | scs_eng_ff | read | | | ||
| Then the response should be successful | ||
| Given there is a "user_name" subject entity with value "alice" and referenced as "alice_ff" | ||
| When I send a decision request for entity chain "alice_ff" for "read" action on resource "https://eng-failfast.test/attr/department/value/engineering" | ||
| Then the response should be successful | ||
| And I should get a "DENY" decision response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: opentdf/platform
Length of output: 32525
🏁 Script executed:
Repository: opentdf/platform
Length of output: 6629
Quarantine the known-failing feature from normal BDD runs.
tests-bdd/platform_test.goruns Godog with the default options and noOptions.Tagsfilter, so@claims-only-ersis included in normal BDD invocations despite the in-file note that the scenarios currently fail. Add an executable pending/skip control and exclude it until#3790is fixed.🤖 Prompt for AI Agents