Skip to content

Commit 11b3ea9

Browse files
authored
feat(authz): wire up obligations enforcement in auth service (#2756)
### Proposed Changes * Wires up obligation PDP to JustInTimePDP within Auth Service * EntitlementPolicyCache fetches all obligations from policy alongside attributes/subjectmappings/registeredresources * Interceptors updated so that IPC requests transmit auth state to downstream called RPCs * Update tests * Improved Auth Service errors (sanitizing internal information from error responses) * Improved Auth Service logging * Improved [context metadata implementation](https://github.com/grpc/grpc-go/blob/master/Documentation/grpc-metadata.md) ### Checklist - [X] I have added or updated unit tests - [X] I have added or updated integration tests (if appropriate) - [ ] I have added or updated documentation ### Testing Instructions
1 parent 2cb0c87 commit 11b3ea9

File tree

16 files changed

+882
-143
lines changed

16 files changed

+882
-143
lines changed

service/authorization/v2/authorization.go

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ import (
1212
"github.com/go-viper/mapstructure/v2"
1313
authzV2 "github.com/opentdf/platform/protocol/go/authorization/v2"
1414
authzV2Connect "github.com/opentdf/platform/protocol/go/authorization/v2/authorizationv2connect"
15+
"github.com/opentdf/platform/protocol/go/policy"
1516
otdf "github.com/opentdf/platform/sdk"
1617
"github.com/opentdf/platform/service/internal/access/v2"
1718
"github.com/opentdf/platform/service/logger"
19+
ctxAuth "github.com/opentdf/platform/service/pkg/auth"
1820
"github.com/opentdf/platform/service/pkg/cache"
1921
"github.com/opentdf/platform/service/pkg/serviceregistry"
2022
"go.opentelemetry.io/otel"
@@ -23,6 +25,13 @@ import (
2325
"google.golang.org/protobuf/types/known/wrapperspb"
2426
)
2527

28+
var (
29+
ErrFailedToBuildRequestContext = errors.New("failed to contextualize decision request")
30+
ErrFailedToInitPDP = errors.New("failed to create JIT PDP")
31+
ErrFailedToGetDecision = errors.New("failed to get decision")
32+
ErrFailedToGetEntitlements = errors.New("failed to get entitlements")
33+
)
34+
2635
type Service struct {
2736
sdk *otdf.SDK
2837
config *Config
@@ -138,15 +147,12 @@ func (as *Service) GetEntitlements(ctx context.Context, req *connect.Request[aut
138147
// When authorization service can consume cached policy, switch to the other PDP (process based on policy passed in)
139148
pdp, err := access.NewJustInTimePDP(ctx, as.logger, as.sdk, as.cache)
140149
if err != nil {
141-
as.logger.ErrorContext(ctx, "failed to create JIT PDP", slog.Any("error", err))
142-
return nil, connect.NewError(connect.CodeInternal, err)
150+
return nil, statusifyError(ctx, as.logger, errors.Join(ErrFailedToGetEntitlements, ErrFailedToInitPDP, err))
143151
}
144152

145153
entitlements, err := pdp.GetEntitlements(ctx, entityIdentifier, withComprehensiveHierarchy)
146154
if err != nil {
147-
// TODO: any bad request errors that aren't 500s?
148-
as.logger.ErrorContext(ctx, "failed to get entitlements", slog.Any("error", err))
149-
return nil, connect.NewError(connect.CodeInternal, err)
155+
return nil, statusifyError(ctx, as.logger, errors.Join(ErrFailedToGetEntitlements, err))
150156
}
151157
rsp := &authzV2.GetEntitlementsResponse{
152158
Entitlements: entitlements,
@@ -168,27 +174,34 @@ func (as *Service) GetDecision(ctx context.Context, req *connect.Request[authzV2
168174

169175
pdp, err := access.NewJustInTimePDP(ctx, as.logger, as.sdk, as.cache)
170176
if err != nil {
171-
as.logger.ErrorContext(ctx, "failed to create JIT PDP", slog.Any("error", err))
172-
return nil, connect.NewError(connect.CodeInternal, err)
177+
return nil, statusifyError(ctx, as.logger, errors.Join(ErrFailedToInitPDP, err))
173178
}
174179

175180
request := req.Msg
176181
entityIdentifier := request.GetEntityIdentifier()
177182
action := request.GetAction()
178183
resource := request.GetResource()
184+
fulfillableObligations := request.GetFulfillableObligationFqns()
179185

180-
decisions, permitted, err := pdp.GetDecision(ctx, entityIdentifier, action, []*authzV2.Resource{resource})
186+
reqContext, err := as.getDecisionRequestContext(ctx)
181187
if err != nil {
182-
as.logger.ErrorContext(ctx, "failed to get decision", slog.Any("error", err))
183-
if errors.Is(err, access.ErrFQNNotFound) || errors.Is(err, access.ErrDefinitionNotFound) {
184-
return nil, connect.NewError(connect.CodeNotFound, err)
185-
}
186-
return nil, connect.NewError(connect.CodeInternal, err)
188+
return nil, statusifyError(ctx, as.logger, err)
189+
}
190+
191+
decisions, permitted, err := pdp.GetDecision(
192+
ctx,
193+
entityIdentifier,
194+
action,
195+
[]*authzV2.Resource{resource},
196+
reqContext,
197+
fulfillableObligations,
198+
)
199+
if err != nil {
200+
return nil, statusifyError(ctx, as.logger, err)
187201
}
188202
resp, err := rollupSingleResourceDecision(permitted, decisions)
189203
if err != nil {
190-
as.logger.ErrorContext(ctx, "failed to rollup single-resource decision", slog.Any("error", err))
191-
return nil, connect.NewError(connect.CodeInternal, err)
204+
return nil, statusifyError(ctx, as.logger, err)
192205
}
193206
return connect.NewResponse(resp), nil
194207
}
@@ -206,21 +219,34 @@ func (as *Service) GetDecisionMultiResource(ctx context.Context, req *connect.Re
206219

207220
pdp, err := access.NewJustInTimePDP(ctx, as.logger, as.sdk, as.cache)
208221
if err != nil {
209-
return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to create JIT PDP"), err))
222+
return nil, statusifyError(ctx, as.logger, errors.Join(ErrFailedToInitPDP, err))
210223
}
211224
request := req.Msg
212225
entityIdentifier := request.GetEntityIdentifier()
213226
action := request.GetAction()
214227
resources := request.GetResources()
228+
fulfillableObligations := request.GetFulfillableObligationFqns()
229+
230+
reqContext, err := as.getDecisionRequestContext(ctx)
231+
if err != nil {
232+
return nil, statusifyError(ctx, as.logger, err)
233+
}
215234

216-
decisions, allPermitted, err := pdp.GetDecision(ctx, entityIdentifier, action, resources)
235+
decisions, allPermitted, err := pdp.GetDecision(
236+
ctx,
237+
entityIdentifier,
238+
action,
239+
resources,
240+
reqContext,
241+
fulfillableObligations,
242+
)
217243
if err != nil {
218-
return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to get decision"), err))
244+
return nil, statusifyError(ctx, as.logger, errors.Join(ErrFailedToGetDecision, err))
219245
}
220246

221247
resourceDecisions, err := rollupMultiResourceDecisions(decisions)
222248
if err != nil {
223-
return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to rollup multi-resource decision"), err))
249+
return nil, statusifyError(ctx, as.logger, err)
224250
}
225251

226252
resp := &authzV2.GetDecisionMultiResourceResponse{
@@ -246,27 +272,33 @@ func (as *Service) GetDecisionBulk(ctx context.Context, req *connect.Request[aut
246272

247273
pdp, err := access.NewJustInTimePDP(ctx, as.logger, as.sdk, as.cache)
248274
if err != nil {
249-
return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to create JIT PDP"), err))
275+
return nil, statusifyError(ctx, as.logger, errors.Join(ErrFailedToInitPDP, err))
250276
}
251277

252278
multiRequests := req.Msg.GetDecisionRequests()
253279
decisionResponses := make([]*authzV2.GetDecisionMultiResourceResponse, len(multiRequests))
254280

281+
reqContext, err := as.getDecisionRequestContext(ctx)
282+
if err != nil {
283+
return nil, statusifyError(ctx, as.logger, err)
284+
}
285+
255286
// TODO: revisit performance of this loop after introduction of caching and registered resource values within decisioning,
256287
// as the same entity in multiple requests should only be resolved JIT once, not once per request if the same in each.
257288
for idx, request := range multiRequests {
258289
entityIdentifier := request.GetEntityIdentifier()
259290
action := request.GetAction()
260291
resources := request.GetResources()
292+
fulfillableObligations := request.GetFulfillableObligationFqns()
261293

262-
decisions, allPermitted, err := pdp.GetDecision(ctx, entityIdentifier, action, resources)
294+
decisions, allPermitted, err := pdp.GetDecision(ctx, entityIdentifier, action, resources, reqContext, fulfillableObligations)
263295
if err != nil {
264-
return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to get bulk decision"), err))
296+
return nil, statusifyError(ctx, as.logger, errors.Join(ErrFailedToGetDecision, err))
265297
}
266298

267299
resourceDecisions, err := rollupMultiResourceDecisions(decisions)
268300
if err != nil {
269-
return nil, statusifyError(ctx, as.logger, errors.Join(errors.New("failed to rollup bulk multi-resource decision"), err), slog.Int("index", idx))
301+
return nil, statusifyError(ctx, as.logger, err, slog.Int("index", idx))
270302
}
271303

272304
decisionResponse := &authzV2.GetDecisionMultiResourceResponse{
@@ -283,3 +315,17 @@ func (as *Service) GetDecisionBulk(ctx context.Context, req *connect.Request[aut
283315
}
284316
return connect.NewResponse(rsp), nil
285317
}
318+
319+
// Builds a decision request context out of contextual metadata for the downstream obligation trigger/fulfillment decisioning
320+
func (as *Service) getDecisionRequestContext(ctx context.Context) (*policy.RequestContext, error) {
321+
incoming := true
322+
clientID, err := ctxAuth.GetClientIDFromContext(ctx, incoming)
323+
if err != nil {
324+
return nil, errors.Join(ErrFailedToBuildRequestContext, err)
325+
}
326+
return &policy.RequestContext{
327+
Pep: &policy.PolicyEnforcementPoint{
328+
ClientId: clientID,
329+
},
330+
}, nil
331+
}

0 commit comments

Comments
 (0)