summaryrefslogtreecommitdiff
path: root/vendor/github.com/authzed/zed/internal/printers
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/authzed/zed/internal/printers')
-rw-r--r--vendor/github.com/authzed/zed/internal/printers/debug.go197
-rw-r--r--vendor/github.com/authzed/zed/internal/printers/table.go26
-rw-r--r--vendor/github.com/authzed/zed/internal/printers/tree.go66
-rw-r--r--vendor/github.com/authzed/zed/internal/printers/treeprinter.go43
4 files changed, 332 insertions, 0 deletions
diff --git a/vendor/github.com/authzed/zed/internal/printers/debug.go b/vendor/github.com/authzed/zed/internal/printers/debug.go
new file mode 100644
index 0000000..2dbb9a6
--- /dev/null
+++ b/vendor/github.com/authzed/zed/internal/printers/debug.go
@@ -0,0 +1,197 @@
+package printers
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ "github.com/gookit/color"
+
+ v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
+ "github.com/authzed/spicedb/pkg/tuple"
+)
+
+// DisplayCheckTrace prints out the check trace found in the given debug message.
+func DisplayCheckTrace(checkTrace *v1.CheckDebugTrace, tp *TreePrinter, hasError bool) {
+ displayCheckTrace(checkTrace, tp, hasError, map[string]struct{}{})
+}
+
+func displayCheckTrace(checkTrace *v1.CheckDebugTrace, tp *TreePrinter, hasError bool, encountered map[string]struct{}) {
+ red := color.FgRed.Render
+ green := color.FgGreen.Render
+ cyan := color.FgCyan.Render
+ white := color.FgWhite.Render
+ faint := color.FgGray.Render
+ magenta := color.FgMagenta.Render
+ yellow := color.FgYellow.Render
+
+ orange := color.C256(166).Sprint
+ purple := color.C256(99).Sprint
+ lightgreen := color.C256(35).Sprint
+ caveatColor := color.C256(198).Sprint
+
+ hasPermission := green("✓")
+ resourceColor := white
+ permissionColor := color.FgWhite.Render
+
+ switch checkTrace.PermissionType {
+ case v1.CheckDebugTrace_PERMISSION_TYPE_PERMISSION:
+ permissionColor = lightgreen
+ case v1.CheckDebugTrace_PERMISSION_TYPE_RELATION:
+ permissionColor = orange
+ }
+
+ switch checkTrace.Result {
+ case v1.CheckDebugTrace_PERMISSIONSHIP_CONDITIONAL_PERMISSION:
+ switch checkTrace.CaveatEvaluationInfo.Result {
+ case v1.CaveatEvalInfo_RESULT_FALSE:
+ hasPermission = red("⨉")
+ resourceColor = faint
+ permissionColor = faint
+
+ case v1.CaveatEvalInfo_RESULT_MISSING_SOME_CONTEXT:
+ hasPermission = magenta("?")
+ resourceColor = faint
+ permissionColor = faint
+ }
+ case v1.CheckDebugTrace_PERMISSIONSHIP_NO_PERMISSION:
+ hasPermission = red("⨉")
+ resourceColor = faint
+ permissionColor = faint
+ case v1.CheckDebugTrace_PERMISSIONSHIP_UNSPECIFIED:
+ hasPermission = yellow("∵")
+ }
+
+ additional := ""
+ if checkTrace.GetWasCachedResult() {
+ sourceKind := ""
+ source := checkTrace.Source
+ if source != "" {
+ parts := strings.Split(source, ":")
+ if len(parts) > 0 {
+ sourceKind = parts[0]
+ }
+ }
+ switch sourceKind {
+ case "":
+ additional = cyan(" (cached)")
+
+ case "spicedb":
+ additional = cyan(" (cached by spicedb)")
+
+ case "materialize":
+ additional = purple(" (cached by materialize)")
+
+ default:
+ additional = cyan(fmt.Sprintf(" (cached by %s)", sourceKind))
+ }
+ } else if hasError && isPartOfCycle(checkTrace, map[string]struct{}{}) {
+ hasPermission = orange("!")
+ resourceColor = white
+ }
+
+ isEndOfCycle := false
+ if hasError {
+ key := cycleKey(checkTrace)
+ _, isEndOfCycle = encountered[key]
+ if isEndOfCycle {
+ additional = color.C256(166).Sprint(" (cycle)")
+ }
+ encountered[key] = struct{}{}
+ }
+
+ timing := ""
+ if checkTrace.Duration != nil {
+ timing = fmt.Sprintf(" (%s)", checkTrace.Duration.AsDuration().String())
+ }
+
+ tp = tp.Child(
+ fmt.Sprintf(
+ "%s %s:%s %s%s%s",
+ hasPermission,
+ resourceColor(checkTrace.Resource.ObjectType),
+ resourceColor(checkTrace.Resource.ObjectId),
+ permissionColor(checkTrace.Permission),
+ additional,
+ timing,
+ ),
+ )
+
+ if isEndOfCycle {
+ return
+ }
+
+ if checkTrace.GetCaveatEvaluationInfo() != nil {
+ indicator := ""
+ exprColor := color.FgWhite.Render
+ switch checkTrace.CaveatEvaluationInfo.Result {
+ case v1.CaveatEvalInfo_RESULT_FALSE:
+ indicator = red("⨉")
+ exprColor = faint
+
+ case v1.CaveatEvalInfo_RESULT_TRUE:
+ indicator = green("✓")
+
+ case v1.CaveatEvalInfo_RESULT_MISSING_SOME_CONTEXT:
+ indicator = magenta("?")
+ }
+
+ white := color.HEXStyle("fff")
+ white.SetOpts(color.Opts{color.OpItalic})
+
+ contextMap := checkTrace.CaveatEvaluationInfo.Context.AsMap()
+ caveatName := checkTrace.CaveatEvaluationInfo.CaveatName
+
+ c := tp.Child(fmt.Sprintf("%s %s %s", indicator, exprColor(checkTrace.CaveatEvaluationInfo.Expression), caveatColor(caveatName)))
+ if len(contextMap) > 0 {
+ contextJSON, _ := json.MarshalIndent(contextMap, "", " ")
+ c.Child(string(contextJSON))
+ } else {
+ if checkTrace.CaveatEvaluationInfo.Result != v1.CaveatEvalInfo_RESULT_MISSING_SOME_CONTEXT {
+ c.Child(faint("(no matching context found)"))
+ }
+ }
+
+ if checkTrace.CaveatEvaluationInfo.Result == v1.CaveatEvalInfo_RESULT_MISSING_SOME_CONTEXT {
+ c.Child(fmt.Sprintf("missing context: %s", strings.Join(checkTrace.CaveatEvaluationInfo.PartialCaveatInfo.MissingRequiredContext, ", ")))
+ }
+ }
+
+ if checkTrace.GetSubProblems() != nil {
+ for _, subProblem := range checkTrace.GetSubProblems().Traces {
+ displayCheckTrace(subProblem, tp, hasError, encountered)
+ }
+ } else if checkTrace.Result == v1.CheckDebugTrace_PERMISSIONSHIP_HAS_PERMISSION {
+ tp.Child(purple(fmt.Sprintf("%s:%s %s", checkTrace.Subject.Object.ObjectType, checkTrace.Subject.Object.ObjectId, checkTrace.Subject.OptionalRelation)))
+ }
+}
+
+func cycleKey(checkTrace *v1.CheckDebugTrace) string {
+ return fmt.Sprintf("%s#%s", tuple.V1StringObjectRef(checkTrace.Resource), checkTrace.Permission)
+}
+
+func isPartOfCycle(checkTrace *v1.CheckDebugTrace, encountered map[string]struct{}) bool {
+ if checkTrace.GetSubProblems() == nil {
+ return false
+ }
+
+ encounteredCopy := make(map[string]struct{}, len(encountered))
+ for k, v := range encountered {
+ encounteredCopy[k] = v
+ }
+
+ key := cycleKey(checkTrace)
+ if _, ok := encounteredCopy[key]; ok {
+ return true
+ }
+
+ encounteredCopy[key] = struct{}{}
+
+ for _, subProblem := range checkTrace.GetSubProblems().Traces {
+ if isPartOfCycle(subProblem, encounteredCopy) {
+ return true
+ }
+ }
+
+ return false
+}
diff --git a/vendor/github.com/authzed/zed/internal/printers/table.go b/vendor/github.com/authzed/zed/internal/printers/table.go
new file mode 100644
index 0000000..fd6f024
--- /dev/null
+++ b/vendor/github.com/authzed/zed/internal/printers/table.go
@@ -0,0 +1,26 @@
+package printers
+
+import (
+ "io"
+
+ "github.com/olekukonko/tablewriter"
+)
+
+// PrintTable writes an terminal-friendly table of the values to the target.
+func PrintTable(target io.Writer, headers []string, rows [][]string) {
+ table := tablewriter.NewWriter(target)
+ table.SetHeader(headers)
+ table.SetAutoWrapText(false)
+ table.SetAutoFormatHeaders(true)
+ table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
+ table.SetAlignment(tablewriter.ALIGN_LEFT)
+ table.SetCenterSeparator("")
+ table.SetColumnSeparator("")
+ table.SetRowSeparator("")
+ table.SetHeaderLine(false)
+ table.SetBorder(false)
+ table.SetTablePadding("\t")
+ table.SetNoWhiteSpace(true)
+ table.AppendBulk(rows)
+ table.Render()
+}
diff --git a/vendor/github.com/authzed/zed/internal/printers/tree.go b/vendor/github.com/authzed/zed/internal/printers/tree.go
new file mode 100644
index 0000000..b787210
--- /dev/null
+++ b/vendor/github.com/authzed/zed/internal/printers/tree.go
@@ -0,0 +1,66 @@
+package printers
+
+import (
+ "fmt"
+
+ "github.com/jzelinskie/stringz"
+
+ v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
+)
+
+func prettySubject(subj *v1.SubjectReference) string {
+ if subj.OptionalRelation == "" {
+ return fmt.Sprintf(
+ "%s:%s",
+ stringz.TrimPrefixIndex(subj.Object.ObjectType, "/"),
+ subj.Object.ObjectId,
+ )
+ }
+ return fmt.Sprintf(
+ "%s:%s->%s",
+ stringz.TrimPrefixIndex(subj.Object.ObjectType, "/"),
+ subj.Object.ObjectId,
+ subj.OptionalRelation,
+ )
+}
+
+// TreeNodeTree walks an Authzed Tree Node and creates corresponding nodes
+// for a treeprinter.
+func TreeNodeTree(tp *TreePrinter, treeNode *v1.PermissionRelationshipTree) {
+ if treeNode.ExpandedObject != nil {
+ tp = tp.Child(fmt.Sprintf(
+ "%s:%s->%s",
+ stringz.TrimPrefixIndex(treeNode.ExpandedObject.ObjectType, "/"),
+ treeNode.ExpandedObject.ObjectId,
+ treeNode.ExpandedRelation,
+ ))
+ }
+ switch typed := treeNode.TreeType.(type) {
+ case *v1.PermissionRelationshipTree_Intermediate:
+ switch typed.Intermediate.Operation {
+ case v1.AlgebraicSubjectSet_OPERATION_UNION:
+ union := tp.Child("union")
+ for _, child := range typed.Intermediate.Children {
+ TreeNodeTree(union, child)
+ }
+ case v1.AlgebraicSubjectSet_OPERATION_INTERSECTION:
+ intersection := tp.Child("intersection")
+ for _, child := range typed.Intermediate.Children {
+ TreeNodeTree(intersection, child)
+ }
+ case v1.AlgebraicSubjectSet_OPERATION_EXCLUSION:
+ exclusion := tp.Child("exclusion")
+ for _, child := range typed.Intermediate.Children {
+ TreeNodeTree(exclusion, child)
+ }
+ default:
+ panic("unknown expand operation")
+ }
+ case *v1.PermissionRelationshipTree_Leaf:
+ for _, subject := range typed.Leaf.Subjects {
+ tp.Child(prettySubject(subject))
+ }
+ default:
+ panic("unknown TreeNode type")
+ }
+}
diff --git a/vendor/github.com/authzed/zed/internal/printers/treeprinter.go b/vendor/github.com/authzed/zed/internal/printers/treeprinter.go
new file mode 100644
index 0000000..55c3830
--- /dev/null
+++ b/vendor/github.com/authzed/zed/internal/printers/treeprinter.go
@@ -0,0 +1,43 @@
+package printers
+
+import (
+ "strings"
+
+ "github.com/xlab/treeprint"
+
+ "github.com/authzed/zed/internal/console"
+)
+
+type TreePrinter struct {
+ tree treeprint.Tree
+}
+
+func NewTreePrinter() *TreePrinter {
+ return &TreePrinter{}
+}
+
+func (tp *TreePrinter) Child(val string) *TreePrinter {
+ if tp.tree == nil {
+ tp.tree = treeprint.NewWithRoot(val)
+ return tp
+ }
+ return &TreePrinter{tree: tp.tree.AddBranch(val)}
+}
+
+func (tp *TreePrinter) Print() {
+ console.Println(tp.String())
+}
+
+func (tp *TreePrinter) Indented() string {
+ var sb strings.Builder
+ lines := strings.Split(tp.String(), "\n")
+ for _, line := range lines {
+ sb.WriteString(" " + line + "\n")
+ }
+
+ return sb.String()
+}
+
+func (tp *TreePrinter) String() string {
+ return tp.tree.String()
+}