diff options
| author | mo khan <mo@mokhan.ca> | 2025-07-22 17:35:49 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2025-07-22 17:35:49 -0600 |
| commit | 20ef0d92694465ac86b550df139e8366a0a2b4fa (patch) | |
| tree | 3f14589e1ce6eb9306a3af31c3a1f9e1af5ed637 /vendor/github.com/authzed/spicedb/pkg/development/warnings.go | |
| parent | 44e0d272c040cdc53a98b9f1dc58ae7da67752e6 (diff) | |
feat: connect to spicedb
Diffstat (limited to 'vendor/github.com/authzed/spicedb/pkg/development/warnings.go')
| -rw-r--r-- | vendor/github.com/authzed/spicedb/pkg/development/warnings.go | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/vendor/github.com/authzed/spicedb/pkg/development/warnings.go b/vendor/github.com/authzed/spicedb/pkg/development/warnings.go new file mode 100644 index 0000000..6be9a1c --- /dev/null +++ b/vendor/github.com/authzed/spicedb/pkg/development/warnings.go @@ -0,0 +1,309 @@ +package development + +import ( + "context" + "fmt" + + "github.com/ccoveille/go-safecast" + + log "github.com/authzed/spicedb/internal/logging" + "github.com/authzed/spicedb/pkg/namespace" + corev1 "github.com/authzed/spicedb/pkg/proto/core/v1" + devinterface "github.com/authzed/spicedb/pkg/proto/developer/v1" + "github.com/authzed/spicedb/pkg/schema" + "github.com/authzed/spicedb/pkg/spiceerrors" +) + +var allChecks = checks{ + relationChecks: []relationCheck{ + lintRelationReferencesParentType, + }, + computedUsersetChecks: []computedUsersetCheck{ + lintPermissionReferencingItself, + }, + ttuChecks: []ttuCheck{ + lintArrowReferencingRelation, + lintArrowReferencingUnreachable, + lintArrowOverSubRelation, + }, +} + +func warningForMetadata(warningName string, message string, sourceCode string, metadata namespace.WithSourcePosition) *devinterface.DeveloperWarning { + return warningForPosition(warningName, message, sourceCode, metadata.GetSourcePosition()) +} + +func warningForPosition(warningName string, message string, sourceCode string, sourcePosition *corev1.SourcePosition) *devinterface.DeveloperWarning { + if sourcePosition == nil { + return &devinterface.DeveloperWarning{ + Message: message, + SourceCode: sourceCode, + } + } + + // NOTE: zeroes on failure are fine here. + lineNumber, err := safecast.ToUint32(sourcePosition.ZeroIndexedLineNumber) + if err != nil { + log.Err(err).Msg("could not cast lineNumber to uint32") + } + columnNumber, err := safecast.ToUint32(sourcePosition.ZeroIndexedColumnPosition) + if err != nil { + log.Err(err).Msg("could not cast columnPosition to uint32") + } + + return &devinterface.DeveloperWarning{ + Message: message + " (" + warningName + ")", + Line: lineNumber + 1, + Column: columnNumber + 1, + SourceCode: sourceCode, + } +} + +// GetWarnings returns a list of warnings for the given developer context. +func GetWarnings(ctx context.Context, devCtx *DevContext) ([]*devinterface.DeveloperWarning, error) { + warnings := []*devinterface.DeveloperWarning{} + res := schema.ResolverForCompiledSchema(*devCtx.CompiledSchema) + ts := schema.NewTypeSystem(res) + + for _, def := range devCtx.CompiledSchema.ObjectDefinitions { + found, err := addDefinitionWarnings(ctx, def, ts) + if err != nil { + return nil, err + } + warnings = append(warnings, found...) + } + + return warnings, nil +} + +type contextKey string + +var relationKey = contextKey("relation") + +func addDefinitionWarnings(ctx context.Context, nsDef *corev1.NamespaceDefinition, ts *schema.TypeSystem) ([]*devinterface.DeveloperWarning, error) { + def, err := schema.NewDefinition(ts, nsDef) + if err != nil { + return nil, err + } + + warnings := []*devinterface.DeveloperWarning{} + for _, rel := range nsDef.Relation { + ctx = context.WithValue(ctx, relationKey, rel) + + for _, check := range allChecks.relationChecks { + if shouldSkipCheck(rel.Metadata, check.name) { + continue + } + + checkerWarning, err := check.fn(ctx, rel, def) + if err != nil { + return nil, err + } + + if checkerWarning != nil { + warnings = append(warnings, checkerWarning) + } + } + + if def.IsPermission(rel.Name) { + found, err := walkUsersetRewrite(ctx, rel.UsersetRewrite, rel, allChecks, def) + if err != nil { + return nil, err + } + + warnings = append(warnings, found...) + } + } + + return warnings, nil +} + +func shouldSkipCheck(metadata *corev1.Metadata, name string) bool { + if metadata == nil { + return false + } + + comments := namespace.GetComments(metadata) + for _, comment := range comments { + if comment == "// spicedb-ignore-warning: "+name { + return true + } + } + + return false +} + +type tupleset interface { + GetRelation() string +} + +type ttu interface { + GetTupleset() tupleset + GetComputedUserset() *corev1.ComputedUserset + GetArrowString() (string, error) +} + +type ( + relationChecker func(ctx context.Context, relation *corev1.Relation, def *schema.Definition) (*devinterface.DeveloperWarning, error) + computedUsersetChecker func(ctx context.Context, computedUserset *corev1.ComputedUserset, sourcePosition *corev1.SourcePosition, def *schema.Definition) (*devinterface.DeveloperWarning, error) + ttuChecker func(ctx context.Context, ttu ttu, sourcePosition *corev1.SourcePosition, def *schema.Definition) (*devinterface.DeveloperWarning, error) +) + +type relationCheck struct { + name string + fn relationChecker +} + +type computedUsersetCheck struct { + name string + fn computedUsersetChecker +} + +type ttuCheck struct { + name string + fn ttuChecker +} + +type checks struct { + relationChecks []relationCheck + computedUsersetChecks []computedUsersetCheck + ttuChecks []ttuCheck +} + +func walkUsersetRewrite(ctx context.Context, rewrite *corev1.UsersetRewrite, relation *corev1.Relation, checks checks, def *schema.Definition) ([]*devinterface.DeveloperWarning, error) { + if rewrite == nil { + return nil, nil + } + + switch t := (rewrite.RewriteOperation).(type) { + case *corev1.UsersetRewrite_Union: + return walkUsersetOperations(ctx, t.Union.Child, relation, checks, def) + + case *corev1.UsersetRewrite_Intersection: + return walkUsersetOperations(ctx, t.Intersection.Child, relation, checks, def) + + case *corev1.UsersetRewrite_Exclusion: + return walkUsersetOperations(ctx, t.Exclusion.Child, relation, checks, def) + + default: + return nil, spiceerrors.MustBugf("unexpected rewrite operation type %T", t) + } +} + +func walkUsersetOperations(ctx context.Context, ops []*corev1.SetOperation_Child, relation *corev1.Relation, checks checks, def *schema.Definition) ([]*devinterface.DeveloperWarning, error) { + warnings := []*devinterface.DeveloperWarning{} + for _, op := range ops { + switch t := op.ChildType.(type) { + case *corev1.SetOperation_Child_XThis: + continue + + case *corev1.SetOperation_Child_ComputedUserset: + for _, check := range checks.computedUsersetChecks { + if shouldSkipCheck(relation.Metadata, check.name) { + continue + } + + checkerWarning, err := check.fn(ctx, t.ComputedUserset, op.SourcePosition, def) + if err != nil { + return nil, err + } + + if checkerWarning != nil { + warnings = append(warnings, checkerWarning) + } + } + + case *corev1.SetOperation_Child_UsersetRewrite: + found, err := walkUsersetRewrite(ctx, t.UsersetRewrite, relation, checks, def) + if err != nil { + return nil, err + } + + warnings = append(warnings, found...) + + case *corev1.SetOperation_Child_FunctionedTupleToUserset: + for _, check := range checks.ttuChecks { + if shouldSkipCheck(relation.Metadata, check.name) { + continue + } + + checkerWarning, err := check.fn(ctx, wrappedFunctionedTTU{t.FunctionedTupleToUserset}, op.SourcePosition, def) + if err != nil { + return nil, err + } + + if checkerWarning != nil { + warnings = append(warnings, checkerWarning) + } + } + + case *corev1.SetOperation_Child_TupleToUserset: + for _, check := range checks.ttuChecks { + if shouldSkipCheck(relation.Metadata, check.name) { + continue + } + + checkerWarning, err := check.fn(ctx, wrappedTTU{t.TupleToUserset}, op.SourcePosition, def) + if err != nil { + return nil, err + } + + if checkerWarning != nil { + warnings = append(warnings, checkerWarning) + } + } + + case *corev1.SetOperation_Child_XNil: + continue + + default: + return nil, spiceerrors.MustBugf("unexpected set operation type %T", t) + } + } + + return warnings, nil +} + +type wrappedFunctionedTTU struct { + *corev1.FunctionedTupleToUserset +} + +func (wfttu wrappedFunctionedTTU) GetTupleset() tupleset { + return wfttu.FunctionedTupleToUserset.GetTupleset() +} + +func (wfttu wrappedFunctionedTTU) GetComputedUserset() *corev1.ComputedUserset { + return wfttu.FunctionedTupleToUserset.GetComputedUserset() +} + +func (wfttu wrappedFunctionedTTU) GetArrowString() (string, error) { + var functionName string + switch wfttu.Function { + case corev1.FunctionedTupleToUserset_FUNCTION_ANY: + functionName = "any" + + case corev1.FunctionedTupleToUserset_FUNCTION_ALL: + functionName = "all" + + default: + return "", spiceerrors.MustBugf("unknown function type %T", wfttu.Function) + } + + return fmt.Sprintf("%s.%s(%s)", wfttu.GetTupleset().GetRelation(), functionName, wfttu.GetComputedUserset().GetRelation()), nil +} + +type wrappedTTU struct { + *corev1.TupleToUserset +} + +func (wtu wrappedTTU) GetTupleset() tupleset { + return wtu.TupleToUserset.GetTupleset() +} + +func (wtu wrappedTTU) GetComputedUserset() *corev1.ComputedUserset { + return wtu.TupleToUserset.GetComputedUserset() +} + +func (wtu wrappedTTU) GetArrowString() (string, error) { + arrowString := fmt.Sprintf("%s->%s", wtu.GetTupleset().GetRelation(), wtu.GetComputedUserset().GetRelation()) + return arrowString, nil +} |
