Skip to content

Commit

Permalink
refactor: core error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
fredmaggiowski committed Jun 30, 2023
1 parent c223f45 commit 107a520
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 29 deletions.
16 changes: 15 additions & 1 deletion core/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,19 @@ package core
import "fmt"

var (
ErrPolicyEvalFailed = fmt.Errorf("policy evaluation failed")
ErrEvaluatorCreationFailed = fmt.Errorf("error during evaluator creation")
ErrEvaluatorNotFound = fmt.Errorf("evaluator not found")

ErrPolicyEvalFailed = fmt.Errorf("policy evaluation failed")
ErrPartialPolicyEvalFailed = fmt.Errorf("partial %w", ErrPolicyEvalFailed)
ErrResponsePolicyEvalFailed = fmt.Errorf("response %w", ErrPolicyEvalFailed)

ErrFailedInputParse = fmt.Errorf("failed input parse")
ErrFailedInputEncode = fmt.Errorf("failed input encode")
ErrFailedInputRequestParse = fmt.Errorf("failed request body parse")
ErrFailedInputRequestDeserialization = fmt.Errorf("failed request body deserialization")

ErrUnexepectedContentType = fmt.Errorf("unexpected content type")

ErrOPATransportInvalidResponseBody = fmt.Errorf("response body is not valid")
)
10 changes: 6 additions & 4 deletions core/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,11 @@ func CreateRegoQueryInput(

inputBytes, err := json.Marshal(input)
if err != nil {
return nil, fmt.Errorf("failed input JSON encode: %v", err)
return nil, fmt.Errorf("%w: %v", ErrFailedInputEncode, err)
}
logger.Tracef("OPA input rego creation in: %+v", time.Since(opaInputCreationTime))
logger.
WithField("inputCreationTimeMicroseconds", time.Since(opaInputCreationTime).Microseconds()).
Tracef("input creation time")
return inputBytes, nil
}

Expand All @@ -131,10 +133,10 @@ func (req requestInfo) Input(user types.User, responseBody any) (Input, error) {
if shouldParseJSONBody {
bodyBytes, err := io.ReadAll(req.Body)
if err != nil {
return Input{}, fmt.Errorf("failed request body parse: %s", err.Error())
return Input{}, fmt.Errorf("%w: %s", ErrFailedInputRequestParse, err.Error())
}
if err := json.Unmarshal(bodyBytes, &requestBody); err != nil {
return Input{}, fmt.Errorf("failed request body deserialization: %s", err.Error())
return Input{}, fmt.Errorf("%w: %s", ErrFailedInputRequestDeserialization, err.Error())
}
req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
Expand Down
6 changes: 3 additions & 3 deletions core/opa_transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er

if !utils.HasApplicationJSONContentType(resp.Header) {
t.logger.WithField("foundContentType", resp.Header.Get(utils.ContentTypeHeaderKey)).Debug("found content type")
t.responseWithError(resp, fmt.Errorf("content-type is not application/json"), http.StatusInternalServerError)
t.responseWithError(resp, fmt.Errorf("%w: response content-type should be application/json", ErrUnexepectedContentType), http.StatusInternalServerError)
return resp, nil
}

var decodedBody interface{}
if err := json.Unmarshal(b, &decodedBody); err != nil {
return nil, fmt.Errorf("response body is not valid: %s", err.Error())
return nil, fmt.Errorf("%w: %s", ErrOPATransportInvalidResponseBody, err.Error())
}

userInfo, err := mongoclient.RetrieveUserBindingsAndRoles(t.logger, t.request, t.userHeaders)
Expand All @@ -121,7 +121,7 @@ func (t *OPATransport) RoundTrip(req *http.Request) (resp *http.Response, err er
}

func (t *OPATransport) responseWithError(resp *http.Response, err error, statusCode int) {
t.logger.WithField("error", logrus.Fields{"message": err.Error()}).Error("error while evaluating column filter query")
t.logger.WithField("error", logrus.Fields{"message": err.Error()}).Error(ErrResponsePolicyEvalFailed)
message := utils.NO_PERMISSIONS_ERROR_MESSAGE
if statusCode != http.StatusForbidden {
message = utils.GENERIC_BUSINESS_ERROR_MESSAGE
Expand Down
56 changes: 35 additions & 21 deletions core/opaevaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,29 @@ type PartialEvaluator struct {
}

func createPartialEvaluator(ctx context.Context, logger *logrus.Entry, policy string, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (*PartialEvaluator, error) {
logger.Infof("precomputing rego query for allow policy: %s", policy)
logger.WithField("policyName", policy).Info("precomputing rego policy")

policyEvaluatorTime := time.Now()
partialResultEvaluator, err := NewPartialResultEvaluator(ctx, policy, opaModuleConfig, options)
if err == nil {
logger.Infof("computed rego query for policy: %s in %s", policy, time.Since(policyEvaluatorTime))
return &PartialEvaluator{
PartialEvaluator: partialResultEvaluator,
}, nil
if err != nil {
return nil, err
}
return nil, err

logger.
WithFields(logrus.Fields{
"policyName": policy,
"computationTimeMicroserconds": time.Since(policyEvaluatorTime).Microseconds,
}).
Info("precomputation time")

return &PartialEvaluator{PartialEvaluator: partialResultEvaluator}, nil
}

func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *openapi.OpenAPISpec, opaModuleConfig *OPAModuleConfig, options *EvaluatorOptions) (PartialResultsEvaluators, error) {
if oas == nil {
return nil, fmt.Errorf("oas must not be nil")
}

policyEvaluators := PartialResultsEvaluators{}
for path, OASContent := range oas.Paths {
for verb, verbConfig := range OASContent {
Expand All @@ -91,17 +97,24 @@ func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *openapi.Ope
allowPolicy := verbConfig.PermissionV2.RequestFlow.PolicyName
responsePolicy := verbConfig.PermissionV2.ResponseFlow.PolicyName

logger.Infof("precomputing rego queries for API: %s %s. Allow policy: %s. Response policy: %s.", verb, path, allowPolicy, responsePolicy)
logger.
WithFields(logrus.Fields{
"verb": verb,
"policyName": allowPolicy,
"path": path,
"responsePolicyName": responsePolicy,
}).
Info("precomputing rego queries for API")

if allowPolicy == "" {
// allow policy is required, if missing assume the API has no valid x-rond configuration.
continue
}

if _, ok := policyEvaluators[allowPolicy]; !ok {
evaluator, err := createPartialEvaluator(ctx, logger, allowPolicy, oas, opaModuleConfig, options)

if err != nil {
return nil, fmt.Errorf("error during evaluator creation: %s", err.Error())
return nil, fmt.Errorf("%w: %s", ErrEvaluatorCreationFailed, err.Error())
}

policyEvaluators[allowPolicy] = *evaluator
Expand All @@ -110,9 +123,8 @@ func SetupEvaluators(ctx context.Context, logger *logrus.Entry, oas *openapi.Ope
if responsePolicy != "" {
if _, ok := policyEvaluators[responsePolicy]; !ok {
evaluator, err := createPartialEvaluator(ctx, logger, responsePolicy, oas, opaModuleConfig, options)

if err != nil {
return nil, fmt.Errorf("error during evaluator creation: %s", err.Error())
return nil, fmt.Errorf("%w: %s", ErrEvaluatorCreationFailed, err.Error())
}

policyEvaluators[responsePolicy] = *evaluator
Expand Down Expand Up @@ -181,7 +193,7 @@ func NewOPAEvaluator(ctx context.Context, policy string, opaModuleConfig *OPAMod
}
inputTerm, err := ast.ParseTerm(string(input))
if err != nil {
return nil, fmt.Errorf("failed input parse: %v", err)
return nil, fmt.Errorf("%w: %v", ErrFailedInputParse, err)
}

sanitizedPolicy := strings.Replace(policy, ".", "_", -1)
Expand Down Expand Up @@ -220,10 +232,12 @@ func (config *OPAModuleConfig) CreateQueryEvaluator(ctx context.Context, logger
opaEvaluatorInstanceTime := time.Now()
evaluator, err := NewOPAEvaluator(ctx, policy, config, input, options)
if err != nil {
logger.WithError(err).Error("failed RBAC policy creation")
logger.WithError(err).Error(ErrEvaluatorCreationFailed)
return nil, err
}
logger.Tracef("OPA evaluator instantiated in: %+v", time.Since(opaEvaluatorInstanceTime))
logger.
WithField("evaluatorCreationTimeMicroseconds", time.Since(opaEvaluatorInstanceTime).Microseconds()).
Trace("evaluator creation time")
return evaluator, nil
}

Expand Down Expand Up @@ -265,7 +279,7 @@ func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx con
if eval, ok := partialEvaluators[policy]; ok {
inputTerm, err := ast.ParseTerm(string(input))
if err != nil {
return nil, fmt.Errorf("failed input parse: %v", err)
return nil, fmt.Errorf("%w: %v", ErrFailedInputParse, err)
}

evaluator := eval.PartialEvaluator.Rego(
Expand All @@ -283,7 +297,7 @@ func (partialEvaluators PartialResultsEvaluators) GetEvaluatorFromPolicy(ctx con
routerInfo: options.RouterInfo,
}, nil
}
return nil, fmt.Errorf("policy evaluator not found: %s", policy)
return nil, fmt.Errorf("%w: %s", ErrEvaluatorNotFound, policy)
}

func (evaluator *OPAEvaluator) metrics() metrics.Metrics {
Expand All @@ -297,7 +311,7 @@ func (evaluator *OPAEvaluator) partiallyEvaluate(logger *logrus.Entry) (primitiv
opaEvaluationTimeStart := time.Now()
partialResults, err := evaluator.PolicyEvaluator.Partial(evaluator.Context)
if err != nil {
return nil, fmt.Errorf("policy Evaluation has failed when partially evaluating the query: %s", err.Error())
return nil, fmt.Errorf("%w: %s", ErrPartialPolicyEvalFailed, err.Error())
}

opaEvaluationTime := time.Since(opaEvaluationTimeStart)
Expand Down Expand Up @@ -335,7 +349,7 @@ func (evaluator *OPAEvaluator) Evaluate(logger *logrus.Entry) (interface{}, erro

results, err := evaluator.PolicyEvaluator.Eval(evaluator.Context)
if err != nil {
return nil, fmt.Errorf("policy Evaluation has failed when evaluating the query: %s", err.Error())
return nil, fmt.Errorf("%w: %s", ErrPolicyEvalFailed, err.Error())
}

opaEvaluationTime := time.Since(opaEvaluationTimeStart)
Expand Down Expand Up @@ -417,11 +431,11 @@ func LoadRegoModule(rootDirectory string) (*OPAModuleConfig, error) {
})

if regoModulePath == "" {
return nil, fmt.Errorf("no rego module found in directory")
return nil, ErrMissingRegoModules

Check failure on line 434 in core/opaevaluator.go

View workflow job for this annotation

GitHub Actions / Test with go version 1.19 on OS ubuntu-latest

undefined: ErrMissingRegoModules

Check failure on line 434 in core/opaevaluator.go

View workflow job for this annotation

GitHub Actions / Bench with go version 1.19 on OS ubuntu-latest

undefined: ErrMissingRegoModules
}
fileContent, err := utils.ReadFile(regoModulePath)
if err != nil {
return nil, fmt.Errorf("failed rego file read: %s", err.Error())
return nil, fmt.Errorf("%w: %s", ErrRegoModuleReadFailed, err.Error())

Check failure on line 438 in core/opaevaluator.go

View workflow job for this annotation

GitHub Actions / Test with go version 1.19 on OS ubuntu-latest

undefined: ErrRegoModuleReadFailed

Check failure on line 438 in core/opaevaluator.go

View workflow job for this annotation

GitHub Actions / Bench with go version 1.19 on OS ubuntu-latest

undefined: ErrRegoModuleReadFailed
}

return &OPAModuleConfig{
Expand Down

0 comments on commit 107a520

Please sign in to comment.