Skip to content

Commit 6784e88

Browse files
authored
feat(authz): service logic to use request auth as entity identifier in PDP decisions/entitlements (#2790)
### Proposed Changes * pulls access token from context (where it was set by interceptors) in PDP logic * protovalidate tests for new proto ### Checklist - [ ] I have added or updated unit tests - [ ] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions
1 parent 2054e91 commit 6784e88

File tree

2 files changed

+114
-2
lines changed

2 files changed

+114
-2
lines changed

service/authorization/v2/authorization_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/stretchr/testify/assert"
1515
"github.com/stretchr/testify/require"
1616
"google.golang.org/protobuf/proto"
17+
"google.golang.org/protobuf/types/known/wrapperspb"
1718
)
1819

1920
var (
@@ -141,6 +142,27 @@ var (
141142
},
142143
expectedValidationError: "entity_identifier",
143144
},
145+
{
146+
name: "entity identifier - request token invalid",
147+
request: &authzV2.GetDecisionMultiResourceRequest{
148+
EntityIdentifier: &authzV2.EntityIdentifier{
149+
Identifier: &authzV2.EntityIdentifier_WithRequestToken{
150+
WithRequestToken: wrapperspb.Bool(false),
151+
},
152+
},
153+
Action: sampleActionCreate,
154+
Resources: []*authzV2.Resource{
155+
{
156+
Resource: &authzV2.Resource_AttributeValues_{
157+
AttributeValues: &authzV2.Resource_AttributeValues{
158+
Fqns: []string{sampleResourceFQN},
159+
},
160+
},
161+
},
162+
},
163+
},
164+
expectedValidationError: "entity_identifier",
165+
},
144166
{
145167
name: "missing action",
146168
request: &authzV2.GetDecisionMultiResourceRequest{
@@ -505,6 +527,24 @@ func Test_GetDecisionRequest_Succeeds(t *testing.T) {
505527
},
506528
},
507529
},
530+
{
531+
name: "entity: use request token, action: create, resource: attribute values",
532+
request: &authzV2.GetDecisionRequest{
533+
EntityIdentifier: &authzV2.EntityIdentifier{
534+
Identifier: &authzV2.EntityIdentifier_WithRequestToken{
535+
WithRequestToken: wrapperspb.Bool(true),
536+
},
537+
},
538+
Action: sampleActionCreate,
539+
Resource: &authzV2.Resource{
540+
Resource: &authzV2.Resource_AttributeValues_{
541+
AttributeValues: &authzV2.Resource_AttributeValues{
542+
Fqns: []string{sampleResourceFQN},
543+
},
544+
},
545+
},
546+
},
547+
},
508548
{
509549
name: "entity: token, action: create, resource: registered",
510550
request: &authzV2.GetDecisionRequest{
@@ -685,6 +725,44 @@ func Test_GetDecisionRequest_Fails(t *testing.T) {
685725
},
686726
expectedValidationError: "entity_identifier",
687727
},
728+
{
729+
name: "entity identifier (request token) but nil",
730+
request: &authzV2.GetDecisionRequest{
731+
EntityIdentifier: &authzV2.EntityIdentifier{
732+
Identifier: &authzV2.EntityIdentifier_WithRequestToken{
733+
WithRequestToken: nil,
734+
},
735+
},
736+
Action: sampleActionCreate,
737+
Resource: &authzV2.Resource{
738+
Resource: &authzV2.Resource_AttributeValues_{
739+
AttributeValues: &authzV2.Resource_AttributeValues{
740+
Fqns: []string{sampleResourceFQN},
741+
},
742+
},
743+
},
744+
},
745+
expectedValidationError: "entity_identifier",
746+
},
747+
{
748+
name: "entity identifier (request token) but false",
749+
request: &authzV2.GetDecisionRequest{
750+
EntityIdentifier: &authzV2.EntityIdentifier{
751+
Identifier: &authzV2.EntityIdentifier_WithRequestToken{
752+
WithRequestToken: wrapperspb.Bool(false),
753+
},
754+
},
755+
Action: sampleActionCreate,
756+
Resource: &authzV2.Resource{
757+
Resource: &authzV2.Resource_AttributeValues_{
758+
AttributeValues: &authzV2.Resource_AttributeValues{
759+
Fqns: []string{sampleResourceFQN},
760+
},
761+
},
762+
},
763+
},
764+
expectedValidationError: "entity_identifier",
765+
},
688766
{
689767
name: "missing action",
690768
request: &authzV2.GetDecisionRequest{

service/internal/access/v2/just_in_time_pdp.go

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,20 @@ import (
1414
"github.com/opentdf/platform/protocol/go/policy"
1515
"github.com/opentdf/platform/protocol/go/policy/subjectmapping"
1616
otdfSDK "github.com/opentdf/platform/sdk"
17+
ctxAuth "github.com/opentdf/platform/service/pkg/auth"
18+
"google.golang.org/protobuf/types/known/wrapperspb"
1719

1820
"github.com/opentdf/platform/service/internal/access/v2/obligations"
1921
"github.com/opentdf/platform/service/logger"
2022
)
2123

2224
var (
23-
ErrMissingRequiredSDK = errors.New("access: missing required SDK")
24-
ErrInvalidEntityType = errors.New("access: invalid entity type")
25+
ErrMissingRequiredSDK = errors.New("access: missing required SDK")
26+
ErrInvalidEntityType = errors.New("access: invalid entity type")
27+
ErrFailedToWithRequestTokenEntityIdentifier = errors.New("access: failed to use request token as entity identifier - none found in context")
28+
ErrInvalidWithRequestTokenEntityIdentifier = errors.New("access: invalid use request token as entity identifier - must be true if provided")
29+
30+
requestAuthTokenEphemeralID = "with-request-token-auth-entity"
2531
)
2632

2733
type JustInTimePDP struct {
@@ -153,6 +159,9 @@ func (p *JustInTimePDP) GetDecision(
153159
case *authzV2.EntityIdentifier_Token:
154160
entityRepresentations, err = p.resolveEntitiesFromToken(ctx, entityIdentifier.GetToken(), skipEnvironmentEntities)
155161

162+
case *authzV2.EntityIdentifier_WithRequestToken:
163+
entityRepresentations, err = p.resolveEntitiesFromRequestToken(ctx, entityIdentifier.GetWithRequestToken(), skipEnvironmentEntities)
164+
156165
case *authzV2.EntityIdentifier_RegisteredResourceValueFqn:
157166
regResValueFQN := strings.ToLower(entityIdentifier.GetRegisteredResourceValueFqn())
158167
// Registered resources do not have entity representations, so only one decision is made
@@ -246,6 +255,9 @@ func (p *JustInTimePDP) GetEntitlements(
246255
// registered resources do not have entity representations, so we can skip the remaining logic
247256
return p.pdp.GetEntitlementsRegisteredResource(ctx, regResValueFQN, withComprehensiveHierarchy)
248257

258+
case *authzV2.EntityIdentifier_WithRequestToken:
259+
entityRepresentations, err = p.resolveEntitiesFromRequestToken(ctx, entityIdentifier.GetWithRequestToken(), skipEnvironmentEntities)
260+
249261
default:
250262
return nil, fmt.Errorf("entity type %T: %w", entityIdentifier.GetIdentifier(), ErrInvalidEntityType)
251263
}
@@ -366,3 +378,25 @@ func (p *JustInTimePDP) resolveEntitiesFromToken(
366378
}
367379
return p.resolveEntitiesFromEntityChain(ctx, entityChains[0], skipEnvironmentEntities)
368380
}
381+
382+
// resolveEntitiesFromRequestToken pulls the request token off the context where it has been set upstream
383+
// by an interceptor and builds an entity.Token that it then resolves
384+
func (p *JustInTimePDP) resolveEntitiesFromRequestToken(
385+
ctx context.Context,
386+
withRequestToken *wrapperspb.BoolValue,
387+
skipEnvironmentEntities bool,
388+
) ([]*entityresolutionV2.EntityRepresentation, error) {
389+
if !withRequestToken.GetValue() {
390+
return nil, ErrInvalidWithRequestTokenEntityIdentifier
391+
}
392+
rawToken := ctxAuth.GetRawAccessTokenFromContext(ctx, p.logger)
393+
if rawToken == "" {
394+
return nil, ErrFailedToWithRequestTokenEntityIdentifier
395+
}
396+
token := &entity.Token{
397+
Jwt: rawToken,
398+
EphemeralId: requestAuthTokenEphemeralID,
399+
}
400+
401+
return p.resolveEntitiesFromToken(ctx, token, skipEnvironmentEntities)
402+
}

0 commit comments

Comments
 (0)