ent/privacy: initial privacy package (#836)

This commit is contained in:
Ariel Mashraki
2020-10-11 14:27:29 +03:00
committed by GitHub
parent 13b379d07c
commit dfc4dee8a5
8 changed files with 260 additions and 199 deletions

File diff suppressed because one or more lines are too long

View File

@@ -10,22 +10,26 @@ in the LICENSE file in the root directory of this source tree.
{{ template "header" . }}
{{ end }}
import "{{ $.Config.Package }}"
import (
"{{ $.Config.Package }}"
"github.com/facebook/ent/privacy"
)
{{ $pkg := base $.Config.Package }}
var (
// Allow may be returned by rules to indicate that the policy
// evaluation should terminate with an allow decision.
Allow = errors.New("ent/privacy: allow rule")
Allow = privacy.Allow
// Deny may be returned by rules to indicate that the policy
// evaluation should terminate with an deny decision.
Deny = errors.New("ent/privacy: deny rule")
Deny = privacy.Deny
// Skip may be returned by rules to indicate that the policy
// evaluation should continue to the next rule.
Skip = errors.New("ent/privacy: skip rule")
Skip = privacy.Skip
)
{{- range $decision := list "Allow" "Deny" "Skip" }}
@@ -35,52 +39,25 @@ var (
}
{{- end }}
type decisionCtxKey struct {}
// DecisionContext creates a decision context.
// DecisionContext creates a new context from the given parent context with
// a policy decision attach to it.
func DecisionContext(parent context.Context, decision error) context.Context {
if decision == nil || errors.Is(decision, Skip) {
return parent
}
return context.WithValue(parent, decisionCtxKey{}, decision)
return privacy.DecisionContext(parent, decision)
}
func decisionFromContext(ctx context.Context) (error, bool) {
decision, ok := ctx.Value(decisionCtxKey{}).(error)
if ok && errors.Is(decision, Allow) {
decision = nil
}
return decision, ok
// DecisionFromContext retrieves the policy decision from the context.
func DecisionFromContext(ctx context.Context) (error, bool) {
return privacy.DecisionFromContext(ctx)
}
type (
// QueryPolicy combines multiple query rules into a single policy.
QueryPolicy []QueryRule
// QueryRule defines the interface deciding whether a
// query is allowed and optionally modify it.
QueryRule interface {
EvalQuery(context.Context, {{ $pkg }}.Query) error
}
QueryRule = privacy.QueryRule
// QueryPolicy combines multiple query rules into a single policy.
QueryPolicy = privacy.QueryPolicy
)
// EvalQuery evaluates a query against a query policy.
func (policy QueryPolicy) EvalQuery(ctx context.Context, q {{ $pkg }}.Query) error {
if decision, ok := decisionFromContext(ctx); ok {
return decision
}
for _, rule := range policy {
switch decision := rule.EvalQuery(ctx, q); {
case decision == nil || errors.Is(decision, Skip):
case errors.Is(decision, Allow):
return nil
default:
return decision
}
}
return nil
}
// QueryRuleFunc type is an adapter to allow the use of
// ordinary functions as query rules.
type QueryRuleFunc func(context.Context, {{ $pkg }}.Query) error
@@ -91,32 +68,13 @@ func (f QueryRuleFunc) EvalQuery(ctx context.Context, q {{ $pkg }}.Query) error
}
type (
// MutationPolicy combines multiple mutation rules into a single policy.
MutationPolicy []MutationRule
// MutationRule defines the interface deciding whether a
// mutation is allowed and optionally modify it.
MutationRule interface {
EvalMutation(context.Context, {{ $pkg }}.Mutation) error
}
MutationRule = privacy.MutationRule
// MutationPolicy combines multiple mutation rules into a single policy.
MutationPolicy = privacy.MutationPolicy
)
// EvalMutation evaluates a mutation against a mutation policy.
func (policy MutationPolicy) EvalMutation(ctx context.Context, m {{ $pkg }}.Mutation) error {
if decision, ok := decisionFromContext(ctx); ok {
return decision
}
for _, rule := range policy {
switch decision := rule.EvalMutation(ctx, m); {
case decision == nil || errors.Is(decision, Skip):
case errors.Is(decision, Allow):
return nil
default:
return decision
}
}
return nil
}
// MutationRuleFunc type is an adapter to allow the use of
// ordinary functions as mutation rules.

View File

@@ -76,6 +76,7 @@ import (
{{- end }}
"github.com/facebook/ent"
"github.com/facebook/ent/privacy"
)
@@ -91,7 +92,7 @@ func init() {
{{- end }}
{{- with $policies := $n.PolicyPositions }}
{{- /* policies defined in schema and mixins. */}}
{{ $pkg }}.Policy = newPolicy({{ range $idx := $n.MixedInPolicies }}{{ $pkg }}Mixin[{{ $idx }}],{{ end }}{{ $schema }}.{{ $n.Name }}{})
{{ $pkg }}.Policy = privacy.NewPolicies({{ range $idx := $n.MixedInPolicies }}{{ $pkg }}Mixin[{{ $idx }}],{{ end }}{{ $schema }}.{{ $n.Name }}{})
{{ $pkg }}.Hooks[0] = func(next ent.Mutator) ent.Mutator {
return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
if err := {{ $pkg }}.Policy.EvalMutation(ctx, m); err != nil {
@@ -190,39 +191,4 @@ func init() {
{{- end }}
{{- end }}
}
{{ $hasPolicy := false }}{{ range $n := $.Nodes }}{{ if $n.NumPolicy }}{{ $hasPolicy = true }}{{ end }}{{ end }}
{{ if $hasPolicy }}
type policies []ent.Policy
// newPolicy creates a policy from list of mixin and ent.Schema.
func newPolicy(ps ...interface{ Policy() ent.Policy }) ent.Policy {
pocs := make(policies, 0, len(ps))
for _, p := range ps {
if policy := p.Policy(); policy != nil {
pocs = append(pocs, policy)
}
}
return pocs
}
func (p policies) EvalMutation(ctx context.Context, m ent.Mutation) error {
for i := range p {
if err := p[i].EvalMutation(ctx, m); err != nil {
return err
}
}
return nil
}
func (p policies) EvalQuery(ctx context.Context, q ent.Query) error {
for i := range p {
if err := p[i].EvalQuery(ctx, q); err != nil {
return err
}
}
return nil
}
{{ end }}
{{ end }}