fga

package
v0.264.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package fga holds OpenFGA domain helpers shared by the CLI commands and the TUI: parsing tuple shorthand and turning an authorization model into a graph.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConditionRows

func ConditionRows(c *openfga.RelationshipCondition) [][2]string

ConditionRows renders a tuple's condition as label/value pairs for detail views: a "Condition" row with the name, and a "Condition Context" row with the context as compact JSON when present. Returns nil for an unconditioned tuple.

func ExpandTree

func ExpandTree(root *ResNode, rootRef string, expand Expander, tupleset func(object, relation string) []string, maxDepth, maxNodes int)

ExpandTree recursively expands computed-userset and tuple-to-userset leaves in place, attaching each reference's own resolution subtree so nested branches appear instead of dead-end leaves — e.g. a `viewer` node that resolves through `owner` gains `owner`'s subtree (and, in turn, its users) as a child.

OpenFGA's Expand API resolves only one level, so this issues a fresh expand per referenced relation via the `expand` callback; `tupleset` lists the objects a tuple-to-userset points at. rootRef ("object#relation" of root) seeds cycle detection. maxDepth bounds recursion depth and maxNodes bounds the total number of expansions (i.e. extra API calls), so a deep or cyclic model can't fan out unbounded. Call this before MarkGranted.

func FormatContextJSON

func FormatContextJSON(ctx map[string]any) string

FormatContextJSON renders a context map as compact JSON, or "" when empty.

func FormatContextualTuple

func FormatContextualTuple(k openfga.TupleKey) string

FormatContextualTuple renders a contextual tuple in userset shorthand, with a " [conditionName]" suffix when the tuple carries a condition.

func FormatTuple

func FormatTuple(k openfga.TupleKey) string

func FormatUserset

func FormatUserset(object, relation, user string) string

FormatUserset renders a tuple's parts in userset shorthand: "object#relation@user". Taking parts rather than a typed key lets it serve both openfga.TupleKey and openfga.CheckRequestTupleKey (which lack a common interface).

func MarkGranted

func MarkGranted(root *ResNode, user string, r GrantResolver) bool

MarkGranted annotates each node with whether it grants `user`. Direct-user leaves match the user string exactly, computed usersets resolve via a Check, and tuple-to-userset leaves read the tupleset then Check the computed relation on each related object. It returns the root's grant status.

func ParseJSONObject

func ParseJSONObject(label, s string) (map[string]any, error)

ParseJSONObject parses s as a JSON object into a map, returning nil for empty input. label names the field in the error message (e.g. "--context" or "context") so both the CLI and TUI can share one implementation.

func ParseTuple

func ParseTuple(user, relation, object string) (openfga.TupleKey, error)

ParseTuple parses the canonical "user relation object" triple from three separate arguments and returns a TupleKey. Each part is validated lightly: user and object should look like "type:id" (user may also carry "#relation" or be a wildcard "type:*").

func RenderResolution

func RenderResolution(root *ResNode, user, object, relation string) string

RenderResolution draws the resolution rooted at object#relation as the playground-style node-link diagram. user is the queried user (its box is highlighted); nodes and connectors on the granting branch are tinted.

func SplitObject

func SplitObject(object string) (typ, id string)

SplitObject splits "type:id" into its type and id components.

func Triple

func Triple(args []string, userFlag, relationFlag, objectFlag string) (user, relation, object string, err error)

Triple resolves a user/relation/object triple from positional args and the --user/--relation/--object flags. Flags set the fields they name; the remaining positionals then fill the still-unset fields left to right. This means `--user user:anne viewer document:roadmap` reads the two positionals as relation and object (rather than shifting them by index). Extra positionals that can't fill an unset field are an error, so a flag and a positional never silently fight over the same field. It errors if any part is missing.

func ValidateObjectRef

func ValidateObjectRef(object string) error

ValidateObjectRef checks that object is a concrete "type:id" (no wildcard, no userset), matching ParseTuple's object rules.

func ValidateUserRef

func ValidateUserRef(user string) error

ValidateUserRef checks that user is a "type:id", a wildcard "type:*", or a userset "type:id#relation" — the same shape ParseTuple accepts for the user position — so a swapped argument yields a friendly hint, not a raw 400.

Types

type DiagramEdge

type DiagramEdge struct {
	From string `json:"from"`
	To   string `json:"to"`
	Kind string `json:"kind"`
	Via  string `json:"via"`
}

DiagramEdge is a directed dependency between two object types: type From has a relation that can be satisfied by users (or usersets) of type To. Kind is "direct" or "ttu" (tuple-to-userset / inherited). Via names the relation the dependency flows through (the relation on From for direct edges, the tupleset relation for ttu edges).

type Expander

type Expander func(object, relation string) *ResNode

Expander resolves an object#relation to its (single-level) Expand subtree, or nil when it can't be expanded (API error, or no such resolution).

type GrantResolver

type GrantResolver struct {
	Check    func(user, relation, object string) bool
	Tupleset func(object, relation string) []string
}

GrantResolver supplies the live lookups MarkGranted needs. Check reports whether `user` has `relation` on `object`. Tupleset returns the objects related to `object` via `relation` — the "user" side of matching tuples — and may be nil to skip tuple-to-userset resolution.

type Graph

type Graph struct {
	SchemaVersion string     `json:"schema_version"`
	Types         []TypeNode `json:"types"`
	// Edges are the inter-type dependencies used to draw the node-link diagram.
	Edges []DiagramEdge `json:"edges"`
}

Graph is the parsed, render-ready view of an authorization model.

func ParseModel

func ParseModel(m *openfga.AuthorizationModel) Graph

ParseModel converts an authorization model into a Graph by interpreting the relation rewrite rules and the directly-related-user-types metadata. Slices are initialized (never nil) so `--json` output serializes empty collections as [] rather than null.

func (Graph) Render

func (g Graph) Render() string

Render draws the graph as a colored tree with a legend. width is advisory.

func (Graph) RenderDiagram

func (g Graph) RenderDiagram() string

RenderDiagram draws the authorization model as a node-link diagram: one rounded card per object type (a colored header plus its relations) laid out left→right by dependency depth, with edges routed orthogonally around the cards (never through them) and colored by resolution kind. The result is a wide, multi-line string meant to be shown inside a scrollable/pannable viewport.

func (Graph) Summary

func (g Graph) Summary() string

Summary returns a one-line summary like "4 types, 11 relations".

type Relation

type Relation struct {
	Name  string         `json:"name"`
	Edges []RelationEdge `json:"edges"`
}

Relation is a single relation on a type with its resolution edges.

type RelationEdge

type RelationEdge struct {
	// Kind is "direct", "computed", or "ttu" (tuple-to-userset).
	Kind string `json:"kind"`
	// Label is a human-readable description of the edge target.
	Label string `json:"label"`
}

RelationEdge describes one resolution path for a relation.

type ResNode

type ResNode struct {
	Name     string     // the "object#relation" this node resolves
	Op       ResOp      // how Children combine (ResLeaf → no children)
	Children []*ResNode // operands for union / intersection / exclusion

	// Leaf payloads — at most one is populated when Op == ResLeaf:
	Users    []string // direct users/usersets, e.g. ["user:anne", "team:eng#member"]
	Computed string   // a computed userset, e.g. "document:roadmap#owner"
	TTUFrom  string   // tuple-to-userset: the tupleset relation, e.g. "document:x#parent"
	TTUTo    []string // the computed usersets reached through that tupleset

	Granted bool // set by MarkGranted: this node reaches the queried user
}

ResNode is one node of a Check resolution. Each node resolves a single object#relation, either as a leaf (direct users, a computed userset, or a tuple-to-userset) or as a boolean combination of child nodes.

func GrantedPath

func GrantedPath(n *ResNode) *ResNode

GrantedPath returns a pruned copy of the tree keeping only the branch(es) that reach the user — the ACL resolution path. It returns nil when nothing grants (e.g. a denied check). Call MarkGranted first.

func ParseResolution

func ParseResolution(tree map[string]any) (*ResNode, bool)

ParseResolution builds a ResNode tree from an Expand response's untyped tree (openfga.ExpandResponse.Tree). It returns false when the tree has no root.

type ResOp

type ResOp int

ResOp identifies how a resolution node combines its children.

const (
	ResLeaf         ResOp = iota // a direct-users / computed / tuple-to-userset leaf
	ResUnion                     // any child grants
	ResIntersection              // all children grant
	ResExclusion                 // base grants and subtract does not (difference)
)

type TypeNode

type TypeNode struct {
	Name      string     `json:"name"`
	Relations []Relation `json:"relations"`
}

TypeNode is one object type in the model with its relations.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL