Help us shape the future of Go! Take the 2026 Go Developer Survey

acl

package
v0.56.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package acl implements per-user / per-file&folder access control for filex.

It is the identity-driven complement to package confine. confine is the token HARD-CEILING (a token's `root:` scope + X-Filex-Root header, a single subtree) and runs as a chi middleware that rewrites request paths. acl runs AFTER, inside the handlers, keyed off the authenticated *user* (not the token) so plain cookie/session callers are filtered too — closing the gap where a session user previously saw every storage and every path.

The two compose: confine narrows to at most one subtree; acl narrows to the (possibly many, possibly none) subtrees the user was granted, and assigns a capability level (viewer/editor/owner) that is finally capped by the user's account-role ceiling.

Backwards compatible: on a storage with RBACEnabled=false, acl returns the account-role base level (user→editor, viewer→viewer, admin→owner) for every path and treats the storage as fully visible — reproducing pre-00012 behavior exactly. Grants are only consulted on RBAC-on storages.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CleanRel

func CleanRel(rel string) string

CleanRel normalizes a storage-relative path to confine-form: no leading or trailing slash, traversal collapsed, "" == root. Mirrors confine.split's rel handling so acl and confine agree on path identity.

func UserGrants added in v0.50.0

func UserGrants(ctx context.Context, store db.Store, storageID int64, u *model.User) ([]*model.FileGrant, error)

UserGrants is every grant that reaches u on one storage: their own, and those of the groups they are in (group_file_grants) that are in their tenant. A group's grant counts exactly as a grant to each member would — the highest covering level wins, and the account-role ceiling caps it.

Types

type Level

type Level int

Level is an ordered capability: None < Viewer < Editor < Owner.

const (
	LevelNone Level = iota
	LevelViewer
	LevelEditor
	LevelOwner
)

func NeedLevel added in v0.49.0

func NeedLevel(p perm.Perm) Level

NeedLevel is the path capability a file-scoped permission also requires: holding files.delete lets a user delete only where acl already lets them write. Permissions that are not about a path answer LevelNone.

The levels are the ones the handlers demanded before permissions existed: reads are viewer, every mutation is editor, sharing a public link is editor (handlers/share.go) and sharing with a person is owner (handlers/grants.go).

func ParseLevel

func ParseLevel(s string) Level

ParseLevel maps a grant level string to a Level (unknown → LevelNone).

func RoleCeiling

func RoleCeiling(role string) Level

RoleCeiling is the maximum effective level an account role may ever reach. A viewer account is capped at LevelViewer (read-only everywhere, even on RBAC-off storages and even if handed an editor/owner item grant).

func (Level) String

func (l Level) String() string

String returns the level string. LevelNone → "none" (NOT "") so the frontend can distinguish "no access" (none) from "ACL not enforced" (field absent); an empty string would be ambiguous with the unwired/dev case.

type Resolver

type Resolver struct {
	// contains filtered or unexported fields
}

Resolver loads grants from the store and builds request-scoped ACL Sets.

func New

func New(store db.Store) *Resolver

New returns a Resolver backed by store.

func (*Resolver) AttachVaults added in v0.54.0

func (r *Resolver) AttachVaults(f VaultFinder)

AttachVaults gives every Set this resolver builds - the lock views every write door hands writegate.Check (Locks), and the request sets (LoadSet) - the answer to "is this path inside a vault", so writegate refuses a write there whichever door it comes through. Called once at wiring, when vaults are on (FILEX_E2E_VAULT).

func (*Resolver) LoadSet

func (r *Resolver) LoadSet(ctx context.Context, u *model.User, s *model.Storage) (*Set, error)

LoadSet builds the ACL set for user u on storage s. For admins and RBAC-off storages it skips the grant query entirely (they never consult grants).

func (*Resolver) Locks

func (r *Resolver) Locks(ctx context.Context, storageID int64) *Set

Locks returns the live app-plugin locks of one storage as a Set that answers only Lock and LockWithin — for a write with no person behind it (a document server's save, an app's output) and for protocol sessions, whose per-session Set was loaded when the session began and does not see a lock taken after that (writegate.Check). A nil Resolver answers nil, which writegate reads as "nothing is locked".

func (*Resolver) Perms added in v0.49.0

func (r *Resolver) Perms(ctx context.Context, u *model.User) (*perm.Result, error)

Perms resolves u's per-user permissions — for the checks that are not about a path (comments, tokens, protocol access, the admin area). A nil Resolver or one without a store answers nil, which Allows reads as "not enforced".

func (*Resolver) PermsPreview added in v0.49.0

func (r *Resolver) PermsPreview(ctx context.Context, u *model.User, change func(*perm.Input)) (*perm.Result, error)

PermsPreview is Perms for a state that is not stored yet (perm.Loader. Preview): what u would hold after change. Nil, like Perms, when unwired.

func (*Resolver) VaultRoot added in v0.55.0

func (r *Resolver) VaultRoot(ctx context.Context, storageID int64, rel string) (string, bool)

VaultRoot is the attached finder's answer (AttachVaults) for a path on a storage: the vault folder rel is inside of, or is. ok is false while vaults are off, and on a nil resolver. It makes the Resolver a VaultFinder for the readers that ask about rows from many storages at once without a Set per storage (the `encrypted` stamp on Recent, Starred, search hits, task #189).

type Set

type Set struct {
	// contains filtered or unexported fields
}

Set is the resolved ACL for one (user, storage) pair for the duration of a request. Grants are batch-loaded once by LoadSet; Effective / CanSee / StorageVisible are then pure in-memory checks with no further DB access.

func (*Set) Allows added in v0.49.0

func (s *Set) Allows(p perm.Perm) bool

Allows reports whether the caller holds per-user permission p, regardless of path. Nil permissions (no loader wired — tests only) allow.

func (*Set) AllowsAt added in v0.49.0

func (s *Set) AllowsAt(rel string, p perm.Perm) bool

AllowsAt is Allows for an action on rel: the same answer, except where a permission rule limited to storages or paths (perm.Result.CanAt) matches this storage and rel. Every check that has the path should ask this one.

func (*Set) BlockedExtension added in v0.49.0

func (s *Set) BlockedExtension(rel string) string

BlockedExtension returns the extension of rel's name that a permission rule binding this caller blocks ("exe", "tar.gz"), or "". Compared on the lowercase name, so "Setup.EXE" is caught by "exe".

func (*Set) Can added in v0.49.0

func (s *Set) Can(rel string, p perm.Perm) bool

Can reports whether the caller may take action p on rel: the path level p needs (NeedLevel, which includes the app-plugin lock cap) AND the per-user permission itself.

A file type a permission rule blocks (blocked_extensions) is refused here for every action that would produce a file of that name — adding, changing or renaming/moving to it — so every door that asks Can refuses it without knowing rules exist. What such a file already there can still do is be read and deleted.

func (*Set) CanSee

func (s *Set) CanSee(rel string) bool

CanSee reports whether rel should appear in a listing for this caller: either the caller has ≥viewer on it, OR rel is a (strict or equal) ancestor of a granted path — so ancestor folders render as traversal nodes that let the user drill down to the subtree they were actually granted.

func (*Set) Ceiling

func (s *Set) Ceiling() Level

Ceiling is the account-role ceiling for this set's user.

func (*Set) Effective

func (s *Set) Effective(rel string) Level

Effective returns the caller's effective capability on a storage-relative path: the highest grant covering it (direct or inherited from an ancestor folder), capped by the account-role ceiling. Admins are always Owner; RBAC-off storages return the account-role base.

func (*Set) EffectiveIgnoringLocks

func (s *Set) EffectiveIgnoringLocks(rel string) Level

EffectiveIgnoringLocks is Effective without the app-plugin lock cap — for the one caller that may write into a locked file: a job of the plugin holding the lock.

func (*Set) Grants

func (s *Set) Grants() []*model.FileGrant

Grants returns the raw grants loaded for this set (may be nil for admins / RBAC-off storages). The permissions handler uses this for the panel.

func (*Set) Lock

func (s *Set) Lock(rel string) *model.AppPluginLock

Lock returns the live app-plugin lock on rel, or nil. Exact path only: a lock on a file does not freeze its folder (the folder can still gain siblings), and a lock on a folder freezes only the folder entry itself.

func (*Set) LockWithin

func (s *Set) LockWithin(rel string) *model.AppPluginLock

LockWithin returns a live app-plugin lock on rel itself or anywhere under it, or nil. Renaming, moving or deleting a folder takes every file inside along, so a mutation of the folder ENTRY is refused while any descendant is locked — while uploads into the folder, which touch no locked file, stay open (LockWithin is consulted by source-side mutations only; Effective is not).

func (*Set) Perms added in v0.49.0

func (s *Set) Perms() *perm.Result

Perms returns the caller's resolved permissions (nil when unwired).

func (*Set) StorageVisible

func (s *Set) StorageVisible() bool

StorageVisible reports whether the storage should appear in the caller's storage/drive list at all. Admins and RBAC-off storages are always visible; on an RBAC-on storage a non-admin needs at least one grant.

func (*Set) VaultRoot added in v0.54.0

func (s *Set) VaultRoot(rel string) (string, bool)

VaultRoot is the vault folder rel is inside of, or is - writegate.Vaults, so every lock view and request set a door hands writegate.Check refuses a write inside a vault. ok is false with no vault finder wired, and on a nil Set.

func (*Set) WhyAt added in v0.49.0

func (s *Set) WhyAt(rel string, p perm.Perm) perm.Source

WhyAt is where AllowsAt's answer came from.

type VaultFinder added in v0.54.0

type VaultFinder interface {
	VaultRoot(ctx context.Context, storageID int64, rel string) (root string, ok bool)
}

VaultFinder answers which vault folder (docs/E2E-VAULT-FORMAT.md) holds a path on a storage, or is it: internal/vaultlock.Finder.

Jump to

Keyboard shortcuts

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