organizations

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package organizations adds organizations, memberships, and fine-grained permissions to Auth-All.

A person belongs to one or more organizations. A membership carries a role, and a role carries a set of permission statements. A route asks for one permission, and the check runs in the process of the application.

orgs := organizations.New(
    organizations.Roles(
        organizations.Role("owner", "*"),
        organizations.Role("admin", "member:*", "project:*", "billing:read"),
        organizations.Role("member", "project:read", "project:write"),
        organizations.Role("viewer", "project:read"),
    ),
    organizations.DefaultRole("member"),
    organizations.OwnerRole("owner"),
)
auth, err := authall.New(authall.WithStore(s), authall.WithPlugins(orgs))
mux.Handle("POST /projects", orgs.Require("project:write", createProject))

Every decision is default deny. A request with no active organization, a suspended membership, and an unknown permission all refuse.

Index

Constants

View Source
const (
	PermissionOrgRead     = "organization:read"
	PermissionOrgUpdate   = "organization:update"
	PermissionOrgDelete   = "organization:delete"
	PermissionMemberRead  = "member:read"
	PermissionMemberWrite = "member:write"
	PermissionMemberAdd   = "member:invite"
	PermissionRoleWrite   = "role:write"
	PermissionTeamWrite   = "team:write"
)

The permission statements that the built-in routes of Auth-All ask for. An application declares them in its roles. The example roles of the guide give "member:*" to an administrator and "*" to an owner.

View Source
const DefaultInvitationTTL = 7 * 24 * time.Hour

DefaultInvitationTTL is the lifetime of one invitation. The option InvitationTTL changes it.

View Source
const ID = "organizations"

ID is the stable plugin identifier.

Variables

View Source
var ErrNoObjectChecker = errors.New("authall/organizations: no object checker is configured. Use organizations.WithObjectChecker")

ErrNoObjectChecker reports that the application configured no checker.

Functions

This section is empty.

Types

type Active

type Active struct {
	// Organization is the active organization of the session.
	Organization *store.Organization
	// Membership is the membership of that organization.
	Membership *store.Membership
}

Active carries the active organization of one request.

func From

func From(ctx context.Context) (Active, bool)

From returns the active organization and the membership of the request context. The second result is false when no organization is active.

type CreateInput

type CreateInput struct {
	Name string
	Slug string
	// OwnerID is the first member. An empty value uses the caller.
	OwnerID string
	// Extra holds the host-owned fields of the organization.
	Extra map[string]any
}

CreateInput names one new organization.

type InvitationPage

type InvitationPage struct {
	Invitations []store.Invitation
	NextCursor  string
}

InvitationPage is one page of the invitation list.

type InviteInput

type InviteInput struct {
	OrgID string
	Email string
	Role  string
}

InviteInput names one invitation.

type MemberPage

type MemberPage struct {
	Members []store.Membership
	// NextCursor continues the list. An empty value means that no page
	// follows.
	NextCursor string
}

MemberPage is one page of the member list.

type ObjectChecker

type ObjectChecker interface {
	// Allowed reports whether the subject can run the action on the object. An
	// error denies the request.
	Allowed(ctx context.Context, q Query) (bool, error)
}

ObjectChecker answers a question about one object.

Auth-All resolves the person, the organization, and the role, and it passes them to the checker. An application that needs a relationship graph wires this to Ory Keto, SpiceDB, or OpenFGA. Auth-All holds the identity that those services lack.

Auth-All answers a role question and a permission question. It answers no per-object question of its own.

type Option

type Option func(*Plugin)

Option configures the plugin.

func AllowCustomRoles

func AllowCustomRoles(allow bool) Option

AllowCustomRoles lets an organization declare its own role at run time. A custom role never holds a permission that its creator lacks.

func DefaultRole

func DefaultRole(name string) Option

DefaultRole names the role of a new member. The default is the last declared role, which is the weakest role of a declaration that starts at the owner.

func InvitationTTL

func InvitationTTL(d time.Duration) Option

InvitationTTL sets the lifetime of one invitation. The default is 7 days.

func MaxMembers

func MaxMembers(limit int) Option

MaxMembers limits the members of one organization. The count holds the active members and the pending invitations. A value of 0 sets no limit.

func OwnerRole

func OwnerRole(name string) Option

OwnerRole names the highest role of an organization. The owner guard keeps one enabled member with this role.

func Roles

func Roles(defs ...RoleDefinition) Option

Roles declares the built-in roles of the application.

func WithObjectChecker

func WithObjectChecker(c ObjectChecker) Option

WithObjectChecker sends a per-object question to an external policy service. The option is off by default, so CanObject reports an error until the application wires a checker.

func WithPersonalOrganizations

func WithPersonalOrganizations() Option

WithPersonalOrganizations creates one organization for each new person.

The option is off by default, because many applications need none, and an automatic row surprises them.

type Plugin

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

Plugin is the organizations plugin.

func New

func New(opts ...Option) *Plugin

New returns the organizations plugin. Registration fails when the roles are empty, hold a duplicate name, or do not hold the default role and the owner role.

func (*Plugin) AcceptInvitation

func (p *Plugin) AcceptInvitation(ctx context.Context, user *store.User, token string) (*store.Membership, error)

AcceptInvitation turns one invitation into a membership.

The signed-in user must hold the normalized address of the invitation. An unknown, used, revoked, or expired invitation gives one message for all four cases, so a holder learns nothing about the token.

func (*Plugin) AddTeamMember

func (p *Plugin) AddTeamMember(ctx context.Context, actor *store.User, orgID, teamID, userID string) error

AddTeamMember puts one member of the organization in one team.

A user without a membership of the organization never joins a team.

func (*Plugin) AdminDelete

func (p *Plugin) AdminDelete(ctx context.Context, actor *store.User, orgID string) error

AdminDelete removes one organization and every row that belongs to it. An administrator of the application uses it, so it runs no membership check.

func (*Plugin) AdminList

func (p *Plugin) AdminList(ctx context.Context, limit int, cursor string) ([]store.Organization, string, error)

AdminList returns one page of every organization of the application. An administrator of the application uses it.

func (*Plugin) Can

func (p *Plugin) Can(ctx context.Context, statement string) bool

Can reports whether the active organization of the request holds the permission. It returns false when no organization is active, which is default deny.

func (*Plugin) CanObject

func (p *Plugin) CanObject(ctx context.Context, action, object string) (bool, error)

CanObject asks the external policy service about one object.

It reports an error when no checker is configured, and it denies when the checker fails. Every other outcome is the answer of the checker.

func (*Plugin) Create

func (p *Plugin) Create(ctx context.Context, actor *store.User, in CreateInput) (*store.Organization, error)

Create returns a new organization, and it makes the owner the first member.

The creator becomes a member with the configured owner role, so an organization never starts without an owner.

func (*Plugin) CreateRole

func (p *Plugin) CreateRole(ctx context.Context, actor *store.User, orgID, name string, statements []string) (*store.CustomRole, error)

CreateRole declares one role of one organization at run time.

The name never shadows a built-in role, and the new role never holds a permission that the creator lacks. The role stores its own statements, so a later change of a built-in role never widens it.

func (*Plugin) CreateTeam

func (p *Plugin) CreateTeam(ctx context.Context, actor *store.User, orgID, name, role string) (*store.Team, error)

CreateTeam declares one team of one organization.

A team name is unique inside the organization. A team can carry a role, and the effective permission set of a member is the union of the organization role and of every team role.

func (*Plugin) Delete

func (p *Plugin) Delete(ctx context.Context, actor *store.User, orgID string) error

Delete removes one organization and every row that belongs to it.

The Before hook receives the transactional store, so the application removes its own rows of the organization in the same transaction.

func (*Plugin) DeleteRole

func (p *Plugin) DeleteRole(ctx context.Context, actor *store.User, orgID, name string) error

DeleteRole removes one custom role of one organization.

func (*Plugin) DeleteTeam

func (p *Plugin) DeleteTeam(ctx context.Context, actor *store.User, orgID, teamID string) error

DeleteTeam removes one team and its team memberships. The organization memberships stay.

func (*Plugin) Get

func (p *Plugin) Get(ctx context.Context, orgID string) (*store.Organization, error)

Get returns one organization.

func (*Plugin) ID

func (p *Plugin) ID() string

ID implements plugin.Plugin.

func (*Plugin) Invite

func (p *Plugin) Invite(ctx context.Context, actor *store.User, in InviteInput) (*store.Invitation, string, error)

Invite creates one pending invitation and returns the plaintext token.

The plaintext appears one time, in the return value. The store keeps the SHA-256 digest only. Auth-All sends no message. It emits the intent, and the application sends the invitation.

func (*Plugin) KeyCredential

func (p *Plugin) KeyCredential(ctx context.Context, orgID, ownerID, keyRole string) (
	*store.Organization, *store.Membership, []string, error)

KeyCredential returns the organization credential of one API key.

The permissions are the intersection of the key permissions and the live permissions of the owner in that organization, so a demoted member keeps no stronger key. A key of an organization that the owner left authenticates nothing.

func (*Plugin) KnownRole

func (p *Plugin) KnownRole(ctx context.Context, orgID, role string) (bool, error)

KnownRole reports whether the organization holds the role.

func (*Plugin) List

func (p *Plugin) List(ctx context.Context, userID string, limit int, cursor string) ([]store.Organization, string, error)

List returns one page of the organizations of one user.

func (*Plugin) ListInvitations

func (p *Plugin) ListInvitations(ctx context.Context, orgID string, f store.InvitationFilter) (InvitationPage, error)

ListInvitations returns one page of the invitations of one organization.

func (*Plugin) ListMembers

func (p *Plugin) ListMembers(ctx context.Context, orgID string, f store.MemberFilter) (MemberPage, error)

ListMembers returns one page of the members of one organization.

func (*Plugin) ListRoles

func (p *Plugin) ListRoles(ctx context.Context, orgID string) ([]store.CustomRole, error)

ListRoles returns the custom roles of one organization.

func (*Plugin) ListTeams

func (p *Plugin) ListTeams(ctx context.Context, orgID string) ([]store.Team, error)

ListTeams returns the teams of one organization.

func (*Plugin) PermissionsOf

func (p *Plugin) PermissionsOf(role string) (permission.Set, bool)

PermissionsOf returns the permission set of a declared role. The second result reports whether the configuration declares the role.

func (*Plugin) Register

func (p *Plugin) Register(r *plugin.Registry) error

Register implements plugin.Plugin.

func (*Plugin) Remove

func (p *Plugin) Remove(ctx context.Context, actor *store.User, orgID, userID string) error

Remove deletes one membership.

The removal ends the active organization of every session of that member in that organization, so the next request of every instance refuses.

func (*Plugin) RemoveTeamMember

func (p *Plugin) RemoveTeamMember(ctx context.Context, actor *store.User, orgID, teamID, userID string) error

RemoveTeamMember takes one member out of one team. The organization membership stays.

func (*Plugin) Require

func (p *Plugin) Require(statement string, next http.Handler) http.Handler

Require protects a handler with one permission of the active organization.

A request with no principal gets 401 UNAUTHORIZED. A request with no active organization gets 403 NO_ACTIVE_ORGANIZATION. A principal whose permission set does not hold the statement gets 403 PERMISSION_DENIED.

Require panics at construction when the statement is invalid, and when no declared role holds it. A route that no role can reach is a fault of the application, and it must fail at the start.

func (*Plugin) RequireFunc

func (p *Plugin) RequireFunc(statement string, next http.HandlerFunc) http.Handler

RequireFunc is the http.HandlerFunc form of Require.

func (*Plugin) Restore

func (p *Plugin) Restore(ctx context.Context, actor *store.User, orgID, userID string) (*store.Membership, error)

Restore gives a suspended member the permissions of its role again.

func (*Plugin) RevokeInvitation

func (p *Plugin) RevokeInvitation(ctx context.Context, actor *store.User, orgID, invitationID string) error

RevokeInvitation ends one pending invitation.

func (*Plugin) RoleNames

func (p *Plugin) RoleNames() []string

RoleNames returns the declared role names, in declaration order.

func (*Plugin) SetActive

func (p *Plugin) SetActive(ctx context.Context, w http.ResponseWriter, r *http.Request, orgID string) error

SetActive writes the active organization in the session row of the caller.

The active organization lives in the session, so every instance reads it, and a revocation removes it with the session. A request value never sets it. SetActive fails when the caller holds no active membership there.

func (*Plugin) SetRole

func (p *Plugin) SetRole(ctx context.Context, actor *store.User, orgID, userID, role string) (*store.Membership, error)

SetRole changes the role of one member.

The role must be a built-in role or a custom role of that organization. The caller never grants a role that holds a permission the caller lacks, and the change never removes the last owner.

func (*Plugin) SetStatus

func (p *Plugin) SetStatus(ctx context.Context, actor *store.User, orgID, userID, status string) (*store.Membership, error)

SetStatus suspends or restores one member.

A suspended membership keeps the row, and it holds no permission. The change never suspends the last owner.

func (*Plugin) Suspend

func (p *Plugin) Suspend(ctx context.Context, actor *store.User, orgID, userID string) (*store.Membership, error)

Suspend stops every permission of one member and keeps the row.

func (*Plugin) Update

func (p *Plugin) Update(ctx context.Context, actor *store.User, orgID string, in UpdateInput) (*store.Organization, error)

Update writes the name, the slug, and the host-owned fields.

type Query

type Query struct {
	// SubjectID is the person of the request.
	SubjectID string
	// OrgID is the active organization.
	OrgID string
	// Role is the role of the membership.
	Role string
	// Permissions holds the effective statements of the member.
	Permissions []string
	// Action is the asked permission, for example "document:read".
	Action string
	// Object names the object, for example "document:abc123".
	Object string
}

Query is one per-object question.

type RoleDefinition

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

RoleDefinition is one built-in role of the application. Role builds it.

func Role

func Role(name string, statements ...string) RoleDefinition

Role declares one built-in role and its permission statements.

The declaration is code, so a review sees it and a test covers it. Role panics on an invalid statement, because a wrong declaration must fail at the start of the application and not on a request.

func (RoleDefinition) Name

func (r RoleDefinition) Name() string

Name returns the name of the role.

func (RoleDefinition) Permissions

func (r RoleDefinition) Permissions() permission.Set

Permissions returns the permission set of the role.

type UpdateInput

type UpdateInput struct {
	Name *string
	Slug *string
	// Extra holds the host-owned fields that the change writes.
	Extra map[string]any
}

UpdateInput names the changed fields of one organization. A nil field keeps the stored value.

Directories

Path Synopsis
Package permission holds the permission statements of Auth-All.
Package permission holds the permission statements of Auth-All.

Jump to

Keyboard shortcuts

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