mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 18:36:30 +02:00
* wip * wip * wip * remove SignedJWT field * set google_cloud_serverless_authentication_service_account * update jwt claim headers * add mock get_google_cloud_serverless_headers for opa test * swap issuer and audience * add comment * change default port in authz
124 lines
3 KiB
Go
124 lines
3 KiB
Go
package evaluator
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/open-policy-agent/opa/rego"
|
|
"github.com/open-policy-agent/opa/storage"
|
|
|
|
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
|
)
|
|
|
|
// A CustomEvaluatorRequest is the data needed to evaluate a custom rego policy.
|
|
type CustomEvaluatorRequest struct {
|
|
RegoPolicy string
|
|
HTTP RequestHTTP `json:"http"`
|
|
Session RequestSession `json:"session"`
|
|
}
|
|
|
|
// A CustomEvaluatorResponse is the response from the evaluation of a custom rego policy.
|
|
type CustomEvaluatorResponse struct {
|
|
Allowed bool
|
|
Denied bool
|
|
Reason string
|
|
Headers map[string]string
|
|
}
|
|
|
|
// A CustomEvaluator evaluates custom rego policies.
|
|
type CustomEvaluator struct {
|
|
store storage.Store
|
|
mu sync.Mutex
|
|
queries map[string]rego.PreparedEvalQuery
|
|
}
|
|
|
|
// NewCustomEvaluator creates a new CustomEvaluator.
|
|
func NewCustomEvaluator(store storage.Store) *CustomEvaluator {
|
|
ce := &CustomEvaluator{
|
|
store: store,
|
|
queries: map[string]rego.PreparedEvalQuery{},
|
|
}
|
|
return ce
|
|
}
|
|
|
|
// Evaluate evaluates the custom rego policy.
|
|
func (ce *CustomEvaluator) Evaluate(ctx context.Context, req *CustomEvaluatorRequest) (*CustomEvaluatorResponse, error) {
|
|
_, span := trace.StartSpan(ctx, "authorize.evaluator.custom.Evaluate")
|
|
defer span.End()
|
|
|
|
q, err := ce.getPreparedEvalQuery(ctx, req.RegoPolicy)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
resultSet, err := q.Eval(ctx, rego.EvalInput(struct {
|
|
HTTP RequestHTTP `json:"http"`
|
|
Session RequestSession `json:"session"`
|
|
}{HTTP: req.HTTP, Session: req.Session}))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
vars, ok := resultSet[0].Bindings.WithoutWildcards()["result"].(map[string]interface{})
|
|
if !ok {
|
|
vars = make(map[string]interface{})
|
|
}
|
|
|
|
res := &CustomEvaluatorResponse{
|
|
Headers: getHeadersVar(resultSet[0].Bindings.WithoutWildcards()),
|
|
}
|
|
res.Allowed, _ = vars["allow"].(bool)
|
|
if v, ok := vars["deny"]; ok {
|
|
// support `deny = true`
|
|
if b, ok := v.(bool); ok {
|
|
res.Denied = b
|
|
}
|
|
|
|
// support `deny[reason] = true`
|
|
if m, ok := v.(map[string]interface{}); ok {
|
|
for mk, mv := range m {
|
|
if b, ok := mv.(bool); ok {
|
|
res.Denied = b
|
|
res.Reason = mk
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (ce *CustomEvaluator) getPreparedEvalQuery(ctx context.Context, src string) (rego.PreparedEvalQuery, error) {
|
|
ce.mu.Lock()
|
|
defer ce.mu.Unlock()
|
|
|
|
q, ok := ce.queries[src]
|
|
if ok {
|
|
return q, nil
|
|
}
|
|
|
|
r := rego.New(
|
|
rego.Store(ce.store),
|
|
rego.Module("pomerium.custom_policy", src),
|
|
rego.Query("result = data.pomerium.custom_policy"),
|
|
)
|
|
q, err := r.PrepareForEval(ctx)
|
|
if err != nil {
|
|
// if no package is in the src, add it
|
|
if strings.Contains(err.Error(), "package expected") {
|
|
r = rego.New(
|
|
rego.Store(ce.store),
|
|
rego.Module("pomerium.custom_policy", "package pomerium.custom_policy\n\n"+src),
|
|
rego.Query("result = data.pomerium.custom_policy"),
|
|
)
|
|
q, err = r.PrepareForEval(ctx)
|
|
}
|
|
}
|
|
if err != nil {
|
|
return q, fmt.Errorf("invalid rego policy: %w", err)
|
|
}
|
|
|
|
ce.queries[src] = q
|
|
return q, nil
|
|
}
|