Skip to content

Commit 874ec7b

Browse files
authored
feat(authz): audit logs should properly handle obligations (#2824)
### Proposed Changes * audit logs report on: * overall fulfillable obligations * required obligations at the resource decision level * whether or not obligations were satisfied * adds unit test that entitlements related to the decision come back from pdp call to populate audit log * update audit logs to clarify decision audit logs only contain entitlements relevant to decisioning (not all entitlements) Single resource success log: ```json { "time": "2025-10-21T19:42:59.465451-07:00", "level": "AUDIT", "msg": "decision", "namespace": "authorization", "version": "v2", "audit": { "object": { "type": "entity_object", "id": "jwtentity-1-clientid-opentdf-read", "name": "decisionRequest-read", "attributes": { "assertions": null, "attrs": null, "permissions": null } }, "action": { "type": "read", "result": "success" }, "actor": { "id": "jwtentity-1-clientid-opentdf", "attributes": [ { "entitlements_relevant_to_decision": { "https://example.com/attr/class/value/topsecret": [ { "id": "a1bb9ff7-74a1-4c30-a5c2-7067a1d84aef", "Value": null, "name": "read" } ] } } ] }, "eventMetaData": { "resource_decisions": [ { "passed": true, "obligations_satisfied": true, "entitled": true, "resource_id": "resource-0", "resource_name": "https://reg_res/testing/value/secret", "data_rule_results": [ { "passed": true, "resource_value_fqns": [ "https://example.com/attr/class/value/secret" ], "attribute": { "id": "70e2949e-8a15-4b4d-ab2a-a1a9bfa415a9", "rule": 3, "fqn": "https://example.com/attr/class" }, "entitlement_failures": null } ], "required_obligation_value_fqns": [ "https://example.com/obl/drm/value/mask" ] } ], "fulfillable_obligation_value_fqns": [ "https://example.com/obl/drm/value/mask" ], "obligations_satisfied": true }, "clientInfo": { "userAgent": "grpc-go/1.61.0", "platform": "authorization.v2", "requestIP": "None" }, "original": null, "updated": null, "requestID": "17a49c85-8a0c-4996-8804-d91d0b77e501", "timestamp": "2025-10-21T19:42:59-07:00" } } ``` single resource failure log: ```json { "time": "2025-10-21T19:42:18.829716-07:00", "level": "AUDIT", "msg": "decision", "namespace": "authorization", "version": "v2", "audit": { "object": { "type": "entity_object", "id": "jwtentity-1-clientid-opentdf-read", "name": "decisionRequest-read", "attributes": { "assertions": null, "attrs": null, "permissions": null } }, "action": { "type": "read", "result": "failure" }, "actor": { "id": "jwtentity-1-clientid-opentdf", "attributes": [ { "entitlements_relevant_to_decision": { "https://example.com/attr/class/value/topsecret": [ { "id": "a1bb9ff7-74a1-4c30-a5c2-7067a1d84aef", "Value": null, "name": "read" } ] } } ] }, "eventMetaData": { "resource_decisions": [ { "passed": false, "obligations_satisfied": false, "entitled": true, "resource_id": "resource-0", "resource_name": "https://reg_res/testing/value/secret", "data_rule_results": [ { "passed": true, "resource_value_fqns": [ "https://example.com/attr/class/value/secret" ], "attribute": { "id": "70e2949e-8a15-4b4d-ab2a-a1a9bfa415a9", "rule": 3, "fqn": "https://example.com/attr/class" }, "entitlement_failures": null } ], "required_obligation_value_fqns": [ "https://example.com/obl/drm/value/mask" ] } ], "fulfillable_obligation_value_fqns": [], "obligations_satisfied": false }, "clientInfo": { "userAgent": "grpc-go/1.61.0", "platform": "authorization.v2", "requestIP": "None" }, "original": null, "updated": null, "requestID": "eb3681fc-96e5-4328-be1b-3b4da9deab19", "timestamp": "2025-10-21T19:42:18-07:00" } } ``` multi-resource success log: ```json { "time": "2025-10-21T19:40:00.00347-07:00", "level": "AUDIT", "msg": "decision", "namespace": "authorization", "version": "v2", "audit": { "object": { "type": "entity_object", "id": "jwtentity-1-clientid-opentdf-read", "name": "decisionRequest-read", "attributes": { "assertions": null, "attrs": null, "permissions": null } }, "action": { "type": "read", "result": "success" }, "actor": { "id": "jwtentity-1-clientid-opentdf", "attributes": [ { "entitlements_relevant_to_decision": { "https://example.com/attr/class/value/topsecret": [ { "id": "a1bb9ff7-74a1-4c30-a5c2-7067a1d84aef", "Value": null, "name": "read" } ] } } ] }, "eventMetaData": { "resource_decisions": [ { "passed": true, "obligations_satisfied": true, "entitled": true, "resource_id": "resource-0", "resource_name": "https://reg_res/testing/value/secret", "data_rule_results": [ { "passed": true, "resource_value_fqns": [ "https://example.com/attr/class/value/secret" ], "attribute": { "id": "70e2949e-8a15-4b4d-ab2a-a1a9bfa415a9", "rule": 3, "fqn": "https://example.com/attr/class" }, "entitlement_failures": null } ], "required_obligation_value_fqns": [ "https://example.com/obl/drm/value/mask" ] }, { "passed": true, "obligations_satisfied": true, "entitled": true, "resource_id": "resource-1", "data_rule_results": [ { "passed": true, "resource_value_fqns": [ "https://example.com/attr/class/value/secret" ], "attribute": { "id": "70e2949e-8a15-4b4d-ab2a-a1a9bfa415a9", "rule": 3, "fqn": "https://example.com/attr/class" }, "entitlement_failures": null } ], "required_obligation_value_fqns": [ "https://example.com/obl/drm/value/mask" ] } ], "fulfillable_obligation_value_fqns": [ "https://example.com/obl/drm/value/mask" ], "obligations_satisfied": true }, "clientInfo": { "userAgent": "grpc-go/1.61.0", "platform": "authorization.v2", "requestIP": "None" }, "original": null, "updated": null, "requestID": "f87d7480-20b5-4e2e-b08a-0e4608e43c9e", "timestamp": "2025-10-21T19:40:00-07:00" } } ``` Multi resource failure log: ```json { "time": "2025-10-21T19:40:06.710929-07:00", "level": "AUDIT", "msg": "decision", "namespace": "authorization", "version": "v2", "audit": { "object": { "type": "entity_object", "id": "jwtentity-1-clientid-opentdf-read", "name": "decisionRequest-read", "attributes": { "assertions": null, "attrs": null, "permissions": null } }, "action": { "type": "read", "result": "failure" }, "actor": { "id": "jwtentity-1-clientid-opentdf", "attributes": [ { "entitlements_relevant_to_decision": { "https://example.com/attr/class/value/topsecret": [ { "id": "a1bb9ff7-74a1-4c30-a5c2-7067a1d84aef", "Value": null, "name": "read" } ] } } ] }, "eventMetaData": { "resource_decisions": [ { "passed": false, "obligations_satisfied": false, "entitled": true, "resource_id": "resource-0", "resource_name": "https://reg_res/testing/value/secret", "data_rule_results": [ { "passed": true, "resource_value_fqns": [ "https://example.com/attr/class/value/secret" ], "attribute": { "id": "70e2949e-8a15-4b4d-ab2a-a1a9bfa415a9", "rule": 3, "fqn": "https://example.com/attr/class" }, "entitlement_failures": null } ], "required_obligation_value_fqns": [ "https://example.com/obl/drm/value/mask" ] }, { "passed": false, "obligations_satisfied": false, "entitled": true, "resource_id": "resource-1", "data_rule_results": [ { "passed": true, "resource_value_fqns": [ "https://example.com/attr/class/value/secret" ], "attribute": { "id": "70e2949e-8a15-4b4d-ab2a-a1a9bfa415a9", "rule": 3, "fqn": "https://example.com/attr/class" }, "entitlement_failures": null } ], "required_obligation_value_fqns": [ "https://example.com/obl/drm/value/mask" ] } ], "fulfillable_obligation_value_fqns": [], "obligations_satisfied": false }, "clientInfo": { "userAgent": "grpc-go/1.61.0", "platform": "authorization.v2", "requestIP": "None" }, "original": null, "updated": null, "requestID": "6561c969-f169-4e16-84a6-f378f481b28d", "timestamp": "2025-10-21T19:40:06-07:00" } } ```
1 parent 412dfd1 commit 874ec7b

File tree

14 files changed

+755
-382
lines changed

14 files changed

+755
-382
lines changed

service/authorization/v2/authorization_test.go

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,7 +1374,7 @@ func Test_RollupSingleResourceDecision(t *testing.T) {
13741374
permitted: true,
13751375
decisions: []*access.Decision{
13761376
{
1377-
Access: true,
1377+
AllPermitted: true,
13781378
Results: []access.ResourceDecision{
13791379
{
13801380
ResourceID: "resource-123",
@@ -1395,7 +1395,7 @@ func Test_RollupSingleResourceDecision(t *testing.T) {
13951395
permitted: true,
13961396
decisions: []*access.Decision{
13971397
{
1398-
Access: true,
1398+
AllPermitted: true,
13991399
Results: []access.ResourceDecision{
14001400
{
14011401
ResourceID: "resource-123",
@@ -1422,7 +1422,7 @@ func Test_RollupSingleResourceDecision(t *testing.T) {
14221422
permitted: false,
14231423
decisions: []*access.Decision{
14241424
{
1425-
Access: true, // Verify permitted takes precedence
1425+
AllPermitted: true, // Verify permitted takes precedence
14261426
Results: []access.ResourceDecision{
14271427
{
14281428
ResourceID: "resource-123",
@@ -1443,7 +1443,7 @@ func Test_RollupSingleResourceDecision(t *testing.T) {
14431443
permitted: false,
14441444
decisions: []*access.Decision{
14451445
{
1446-
Access: true, // Verify permitted takes precedence
1446+
AllPermitted: true, // Verify permitted takes precedence
14471447
Results: []access.ResourceDecision{
14481448
{
14491449
ResourceID: "resource-123",
@@ -1473,8 +1473,8 @@ func Test_RollupSingleResourceDecision(t *testing.T) {
14731473
permitted: true,
14741474
decisions: []*access.Decision{
14751475
{
1476-
Access: true,
1477-
Results: []access.ResourceDecision{},
1476+
AllPermitted: true,
1477+
Results: []access.ResourceDecision{},
14781478
},
14791479
},
14801480
expectedResult: nil,
@@ -1509,7 +1509,7 @@ func Test_RollupMultiResourceDecisions(t *testing.T) {
15091509
name: "should return multiple permit decisions",
15101510
decisions: []*access.Decision{
15111511
{
1512-
Access: true,
1512+
AllPermitted: true,
15131513
Results: []access.ResourceDecision{
15141514
{
15151515
Passed: true,
@@ -1518,7 +1518,7 @@ func Test_RollupMultiResourceDecisions(t *testing.T) {
15181518
},
15191519
},
15201520
{
1521-
Access: true,
1521+
AllPermitted: true,
15221522
Results: []access.ResourceDecision{
15231523
{
15241524
Passed: true,
@@ -1542,7 +1542,7 @@ func Test_RollupMultiResourceDecisions(t *testing.T) {
15421542
name: "should return mix of permit and deny decisions",
15431543
decisions: []*access.Decision{
15441544
{
1545-
Access: true,
1545+
AllPermitted: true,
15461546
Results: []access.ResourceDecision{
15471547
{
15481548
Passed: true,
@@ -1551,7 +1551,7 @@ func Test_RollupMultiResourceDecisions(t *testing.T) {
15511551
},
15521552
},
15531553
{
1554-
Access: false,
1554+
AllPermitted: false,
15551555
Results: []access.ResourceDecision{
15561556
{
15571557
Passed: false,
@@ -1575,7 +1575,7 @@ func Test_RollupMultiResourceDecisions(t *testing.T) {
15751575
name: "should rely on results and default to false decisions",
15761576
decisions: []*access.Decision{
15771577
{
1578-
Access: true,
1578+
AllPermitted: true,
15791579
Results: []access.ResourceDecision{
15801580
{
15811581
Passed: true,
@@ -1588,7 +1588,7 @@ func Test_RollupMultiResourceDecisions(t *testing.T) {
15881588
},
15891589
},
15901590
{
1591-
Access: false,
1591+
AllPermitted: false,
15921592
Results: []access.ResourceDecision{
15931593
{
15941594
Passed: false,
@@ -1616,7 +1616,7 @@ func Test_RollupMultiResourceDecisions(t *testing.T) {
16161616
name: "should ignore global access and care about resource decisions predominantly",
16171617
decisions: []*access.Decision{
16181618
{
1619-
Access: false,
1619+
AllPermitted: false,
16201620
Results: []access.ResourceDecision{
16211621
{
16221622
Passed: false,
@@ -1629,7 +1629,7 @@ func Test_RollupMultiResourceDecisions(t *testing.T) {
16291629
},
16301630
},
16311631
{
1632-
Access: false,
1632+
AllPermitted: false,
16331633
Results: []access.ResourceDecision{
16341634
{
16351635
Passed: true,
@@ -1657,7 +1657,7 @@ func Test_RollupMultiResourceDecisions(t *testing.T) {
16571657
name: "should return obligations whenever found on a resource",
16581658
decisions: []*access.Decision{
16591659
{
1660-
Access: true,
1660+
AllPermitted: true,
16611661
Results: []access.ResourceDecision{
16621662
{
16631663
Passed: true,
@@ -1678,7 +1678,7 @@ func Test_RollupMultiResourceDecisions(t *testing.T) {
16781678
},
16791679
},
16801680
{
1681-
Access: false,
1681+
AllPermitted: false,
16821682
Results: []access.ResourceDecision{
16831683
{
16841684
Passed: false,
@@ -1728,8 +1728,8 @@ func Test_RollupMultiResourceDecisions(t *testing.T) {
17281728
name: "should return error when decision has no results",
17291729
decisions: []*access.Decision{
17301730
{
1731-
Access: true,
1732-
Results: []access.ResourceDecision{},
1731+
AllPermitted: true,
1732+
Results: []access.ResourceDecision{},
17331733
},
17341734
},
17351735
expectedError: ErrDecisionMustHaveResults,
@@ -1794,8 +1794,8 @@ func Test_RollupMultiResourceDecisions_WithNilChecks(t *testing.T) {
17941794
t.Run("nil Results field", func(t *testing.T) {
17951795
decisions := []*access.Decision{
17961796
{
1797-
Access: true,
1798-
Results: nil,
1797+
AllPermitted: true,
1798+
Results: nil,
17991799
},
18001800
}
18011801
_, err := rollupMultiResourceDecisions(decisions)
@@ -1822,8 +1822,8 @@ func Test_RollupSingleResourceDecision_WithNilChecks(t *testing.T) {
18221822
t.Run("nil Results field", func(t *testing.T) {
18231823
decisions := []*access.Decision{
18241824
{
1825-
Access: true,
1826-
Results: nil,
1825+
AllPermitted: true,
1826+
Results: nil,
18271827
},
18281828
}
18291829
_, err := rollupSingleResourceDecision(true, decisions)

service/internal/access/v2/evaluate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func getResourceDecision(
8585
// indicates a failure before attribute definition rule evaluation
8686
if len(resourceAttributeValues.GetFqns()) == 0 {
8787
failure := &ResourceDecision{
88-
Passed: false,
88+
Entitled: false,
8989
ResourceID: resourceID,
9090
ResourceName: registeredResourceValueFQN,
9191
}
@@ -151,7 +151,7 @@ func evaluateResourceAttributeValues(
151151

152152
// Return results in the appropriate structure
153153
result := &ResourceDecision{
154-
Passed: passed,
154+
Entitled: passed,
155155
ResourceID: resourceID,
156156
DataRuleResults: dataRuleResults,
157157
}

service/internal/access/v2/evaluate_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -785,7 +785,7 @@ func (s *EvaluateTestSuite) TestEvaluateResourceAttributeValues() {
785785
} else {
786786
s.Require().NoError(err)
787787
s.NotNil(resourceDecision)
788-
s.Equal(tc.expectAccessible, resourceDecision.Passed)
788+
s.Equal(tc.expectAccessible, resourceDecision.Entitled)
789789

790790
// Check results array has the correct length based on grouping by definition
791791
definitions := make(map[string]bool)
@@ -937,7 +937,7 @@ func (s *EvaluateTestSuite) TestGetResourceDecision() {
937937
} else {
938938
s.Require().NoError(err)
939939
s.NotNil(decision)
940-
s.Equal(tc.expectPass, decision.Passed, "Decision pass status didn't match")
940+
s.Equal(tc.expectPass, decision.Entitled, "Decision entitlement status didn't match")
941941
s.Equal(tc.resource.GetEphemeralId(), decision.ResourceID, "Resource ID didn't match")
942942
}
943943
})

service/internal/access/v2/just_in_time_pdp.go

Lines changed: 81 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919

2020
"github.com/opentdf/platform/service/internal/access/v2/obligations"
2121
"github.com/opentdf/platform/service/logger"
22+
"github.com/opentdf/platform/service/logger/audit"
2223
)
2324

2425
var (
@@ -141,7 +142,7 @@ func (p *JustInTimePDP) GetDecision(
141142
)
142143

143144
// Because there are three possible types of entities, check obligations first to more easily handle decisioning logic
144-
allTriggeredObligationsCanBeFulfilled, requiredObligationsPerResource, err := p.obligationsPDP.GetAllTriggeredObligationsAreFulfilled(
145+
obligationDecision, err := p.obligationsPDP.GetAllTriggeredObligationsAreFulfilled(
145146
ctx,
146147
resources,
147148
action,
@@ -165,26 +166,22 @@ func (p *JustInTimePDP) GetDecision(
165166
case *authzV2.EntityIdentifier_RegisteredResourceValueFqn:
166167
regResValueFQN := strings.ToLower(entityIdentifier.GetRegisteredResourceValueFqn())
167168
// Registered resources do not have entity representations, so only one decision is made
168-
decision, err := p.pdp.GetDecisionRegisteredResource(ctx, regResValueFQN, action, resources)
169+
decision, entitlements, err := p.pdp.GetDecisionRegisteredResource(ctx, regResValueFQN, action, resources)
169170
if err != nil {
170171
return nil, false, fmt.Errorf("failed to get decision for registered resource value FQN [%s]: %w", regResValueFQN, err)
171172
}
172173
if decision == nil {
173174
return nil, false, fmt.Errorf("decision is nil for registered resource value FQN [%s]", regResValueFQN)
174175
}
175176

176-
// If not entitled, obligations are not considered
177-
if !decision.Access {
178-
return []*Decision{decision}, decision.Access, nil
179-
}
180-
181-
// Access should only be granted if entitled AND obligations fulfilled
182-
decision.Access = allTriggeredObligationsCanBeFulfilled
183-
for idx, required := range requiredObligationsPerResource {
184-
decision.Results[idx].RequiredObligationValueFQNs = required
185-
}
177+
// Update resource decisions with obligations and set final access decision
178+
hasRequiredObligations := len(obligationDecision.RequiredObligationValueFQNs) > 0
179+
entitledWithAnyObligationsSatisfied := decision.AllPermitted && (!hasRequiredObligations || obligationDecision.AllObligationsSatisfied)
180+
decision.AllPermitted = entitledWithAnyObligationsSatisfied
181+
decision = setResourceDecisionsWithObligations(decision, obligationDecision)
186182

187-
return []*Decision{decision}, decision.Access, nil
183+
p.auditDecision(ctx, regResValueFQN, action, decision, entitlements, fulfillableObligationValueFQNs, obligationDecision)
184+
return []*Decision{decision}, decision.AllPermitted, nil
188185

189186
default:
190187
return nil, false, ErrInvalidEntityType
@@ -195,38 +192,69 @@ func (p *JustInTimePDP) GetDecision(
195192

196193
// Make initial entitlement decisions
197194
entityDecisions := make([]*Decision, len(entityRepresentations))
195+
entityEntitlements := make([]map[string][]*policy.Action, len(entityRepresentations))
198196
allPermitted := true
199197
for idx, entityRep := range entityRepresentations {
200-
d, err := p.pdp.GetDecision(ctx, entityRep, action, resources)
198+
d, entitlements, err := p.pdp.GetDecision(ctx, entityRep, action, resources)
201199
if err != nil {
202200
return nil, false, fmt.Errorf("failed to get decision for entityRepresentation with original id [%s]: %w", entityRep.GetOriginalId(), err)
203201
}
204202
if d == nil {
205203
return nil, false, fmt.Errorf("decision is nil: %w", err)
206204
}
207-
if !d.Access {
205+
// If any entity lacks access to any resource, update overall decision denial
206+
if !d.AllPermitted {
208207
allPermitted = false
209208
}
210209
entityDecisions[idx] = d
210+
entityEntitlements[idx] = entitlements
211211
}
212212

213-
// If even one entity was denied access, obligations are not considered or returned
214-
if !allPermitted {
215-
return entityDecisions, allPermitted, nil
216-
}
213+
// Update resource decisions with obligations and set final access decision
214+
hasRequiredObligations := len(obligationDecision.RequiredObligationValueFQNs) > 0
215+
allEntitledWithAnyObligationsSatisfied := allPermitted && (!hasRequiredObligations || obligationDecision.AllObligationsSatisfied)
216+
allPermitted = allEntitledWithAnyObligationsSatisfied
217217

218-
// Access should only be granted if entitled AND obligations fulfilled
219-
allPermitted = allTriggeredObligationsCanBeFulfilled
220-
// Obligations are not entity-specific at this time so will be the same across every entity
221-
for _, decision := range entityDecisions {
222-
for idx, required := range requiredObligationsPerResource {
223-
decision.Results[idx].RequiredObligationValueFQNs = required
224-
}
218+
// Propagate obligations within policy on each resource decision object
219+
for entityIdx, decision := range entityDecisions {
220+
// TODO: figure out this multi-entity response?
221+
// entitledWithAnyObligationsSatisfied := decision.AllPermitted && (!hasRequiredObligations || obligationDecision.AllObligationsSatisfied)
222+
// decision.AllPermitted = entitledWithAnyObligationsSatisfied
223+
decision = setResourceDecisionsWithObligations(decision, obligationDecision)
224+
decision.AllPermitted = allPermitted
225+
entityRepID := entityRepresentations[entityIdx].GetOriginalId()
226+
p.auditDecision(ctx, entityRepID, action, decision, entityEntitlements[entityIdx], fulfillableObligationValueFQNs, obligationDecision)
225227
}
226228

227229
return entityDecisions, allPermitted, nil
228230
}
229231

232+
// setResourceDecisionsWithObligations updates all resource decisions with obligation
233+
// information and sets each resource passed state
234+
func setResourceDecisionsWithObligations(
235+
decision *Decision,
236+
obligationDecision obligations.ObligationPolicyDecision,
237+
) *Decision {
238+
hasRequiredObligations := len(obligationDecision.RequiredObligationValueFQNs) > 0
239+
240+
for idx := range decision.Results {
241+
resourceDecision := &decision.Results[idx]
242+
243+
if hasRequiredObligations {
244+
// Update with specific obligation data from the obligations PDP
245+
perResource := obligationDecision.RequiredObligationValueFQNsPerResource[idx]
246+
resourceDecision.ObligationsSatisfied = perResource.ObligationsSatisfied
247+
resourceDecision.RequiredObligationValueFQNs = perResource.RequiredObligationValueFQNs
248+
} else {
249+
// No required obligations means all obligations are satisfied
250+
resourceDecision.ObligationsSatisfied = true
251+
}
252+
253+
resourceDecision.Passed = resourceDecision.Entitled && resourceDecision.ObligationsSatisfied
254+
}
255+
return decision
256+
}
257+
230258
// GetEntitlements retrieves the entitlements for the provided entity chain.
231259
// It resolves the entity chain to get the entity representations and then calls the embedded PDP to get the entitlements.
232260
func (p *JustInTimePDP) GetEntitlements(
@@ -287,8 +315,6 @@ func (p *JustInTimePDP) GetEntitlements(
287315
func (p *JustInTimePDP) getMatchedSubjectMappings(
288316
ctx context.Context,
289317
entityRepresentations []*entityresolutionV2.EntityRepresentation,
290-
// updated with the results, attrValue FQN to attribute and value with subject mappings
291-
// entitleableAttributes map[string]*attrs.GetAttributeValuesByFqnsResponse_AttributeAndValue,
292318
) ([]*policy.SubjectMapping, error) {
293319
// Break the entity down the entities into their properties/selectors and retrieve only those subject mappings
294320
subjectProperties := make([]*policy.SubjectProperty, 0)
@@ -400,3 +426,30 @@ func (p *JustInTimePDP) resolveEntitiesFromRequestToken(
400426

401427
return p.resolveEntitiesFromToken(ctx, token, skipEnvironmentEntities)
402428
}
429+
430+
// auditDecision logs a GetDecisionV2 audit event with obligation information
431+
func (p *JustInTimePDP) auditDecision(
432+
ctx context.Context,
433+
entityID string,
434+
action *policy.Action,
435+
decision *Decision,
436+
entitlements map[string][]*policy.Action,
437+
fulfillableObligationValueFQNs []string,
438+
obligationDecision obligations.ObligationPolicyDecision,
439+
) {
440+
// Determine audit decision result
441+
auditDecision := audit.GetDecisionResultDeny
442+
if decision.AllPermitted {
443+
auditDecision = audit.GetDecisionResultPermit
444+
}
445+
446+
p.logger.Audit.GetDecisionV2(ctx, audit.GetDecisionV2EventParams{
447+
EntityID: entityID,
448+
ActionName: action.GetName(),
449+
Decision: auditDecision,
450+
Entitlements: entitlements,
451+
FulfillableObligationValueFQNs: fulfillableObligationValueFQNs,
452+
ObligationsSatisfied: obligationDecision.AllObligationsSatisfied,
453+
ResourceDecisions: decision.Results,
454+
})
455+
}

0 commit comments

Comments
 (0)