Skip to content

Commit

Permalink
PTCI-830: Refactor Evaluate to expose new Validate method (v2 branch)
Browse files Browse the repository at this point in the history
  • Loading branch information
rchowinfoblox committed Dec 5, 2024
1 parent 86c2529 commit 29a8ae0
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 25 deletions.
14 changes: 11 additions & 3 deletions common/authorizer/authorizer.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package authorizer

import "context"
import (
"context"
)

// OpaEvaluator implements calling OPA with a request and receiving the raw response
type OpaEvaluator func(ctx context.Context, decisionDocument string, opaReq, opaResp interface{}) error
Expand All @@ -21,10 +23,16 @@ type FilterCompartmentFeaturesType map[string][]string

// Authorizer interface is implemented for making arbitrary requests to Opa.
type Authorizer interface {
// Validate evaluates the authorization policy for the given request.
// It takes the context, full method name, request object, and an OpaEvaluator as input.
// Unlike Evaluate, it only returns the raw Opa response, it does not parse the results.
Validate(ctx context.Context, fullMethod string, req interface{}, opaEvaluator OpaEvaluator) (interface{}, error)

// Evaluate evaluates the authorization policy for the given request.
// It takes the context, full method name, request object, and an OpaEvaluator as input.
// It returns a boolean indicating whether the request is authorized, a modified context,
// and an error if any.
// It parses the Opa response and returns a boolean indicating whether the request is authorized,
// a modified context, and an error if any. It also parses and adds the entitled_features and obligations
// from Opa response in the modified context returned.
Evaluate(ctx context.Context, fullMethod string, req interface{}, opaEvaluator OpaEvaluator) (bool, context.Context, error)

// OpaQuery executes a query against the OPA (Open Policy Agent) with the specified decision document.
Expand Down
15 changes: 15 additions & 0 deletions common/authorizer/mock_Authorizer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

62 changes: 40 additions & 22 deletions http_opa/authorizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,45 @@ func (a *httpAuthorizer) Evaluate(ctx context.Context, endpoint string, req inte
"application": a.application,
})

rawResp, err := a.Validate(ctx, endpoint, req, opaEvaluator)
if err != nil {
return false, ctx, err
}
opaResp, ok := rawResp.(opautil.OPAResponse)
if !ok {
return false, ctx, exception.ErrUnknown
}

// Add raw entitled_features data to the context
//REVIEW: is it needed for http?
ctx = opaResp.AddRawEntitledFeatures(ctx)

// Add obligations data to the context
//REVIEW: is it needed for http?
ctx, err = opautil.AddObligations(ctx, opaResp)
if err != nil {
logger.WithField("opaResp", fmt.Sprintf("%#v", opaResp)).WithError(err).Error("parse_obligations_error")
}

// Check if the authorization is allowed
if !opaResp.Allow() {
return false, ctx, exception.ErrForbidden
}

return true, ctx, nil
}

func (a *httpAuthorizer) Validate(ctx context.Context, endpoint string, req interface{}, opaEvaluator az.OpaEvaluator) (interface{}, error) {
// Extract the logger from the context
logger := ctxlogrus.Extract(ctx).WithFields(log.Fields{
"application": a.application,
})

// Get the bearer token from the request
bearer, err := util.GetBearerFromRequest(req.(*http.Request))
if err != nil {
logger.WithError(err).Error("get_bearer_from_request")
return false, ctx, exception.ErrInvalidArg
return nil, exception.ErrInvalidArg
}

// Verify the bearer token and get the raw JWT
Expand All @@ -96,7 +130,7 @@ func (a *httpAuthorizer) Evaluate(ctx context.Context, endpoint string, req inte
}
rawJWT, errs := claimsVerifier([]string{bearer}, nil)
if len(errs) > 0 {
return false, ctx, exception.NewHttpError(
return nil, exception.NewHttpError(
exception.WithError(errors.Join(errs...)),
exception.WithHttpStatus(http.StatusUnauthorized))
}
Expand Down Expand Up @@ -126,7 +160,7 @@ func (a *httpAuthorizer) Evaluate(ctx context.Context, endpoint string, req inte
logger.WithFields(log.Fields{
"endpoint": endpoint,
}).WithError(err).Error("get_decision_input")
return false, ctx, exception.ErrInvalidArg
return nil, exception.ErrInvalidArg
}

opaReq.DecisionInput = *decisionInput
Expand All @@ -137,7 +171,7 @@ func (a *httpAuthorizer) Evaluate(ctx context.Context, endpoint string, req inte
// logger.WithFields(log.Fields{
// "opaReq": opaReq,
// }).WithError(err).Error("opa_request_json_marshal")
// return false, ctx, exception.ErrInvalidArg
// return nil, exception.ErrInvalidArg
// }

now := time.Now()
Expand Down Expand Up @@ -177,7 +211,7 @@ func (a *httpAuthorizer) Evaluate(ctx context.Context, endpoint string, req inte
}).Debug("authorization_result")
}()
if err != nil {
return false, ctx, err
return nil, err
}

// Extract the nested result if present
Expand All @@ -201,23 +235,7 @@ func (a *httpAuthorizer) Evaluate(ctx context.Context, endpoint string, req inte
// }, "out")
}

// Add raw entitled_features data to the context
//REVIEW: is it needed for http?
ctx = opaResp.AddRawEntitledFeatures(ctx)

// Add obligations data to the context
//REVIEW: is it needed for http?
ctx, err = opautil.AddObligations(ctx, opaResp)
if err != nil {
logger.WithField("opaResp", fmt.Sprintf("%#v", opaResp)).WithError(err).Error("parse_obligations_error")
}

// Check if the authorization is allowed
if !opaResp.Allow() {
return false, ctx, exception.ErrForbidden
}

return true, ctx, nil
return opaResp, nil
}

// OpaQuery executes a custom OPA query with the given decision document, request, and response.
Expand Down

0 comments on commit 29a8ae0

Please sign in to comment.