Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions service/authorization/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,16 @@ func (as *AuthorizationService) getDecisions(ctx context.Context, dr *authorizat
continue
}

// Determine resource attribute ID for audit
resourceAttrID := ra.GetResourceAttributesId()
if resourceAttrID == "" && len(ra.GetAttributeValueFqns()) > 0 {
resourceAttrID = ra.GetAttributeValueFqns()[0]
}

// Start deferred audit event
auditEvent := as.logger.Audit.Decision(ctx, ec.GetId(), resourceAttrID, fqns)
defer auditEvent.Log(ctx)

//
// TODO: we should already have the subject mappings here and be able to just use OPA to trim down the known data attr values to the ones matched up with the entities
//
Expand Down Expand Up @@ -656,6 +666,10 @@ func (as *AuthorizationService) getDecisions(ctx context.Context, dr *authorizat
envEntityAttrValues[entityID] = e.GetAttributeValueFqns()
}
}

// Update audit with entitlements as they are computed
auditEvent.UpdateEntitlements(auditECEntitlements)

// call access-pdp
accessPDP := access.NewPdp(as.logger)
decisions, err := accessPDP.DetermineAccess(
Expand Down Expand Up @@ -689,6 +703,9 @@ func (as *AuthorizationService) getDecisions(ctx context.Context, dr *authorizat
Entitlements: entityEntitlementFqns,
})
}

// Update audit with entity decisions
auditEvent.UpdateEntityDecisions(auditEntityDecisions)
}

decisionResp := &authorization.DecisionResponse{
Expand All @@ -706,18 +723,13 @@ func (as *AuthorizationService) getDecisions(ctx context.Context, dr *authorizat
decisionResp.ResourceAttributesId = ra.GetAttributeValueFqns()[0]
}

// Log audit with final decision
auditDecision := audit.GetDecisionResultDeny
if decision == authorization.DecisionResponse_DECISION_PERMIT {
auditDecision = audit.GetDecisionResultPermit
}
as.logger.Audit.GetDecision(ctx, audit.GetDecisionEventParams{
Decision: auditDecision,
EntityChainEntitlements: auditECEntitlements,
EntityChainID: decisionResp.GetEntityChainId(),
EntityDecisions: auditEntityDecisions,
FQNs: fqns,
ResourceAttributeID: decisionResp.GetResourceAttributesId(),
})
auditEvent.Success(ctx, auditDecision)

response[responseIdx] = decisionResp
}
}
Expand Down
53 changes: 31 additions & 22 deletions service/internal/access/v2/just_in_time_pdp.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ func (p *JustInTimePDP) GetDecision(

case *authzV2.EntityIdentifier_RegisteredResourceValueFqn:
regResValueFQN := strings.ToLower(entityIdentifier.GetRegisteredResourceValueFqn())

auditEvent := p.logger.Audit.DecisionV2(ctx, regResValueFQN, action.GetName())
defer auditEvent.Log(ctx)

// Registered resources do not have entity representations, so only one decision is made
decision, entitlements, err := p.pdp.GetDecisionRegisteredResource(ctx, regResValueFQN, action, resources)
if err != nil {
Expand All @@ -190,10 +194,10 @@ func (p *JustInTimePDP) GetDecision(
decision.AllPermitted = entitledWithAnyObligationsSatisfied
decision.Results = resourceDecisions

p.auditDecision(
// Enrich audit with computed information and mark as successful
p.enrichAuditDecision(
ctx,
regResValueFQN,
action,
auditEvent,
entitledWithAnyObligationsSatisfied,
entitlements,
fulfillableObligationValueFQNs,
Expand All @@ -209,11 +213,20 @@ func (p *JustInTimePDP) GetDecision(
return nil, fmt.Errorf("failed to resolve entity identifier: %w", err)
}

auditEvents := make([]*audit.GetDecisionV2Event, 0, len(entityRepresentations))
for _, entityRep := range entityRepresentations {
auditEvent := p.logger.Audit.DecisionV2(ctx, entityRep.GetOriginalId(), action.GetName())
defer auditEvent.Log(ctx)
auditEvents = append(auditEvents, auditEvent)
}

// Get a decision on each entity representation and consolidate into an overall decision
var resourceDecisionsAcrossAllEntityReps []ResourceDecision
allPermitted := true

for _, entityRep := range entityRepresentations {
for i, entityRep := range entityRepresentations {
auditEvent := auditEvents[i]

entityRepresentationDecision, entitlements, err := p.pdp.GetDecision(ctx, entityRep, action, resources)
if err != nil {
return nil, fmt.Errorf("failed to get decision for entityRepresentation with original id [%s]: %w", entityRep.GetOriginalId(), err)
Expand All @@ -239,12 +252,11 @@ func (p *JustInTimePDP) GetDecision(
return nil, fmt.Errorf("failed to apply obligations and consolidate for entity representation [%s]: %w", entityRep.GetOriginalId(), err)
}

// Audit decision for this entity representation
// Enrich audit for this entity representation with computed information and mark as successful
entityAllPermitted := entityRepresentationDecision.AllPermitted && allObligationsSatisfied
p.auditDecision(
p.enrichAuditDecision(
ctx,
entityRep.GetOriginalId(),
action,
auditEvent,
entityAllPermitted,
entitlements,
fulfillableObligationValueFQNs,
Expand Down Expand Up @@ -431,32 +443,29 @@ func (p *JustInTimePDP) resolveEntitiesFromRequestToken(
return p.resolveEntitiesFromToken(ctx, token, skipEnvironmentEntities)
}

// auditDecision logs a GetDecisionV2 audit event with obligation information.
// enrichAuditDecision enriches a deferred audit event with computed information and marks it as successful.
// The auditResourceDecisions parameter should contain the full obligation context including
// for non-entitled resources, which is intentionally excluded from the actual response.
func (p *JustInTimePDP) auditDecision(
func (p *JustInTimePDP) enrichAuditDecision(
ctx context.Context,
entityID string,
action *policy.Action,
auditEvent *audit.GetDecisionV2Event,
allPermitted bool,
entitlements map[string][]*policy.Action,
fulfillableObligationValueFQNs []string,
obligationDecision obligations.ObligationPolicyDecision,
auditResourceDecisions []ResourceDecision,
) {
// Determine audit decision result
// Update audit with computed information
auditEvent.UpdateEntitlements(entitlements)
auditEvent.UpdateResourceDecisions(auditResourceDecisions)
auditEvent.UpdateObligations(fulfillableObligationValueFQNs, obligationDecision.AllObligationsSatisfied)

// Determine final audit decision result
auditDecision := audit.GetDecisionResultDeny
if allPermitted {
auditDecision = audit.GetDecisionResultPermit
}

p.logger.Audit.GetDecisionV2(ctx, audit.GetDecisionV2EventParams{
EntityID: entityID,
ActionName: action.GetName(),
Decision: auditDecision,
Entitlements: entitlements,
FulfillableObligationValueFQNs: fulfillableObligationValueFQNs,
ObligationsSatisfied: obligationDecision.AllObligationsSatisfied,
ResourceDecisions: auditResourceDecisions,
})
// Mark as successful with final decision
auditEvent.Success(ctx, auditDecision)
}
4 changes: 2 additions & 2 deletions service/kas/access/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ type PolicyBody struct {
}

// Audit helper methods
func ConvertToAuditKasPolicy(policy Policy) audit.KasPolicy {
return audit.KasPolicy{
func ConvertToAuditKasPolicy(policy Policy) *audit.KasPolicy {
return &audit.KasPolicy{
UUID: policy.UUID,
Body: audit.KasPolicyBody{
DataAttributes: convertToAuditKasBodyDataAttributes(policy.Body.DataAttributes),
Expand Down
Loading
Loading