approle

package
v0.14.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: 14 Imported by: 0

Documentation

Overview

Package approle stores and resolves the roles principals hold in the applications of an organization (ADR-007).

An application role is one of the roles the application declares in its authz.AppVocabulary, assigned to a principal for one organization. The rules:

  • Owners hold the application's top role (admin on the standard ladder) in every entitled application, implicitly, without a stored row. Admins and members hold only what is assigned to them.
  • Nobody holds an application role by default, and an organization is entitled to no application unless the Service is given an entitlement source (WithEntitlementGrants, normally built from the marketplace by marketplace.ApproleOptions). A seat-licensed application caps how many principals may hold a role in it.
  • Only active owners, admins and members can hold a role; collaborators and non-members cannot.
  • Removing a member deletes their roles (organization.Service RemoveMember does this in its transaction). Deactivating a membership leaves the rows in place but EffectiveRoles ignores them until the membership is active again.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrUnknownApplication means the application is not registered.
	ErrUnknownApplication = errors.New("approle: unknown application")
	// ErrUnknownRole means the role is not one the application declares.
	ErrUnknownRole = errors.New("approle: role not declared by the application")
	// ErrNotEntitled means the organization is not entitled to the
	// application.
	ErrNotEntitled = errors.New("approle: organization is not entitled to the application")
	// ErrNotMember means the principal has no active owner, admin or
	// member membership in the organization.
	ErrNotMember = errors.New("approle: principal is not an active owner, admin or member")
	// ErrNoSeats means every seat licensed for the application is already
	// held by a principal with a role in it.
	ErrNoSeats = errors.New("approle: no seats available for the application")
	// ErrAlreadyAssigned means the principal already has a role in the
	// application; use Change.
	ErrAlreadyAssigned = errors.New("approle: role already assigned")
	// ErrNotAssigned means the principal has no stored role in the
	// application.
	ErrNotAssigned = errors.New("approle: role not assigned")
)

Errors returned by the service.

Functions

func TopRole

func TopRole(v authz.AppVocabulary) string

TopRole returns the highest role of a vocabulary: "admin" if it declares one, otherwise the role with the highest hierarchy level (ties broken by name for determinism).

Types

type AppRole

type AppRole struct {
	ID             uuid.UUID
	PrincipalID    uuid.UUID
	OrganizationID uuid.UUID
	Application    string
	Role           string
	AssignedBy     *uuid.UUID
}

AppRole is a principal's stored role in one application of an organization.

type AssignInput

type AssignInput struct {
	OrganizationID uuid.UUID
	PrincipalID    uuid.UUID
	Application    string
	Role           string
	// AssignedBy is the principal making the change. It may equal
	// PrincipalID: assigning a role to oneself is allowed and logged.
	AssignedBy uuid.UUID
}

AssignInput identifies a role to assign or change.

type EntitlementFunc

type EntitlementFunc func(ctx context.Context, organizationID uuid.UUID, application string) (bool, error)

EntitlementFunc reports whether an organization is entitled to an application.

type Grant

type Grant struct {
	// SeatLimit caps the number of principals holding a role in the
	// application. Nil means unlimited.
	SeatLimit *int
}

Grant describes an organization's entitlement to one application.

type GrantsFunc

type GrantsFunc func(ctx context.Context, organizationID uuid.UUID) (map[string]Grant, error)

GrantsFunc returns the applications an organization is entitled to, keyed by application identifier. One call answers for every application, so it is cheaper than an EntitlementFunc per application.

type Option

type Option func(*Service)

Option configures a Service.

func EntitleAll

func EntitleAll(registry *authz.VocabularyRegistry) Option

EntitleAll treats every registered application as entitled to every organization, with unlimited seats. It is for development and tests; without any entitlement option no organization is entitled to anything.

func WithAccessChangeSink

func WithAccessChangeSink(sink accesschange.Sink) Option

WithAccessChangeSink announces the access changes the service records, a changed or removed application role, to sink after the transaction that recorded them commits (ADR-010). The records are written whether or not a sink is set.

func WithAuthzSyncer

func WithAuthzSyncer(syncer authz.RelationshipSyncer, mode authz.SyncMode) Option

WithAuthzSyncer writes application roles to the authorization graph when syncer also implements authz.AppRoleSyncer; a syncer that does not is ignored, so any existing authz.RelationshipSyncer can be passed. In authz.SyncModeStrict a failed write is returned to the caller after the role was stored; in SyncModeEventual (the default) it is logged.

func WithEntitlementGrants

func WithEntitlementGrants(f GrantsFunc) Option

WithEntitlementGrants sets the source of an organization's entitled applications and their seat limits. marketplace.ApproleOptions builds one from active subscriptions, licenses and the deployment's default entitlements.

func WithEntitlements

func WithEntitlements(f EntitlementFunc) Option

WithEntitlements restricts roles to the applications f reports the organization is entitled to. It carries no seat limits; prefer WithEntitlementGrants, which takes precedence when both are set.

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger sets the logger that records assignments.

type Service

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

Service manages application roles.

func NewService

func NewService(client *ent.Client, registry *authz.VocabularyRegistry, opts ...Option) *Service

NewService returns a Service that validates roles against the vocabularies in registry.

func (*Service) Assign

func (s *Service) Assign(ctx context.Context, in AssignInput) (*AppRole, error)

Assign gives the principal a role in an application. It fails with ErrAlreadyAssigned if the principal already has one. When the application has a seat limit, counting the seats and inserting the role happen under a lock, so concurrent assignments cannot exceed the limit.

func (*Service) AssignInTx

func (s *Service) AssignInTx(ctx context.Context, tx *ent.Tx, in AssignInput) (*AppRole, func(context.Context) error, error)

AssignInTx gives the principal a role inside the caller's transaction, for flows that create the membership and its roles together, such as accepting an invitation. The principal's membership may be created in tx, uncommitted. Seat limits are enforced under the same lock Assign takes. It returns a function to call after tx commits, which audits the assignment and writes it to the authorization graph; its error is the sync error Assign would return.

func (*Service) Change

func (s *Service) Change(ctx context.Context, in AssignInput) (*AppRole, error)

Change sets a different role for a principal who already has one. It fails with ErrNotAssigned if there is none.

func (*Service) EffectiveRoles

func (s *Service) EffectiveRoles(ctx context.Context, principalID, organizationID uuid.UUID) (map[string]string, error)

EffectiveRoles returns the principal's role in each application of the organization, keyed by application. An application absent from the map is one the principal may not open. A non-member, an inactive member and a collaborator get an empty map. An owner holds the top role of every entitled registered application; anyone else holds their stored roles for entitled applications. If both apply, the higher role wins.

func (*Service) EntitledApplications

func (s *Service) EntitledApplications(ctx context.Context, organizationID uuid.UUID) ([]string, error)

EntitledApplications lists the registered applications the organization is entitled to, sorted. A role picker offers these.

func (*Service) ListForOrganization

func (s *Service) ListForOrganization(ctx context.Context, organizationID uuid.UUID, application string) ([]*AppRole, error)

ListForOrganization lists the stored roles in an organization, ordered by application. A non-empty application limits the list to it.

func (*Service) ListForPrincipal

func (s *Service) ListForPrincipal(ctx context.Context, organizationID, principalID uuid.UUID) ([]*AppRole, error)

ListForPrincipal lists the stored roles a principal holds in an organization, ordered by application. Implicit owner roles are not included; see EffectiveRoles.

func (*Service) Remove

func (s *Service) Remove(ctx context.Context, organizationID, principalID uuid.UUID, application string, removedBy uuid.UUID) error

Remove deletes the principal's role in an application. It fails with ErrNotAssigned if there is none.

func (*Service) RemoveForMember

func (s *Service) RemoveForMember(ctx context.Context, organizationID, principalID uuid.UUID) (int, error)

RemoveForMember deletes every role the principal holds in the organization and reports how many it deleted. organization.Service does the same inside its RemoveMember transaction; this is for callers that remove access another way.

func (*Service) Roles

func (s *Service) Roles(application string) ([]string, error)

Roles returns the roles an application declares, lowest first, for role pickers.

func (*Service) ValidateRoles

func (s *Service) ValidateRoles(ctx context.Context, organizationID uuid.UUID, roles map[string]string) error

ValidateRoles checks roles, a map from application identifier to role, against the organization: each application must be registered, declare the role, and be one the organization is entitled to. It does not check membership or seats, so it can validate the roles of an invitation before the invitee is a member.

Jump to

Keyboard shortcuts

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