basicperms

package
v0.0.0-...-e7365f1 Latest Latest
Warning

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

Go to latest
Published: May 18, 2026 License: GPL-3.0 Imports: 18 Imported by: 0

README

basicperms

A self-contained HTTP service for Zanzibar-style relationship-based authorization. Designed to run standalone or be embedded as a library inside another Go service.

Concepts

Namespaces group subjects, objects, and relations (e.g. event, user).

Relation tuples are (namespace, subject, relation, object) quads. The stored wildcard * matches any concrete value.

Userset rewrite rules let one relation imply another:

  • computed_userset — if you hold relation A on an object, you also hold relation B (alias).
  • tuple_to_userset — follow a hop relation to a linked object, then check a second relation there (e.g. inheriting a parent folder's permissions).

Check evaluates whether a subject holds a relation on an object, following rules transitively with cycle detection.

Expand returns the full union tree of subjects who hold a relation on an object.

Embedding

store, err := basicperms.Open(ctx, "sqlite:./perms.db", "")
defer store.Close()

svc := basicperms.NewService(store, logger)
svc.RequireAuth = true   // enforce Basic-auth API key checks
mux.Mount("/perms", svc.Router())

Standalone server

DATABASE_URL=sqlite:./perms.db go run ./cmd/basicperms
Variable Default Description
DATABASE_URL — (required) sqlite:./perms.db or postgres://...
HTTP_ADDR :8752 Listen address
SCHEMA perms Postgres schema name (ignored for SQLite)
AUTH_KEY `` API key seeded into the key table on startup
MOUNT_PREFIX `` Path prefix if mounted under a subpath
LOG_LEVEL info debug, info, warn, or error

When AUTH_KEY is set, all requests must supply that key for authorization.

HTTP API

See OpenAPI spec

Documentation

Overview

Package basicperms provides a self-contained set of HTTP handlers for Zanzibar-style relationship-based authorization stored in either SQLite or Postgres.

Typical usage:

store, _ := basicperms.Open(ctx, "postgres://...", "perms")
defer store.Close()
svc := basicperms.NewService(store, nil)
mux.Mount("/perms", svc.Router())

Index

Constants

View Source
const DefaultSchema = "perms"

DefaultSchema is the default Postgres schema name when none is supplied. Ignored for SQLite.

Variables

View Source
var (
	ErrNotFound      = errors.New("not found")
	ErrDuplicate     = errors.New("duplicate")
	ErrForeignKey    = errors.New("foreign key violation")
	ErrInvalidSchema = errors.New("invalid schema name")
)

Functions

This section is empty.

Types

type CheckResponse

type CheckResponse struct {
	Allowed bool `json:"allowed"`
}

CheckResponse is the response body for GET /check.

type CreateNamespaceInput

type CreateNamespaceInput struct {
	Name string `json:"name"`
}

func (CreateNamespaceInput) Validate

func (i CreateNamespaceInput) Validate() error

type CreateObjectInput

type CreateObjectInput struct {
	Namespace string `json:"namespace"`
	ID        string `json:"id"`
}

func (CreateObjectInput) Validate

func (i CreateObjectInput) Validate() error

type CreateRelationInput

type CreateRelationInput struct {
	Namespace string `json:"namespace"`
	Subject   string `json:"subject"`
	Relation  string `json:"relation"`
	Object    string `json:"object"`
}

func (CreateRelationInput) Validate

func (i CreateRelationInput) Validate() error

type CreateRelationRuleInput

type CreateRelationRuleInput struct {
	Namespace string   `json:"namespace"`
	Relation  string   `json:"relation"`
	RuleType  RuleType `json:"rule_type"`
	Arg1      string   `json:"arg1"`
	Arg2      string   `json:"arg2,omitempty"`
}

CreateRelationRuleInput is the request body for POST /admin/rule.

func (CreateRelationRuleInput) Validate

func (i CreateRelationRuleInput) Validate() error

type CreateSubjectInput

type CreateSubjectInput struct {
	Namespace string `json:"namespace"`
	ID        string `json:"id"`
}

func (CreateSubjectInput) Validate

func (i CreateSubjectInput) Validate() error

type Dialect

type Dialect string
const (
	DialectSQLite   Dialect = "sqlite"
	DialectPostgres Dialect = "postgres"
)

type ErrorResponse

type ErrorResponse struct {
	Message string `json:"message"`
}

ErrorResponse is the JSON shape for all error responses.

type ExpandNode

type ExpandNode struct {
	Tuple    Tuple        `json:"tuple"`
	Type     NodeType     `json:"type"`
	Children []ExpandNode `json:"children"`
}

ExpandNode is a node in the expand result tree. Leaf nodes (type 0) have no meaningful children.

type Namespace

type Namespace struct {
	Name string `json:"name"`
}

Namespace is a logical permission domain.

type NodeType

type NodeType int

NodeType describes how an ExpandNode's children are combined.

const (
	NodeTypeLeaf         NodeType = 0
	NodeTypeUnion        NodeType = 1
	NodeTypeIntersection NodeType = 2
	NodeTypeExclusion    NodeType = 3
)

type Object

type Object struct {
	Namespace string `json:"namespace"`
	ID        string `json:"id"`
}

Object is a resource that permissions apply to within a namespace.

type Relation

type Relation struct {
	Namespace string `json:"namespace"`
	Subject   string `json:"subject"`
	Relation  string `json:"relation"`
	Object    string `json:"object"`
}

Relation is a subject-relation-object tuple within a namespace.

type RelationFilter

type RelationFilter struct {
	Namespace string
	Subject   string
	Relation  string
	Object    string
}

RelationFilter holds optional filters for ListRelations. Empty string means no filter on that field.

type RelationRule

type RelationRule struct {
	Namespace string   `json:"namespace"`
	Relation  string   `json:"relation"`
	RuleType  RuleType `json:"rule_type"`
	Arg1      string   `json:"arg1"`
	Arg2      string   `json:"arg2,omitempty"`
}

RelationRule is a userset rewrite rule that extends a relation with derived membership computed from other relations.

type RuleType

type RuleType string

RuleType identifies the kind of userset rewrite a RelationRule applies.

const (
	// RuleTypeComputedUserset grants access to any subject that holds Arg1
	// on the same object.
	RuleTypeComputedUserset RuleType = "computed_userset"
	// RuleTypeTupleToUserset grants access to any subject that holds Arg2 on
	// the object reached by following Arg1 from the current object.
	RuleTypeTupleToUserset RuleType = "tuple_to_userset"
)

type Service

type Service struct {
	Store  *Store
	Logger *slog.Logger

	// MountPrefix is the path prefix the service is mounted under (e.g.
	// "/perms"). Used for informational purposes; the empty string means root.
	MountPrefix string

	// RequireAuth enables Basic-auth enforcement on every request. When true,
	// callers must supply a valid key (stored in the key table) as the HTTP
	// Basic password. Defaults to false so the embedded library mode inside a
	// host application remains unaffected.
	RequireAuth bool
}

Service binds a Store to the HTTP handlers.

func NewService

func NewService(s *Store, logger *slog.Logger) *Service

NewService constructs a Service with defaults for optional fields. Pass nil for logger to use slog.Default().

func (*Service) Router

func (s *Service) Router() chi.Router

Router returns a chi.Router with all basicperms routes registered.

type Store

type Store struct {
	DB      *sql.DB
	Dialect Dialect
	Schema  string
}

Store wraps a *sql.DB and tracks the dialect and (for Postgres) the schema.

func Open

func Open(ctx context.Context, dburl, schema string) (*Store, error)

Open dials dburl, applies the schema DDL, and returns a ready Store.

func (*Store) CheckKey

func (s *Store) CheckKey(ctx context.Context, key string) (bool, error)

CheckKey reports whether key exists in the key table.

func (*Store) CheckRelation

func (s *Store) CheckRelation(ctx context.Context, namespace, subject, relation, object string) (bool, error)

CheckRelation reports whether subject holds relation on object within namespace. Stored wildcard ("*") values match any concrete value. Userset rewrite rules (computed_userset, tuple_to_userset) are followed transitively with cycle detection.

func (*Store) Close

func (s *Store) Close() error

func (*Store) CreateKey

func (s *Store) CreateKey(ctx context.Context, key string) error

CreateKey inserts key. Returns ErrDuplicate if it already exists.

func (*Store) CreateNamespace

func (s *Store) CreateNamespace(ctx context.Context, name string) (*Namespace, error)

CreateNamespace inserts a new namespace. Returns ErrDuplicate if the name already exists.

func (*Store) CreateObject

func (s *Store) CreateObject(ctx context.Context, namespace, id string) (*Object, error)

CreateObject inserts a new object into the given namespace. Returns ErrDuplicate if the (namespace, id) pair already exists, or ErrForeignKey if the namespace does not exist.

func (*Store) CreateRelation

func (s *Store) CreateRelation(ctx context.Context, namespace, subject, relation, object string) (*Relation, error)

CreateRelation inserts a new relation tuple. Returns ErrDuplicate if the tuple already exists, or ErrForeignKey if the namespace does not exist.

func (*Store) CreateRelationRule

func (s *Store) CreateRelationRule(ctx context.Context, in CreateRelationRuleInput) (*RelationRule, error)

CreateRelationRule adds a userset rewrite rule. Returns ErrDuplicate if the rule already exists, ErrForeignKey if the namespace does not exist.

func (*Store) CreateSubject

func (s *Store) CreateSubject(ctx context.Context, namespace, id string) (*Subject, error)

CreateSubject inserts a new subject into the given namespace. Returns ErrDuplicate if the (namespace, id) pair already exists, or ErrForeignKey if the namespace does not exist.

func (*Store) DeleteKey

func (s *Store) DeleteKey(ctx context.Context, key string) error

DeleteKey removes key. Returns ErrNotFound if it doesn't exist.

func (*Store) DeleteNamespace

func (s *Store) DeleteNamespace(ctx context.Context, name string) error

DeleteNamespace removes the namespace (and via FK cascade, its subjects, objects, and relations). Returns ErrNotFound if it does not exist.

func (*Store) DeleteObject

func (s *Store) DeleteObject(ctx context.Context, namespace, id string) error

DeleteObject removes the object. Returns ErrNotFound if it does not exist.

func (*Store) DeleteRelation

func (s *Store) DeleteRelation(ctx context.Context, namespace, subject, relation, object string) error

DeleteRelation removes the relation tuple. Returns ErrNotFound if it does not exist.

func (*Store) DeleteRelationRule

func (s *Store) DeleteRelationRule(ctx context.Context, namespace, relation string, ruleType RuleType, arg1, arg2 string) error

DeleteRelationRule removes a rule. Returns ErrNotFound if it does not exist.

func (*Store) DeleteSubject

func (s *Store) DeleteSubject(ctx context.Context, namespace, id string) error

DeleteSubject removes the subject. Returns ErrNotFound if it does not exist.

func (*Store) ExpandRelation

func (s *Store) ExpandRelation(ctx context.Context, namespace, object, relation string) (ExpandNode, error)

ExpandRelation returns a union tree of all subjects who hold the given relation to the given object within the namespace, following userset rewrite rules transitively. The root is a union node; its children are leaf nodes (one per unique subject found, deduplicated across all rule paths).

func (*Store) ListNamespaces

func (s *Store) ListNamespaces(ctx context.Context, q string) ([]Namespace, error)

ListNamespaces returns all namespaces whose name contains q (case-insensitive substring match). An empty q returns all namespaces.

func (*Store) ListRelationRules

func (s *Store) ListRelationRules(ctx context.Context, namespace, relation string) ([]RelationRule, error)

ListRelationRules returns rules optionally filtered by namespace and relation.

func (*Store) ListRelations

func (s *Store) ListRelations(ctx context.Context, f RelationFilter) ([]Relation, error)

ListRelations returns all relation tuples matching the provided filter. Any zero-value filter field is ignored.

func (*Store) SeedKey

func (s *Store) SeedKey(ctx context.Context, key string) error

SeedKey inserts key if it doesn't already exist. Safe to call on every startup.

type Subject

type Subject struct {
	Namespace string `json:"namespace"`
	ID        string `json:"id"`
}

Subject is an entity that can be granted permissions within a namespace.

type Tuple

type Tuple struct {
	Namespace string `json:"namespace"`
	Subject   string `json:"subject"`
	Object    string `json:"object"`
	Action    string `json:"action"`
}

Tuple is a relationship triple used in expand tree nodes. Action corresponds to what the admin API calls Relation.

Directories

Path Synopsis
cmd
basicperms command
Command basicperms runs the basicperms package as a standalone HTTP server.
Command basicperms runs the basicperms package as a standalone HTTP server.

Jump to

Keyboard shortcuts

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