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
- Variables
- type Active
- type CreateInput
- type InvitationPage
- type InviteInput
- type MemberPage
- type ObjectChecker
- type Option
- func AllowCustomRoles(allow bool) Option
- func DefaultRole(name string) Option
- func InvitationTTL(d time.Duration) Option
- func MaxMembers(limit int) Option
- func OwnerRole(name string) Option
- func Roles(defs ...RoleDefinition) Option
- func WithObjectChecker(c ObjectChecker) Option
- func WithPersonalOrganizations() Option
- type Plugin
- func (p *Plugin) AcceptInvitation(ctx context.Context, user *store.User, token string) (*store.Membership, error)
- func (p *Plugin) AddTeamMember(ctx context.Context, actor *store.User, orgID, teamID, userID string) error
- func (p *Plugin) AdminDelete(ctx context.Context, actor *store.User, orgID string) error
- func (p *Plugin) AdminList(ctx context.Context, limit int, cursor string) ([]store.Organization, string, error)
- func (p *Plugin) Can(ctx context.Context, statement string) bool
- func (p *Plugin) CanObject(ctx context.Context, action, object string) (bool, error)
- func (p *Plugin) Create(ctx context.Context, actor *store.User, in CreateInput) (*store.Organization, error)
- func (p *Plugin) CreateRole(ctx context.Context, actor *store.User, orgID, name string, ...) (*store.CustomRole, error)
- func (p *Plugin) CreateTeam(ctx context.Context, actor *store.User, orgID, name, role string) (*store.Team, error)
- func (p *Plugin) Delete(ctx context.Context, actor *store.User, orgID string) error
- func (p *Plugin) DeleteRole(ctx context.Context, actor *store.User, orgID, name string) error
- func (p *Plugin) DeleteTeam(ctx context.Context, actor *store.User, orgID, teamID string) error
- func (p *Plugin) Get(ctx context.Context, orgID string) (*store.Organization, error)
- func (p *Plugin) ID() string
- func (p *Plugin) Invite(ctx context.Context, actor *store.User, in InviteInput) (*store.Invitation, string, error)
- func (p *Plugin) KeyCredential(ctx context.Context, orgID, ownerID, keyRole string) (*store.Organization, *store.Membership, []string, error)
- func (p *Plugin) KnownRole(ctx context.Context, orgID, role string) (bool, error)
- func (p *Plugin) List(ctx context.Context, userID string, limit int, cursor string) ([]store.Organization, string, error)
- func (p *Plugin) ListInvitations(ctx context.Context, orgID string, f store.InvitationFilter) (InvitationPage, error)
- func (p *Plugin) ListMembers(ctx context.Context, orgID string, f store.MemberFilter) (MemberPage, error)
- func (p *Plugin) ListRoles(ctx context.Context, orgID string) ([]store.CustomRole, error)
- func (p *Plugin) ListTeams(ctx context.Context, orgID string) ([]store.Team, error)
- func (p *Plugin) PermissionsOf(role string) (permission.Set, bool)
- func (p *Plugin) Register(r *plugin.Registry) error
- func (p *Plugin) Remove(ctx context.Context, actor *store.User, orgID, userID string) error
- func (p *Plugin) RemoveTeamMember(ctx context.Context, actor *store.User, orgID, teamID, userID string) error
- func (p *Plugin) Require(statement string, next http.Handler) http.Handler
- func (p *Plugin) RequireFunc(statement string, next http.HandlerFunc) http.Handler
- func (p *Plugin) Restore(ctx context.Context, actor *store.User, orgID, userID string) (*store.Membership, error)
- func (p *Plugin) RevokeInvitation(ctx context.Context, actor *store.User, orgID, invitationID string) error
- func (p *Plugin) RoleNames() []string
- func (p *Plugin) SetActive(ctx context.Context, w http.ResponseWriter, r *http.Request, orgID string) error
- func (p *Plugin) SetRole(ctx context.Context, actor *store.User, orgID, userID, role string) (*store.Membership, error)
- func (p *Plugin) SetStatus(ctx context.Context, actor *store.User, orgID, userID, status string) (*store.Membership, error)
- func (p *Plugin) Suspend(ctx context.Context, actor *store.User, orgID, userID string) (*store.Membership, error)
- func (p *Plugin) Update(ctx context.Context, actor *store.User, orgID string, in UpdateInput) (*store.Organization, error)
- type Query
- type RoleDefinition
- type UpdateInput
Constants ¶
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.
const DefaultInvitationTTL = 7 * 24 * time.Hour
DefaultInvitationTTL is the lifetime of one invitation. The option InvitationTTL changes it.
const ID = "organizations"
ID is the stable plugin identifier.
Variables ¶
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.
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 ¶
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 ¶
AllowCustomRoles lets an organization declare its own role at run time. A custom role never holds a permission that its creator lacks.
func DefaultRole ¶
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 ¶
InvitationTTL sets the lifetime of one invitation. The default is 7 days.
func MaxMembers ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
DeleteRole removes one custom role of one organization.
func (*Plugin) DeleteTeam ¶
DeleteTeam removes one team and its team memberships. The organization memberships stay.
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) 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) 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) Remove ¶
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 ¶
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 ¶
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) 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.
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.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package permission holds the permission statements of Auth-All.
|
Package permission holds the permission statements of Auth-All. |