organizations

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 23, 2026 License: MIT, MIT Imports: 15 Imported by: 0

README

GoModularAuth Organizations Plugin

gomodularauth-organizations provides multi-tenant organizations, memberships, role-based member permissions, teams, team memberships, invitations, organization metadata, ownership lifecycle, and organization-scoped authorization for GoModularAuth.

Installation

go get github.com/BladiCreator/go-modular-auth/plugins/organizations

Quick Start


Detailed guide: doc/plugins/organizations.md.

Documentation

Overview

Package organizations provides performance acceleration for organization and membership lookups using GoModularAuth's core cache adapter.

Package organizations provides multi-tenancy and organization management capabilities (reserved module).

Index

Constants

View Source
const (
	DefaultCacheTTL      = 5 * time.Minute
	DefaultInvitationTTL = 7 * 24 * time.Hour // 7 days
)

Default configurations.

View Source
const (
	ModelOrganizations           = "organizations"
	ModelOrganizationMemberships = "organization_memberships"
	ModelOrganizationTeams       = "organization_teams"
	ModelOrganizationTeamMembers = "organization_team_members"
	ModelOrganizationInvitations = "organization_invitations"
)
View Source
const (
	EventOrganizationCreated  auth.EventName = "organizations.created"
	EventOrganizationUpdated  auth.EventName = "organizations.updated"
	EventOrganizationDeleted  auth.EventName = "organizations.deleted"
	EventOwnershipTransferred auth.EventName = "organizations.ownership_transferred"
	EventMemberAdded          auth.EventName = "organizations.member_added"
	EventMemberUpdated        auth.EventName = "organizations.member_updated"
	EventMemberRemoved        auth.EventName = "organizations.member_removed"
	EventTeamCreated          auth.EventName = "organizations.team_created"
	EventTeamUpdated          auth.EventName = "organizations.team_updated"
	EventTeamDeleted          auth.EventName = "organizations.team_deleted"
	EventTeamMemberAdded      auth.EventName = "organizations.team_member_added"
	EventTeamMemberRemoved    auth.EventName = "organizations.team_member_removed"
	EventInvitationCreated    auth.EventName = "organizations.invitation_created"
	EventInvitationAccepted   auth.EventName = "organizations.invitation_accepted"
	EventInvitationRevoked    auth.EventName = "organizations.invitation_revoked"
)

Event names emitted by the organizations plugin.

View Source
const PluginID = "organizations"

PluginID identifies this plugin in the GoModularAuth runtime.

View Source
const (
	// SubNamespace is the plugin sub-namespace within GoModularAuth cache keys.
	SubNamespace = "organizations"
)

Variables

View Source
var (
	// ErrOrganizationNotFound is returned when an organization does not exist.
	ErrOrganizationNotFound = errors.New("organizations: organization not found")

	// ErrOrganizationAlreadyExists is returned when an organization with the same slug already exists.
	ErrOrganizationAlreadyExists = errors.New("organizations: organization already exists")

	// ErrMemberNotFound is returned when a user is not a member of the organization.
	ErrMemberNotFound = errors.New("organizations: member not found")

	// ErrMemberAlreadyExists is returned when a user is already a member of the organization.
	ErrMemberAlreadyExists = errors.New("organizations: member already exists")

	// ErrCannotRemoveOwner is returned when attempting to remove or demote the organization owner.
	ErrCannotRemoveOwner = errors.New("organizations: cannot remove or demote organization owner")

	// ErrUnauthorized is returned when a principal lacks required organization-scoped authorization.
	ErrUnauthorized = errors.New("organizations: unauthorized operation")

	// ErrCrossOrganizationAccess is returned when attempting an operation across tenant boundaries.
	ErrCrossOrganizationAccess = errors.New("organizations: cross-organization access forbidden")

	// ErrTeamNotFound is returned when a team does not exist.
	ErrTeamNotFound = errors.New("organizations: team not found")

	// ErrTeamAlreadyExists is returned when a team with the same slug already exists in the organization.
	ErrTeamAlreadyExists = errors.New("organizations: team already exists")

	// ErrTeamMemberNotFound is returned when a user is not a member of the team.
	ErrTeamMemberNotFound = errors.New("organizations: team member not found")

	// ErrTeamMemberAlreadyExists is returned when a user is already in the team.
	ErrTeamMemberAlreadyExists = errors.New("organizations: team member already exists")

	// ErrInvitationNotFound is returned when an invitation does not exist.
	ErrInvitationNotFound = errors.New("organizations: invitation not found")

	// ErrInvitationExpired is returned when attempting to accept an expired invitation.
	ErrInvitationExpired = errors.New("organizations: invitation expired")

	// ErrInvitationAlreadyAccepted is returned when attempting to accept an already redeemed invitation.
	ErrInvitationAlreadyAccepted = errors.New("organizations: invitation already accepted")

	// ErrInvalidInput is returned when input validation fails.
	ErrInvalidInput = errors.New("organizations: invalid input")

	// ErrStoreRequired is returned during plugin initialization when no store is provided.
	ErrStoreRequired = errors.New("organizations: store is required")
)

Functions

func ConstantTimeCompare

func ConstantTimeCompare(expectedHash, actualHash string) bool

ConstantTimeCompare verifies whether two token hashes match without timing leakage.

func GenerateID

func GenerateID(prefix string) string

GenerateID produces a cryptographically random, prefixed unique identifier.

func GenerateToken

func GenerateToken() (string, error)

GenerateToken generates a cryptographically secure 32-byte random hex token.

func HashToken

func HashToken(rawToken string) string

HashToken computes the SHA-256 hex digest of a raw token string.

func OrganizationFromContext

func OrganizationFromContext(ctx context.Context) (string, bool)

OrganizationFromContext retrieves the organization ID bound to ctx, if present.

func RegisterSchema

func RegisterSchema(s *schema.Schema) error

RegisterSchema registers all organizations-related models into the compiled schema.

func WithOrganizationContext

func WithOrganizationContext(ctx context.Context, orgID string) context.Context

WithOrganizationContext returns a new context containing the target organization ID.

Types

type AddMemberInput

type AddMemberInput struct {
	OrganizationID string `json:"organization_id"`
	UserID         string `json:"user_id"`
	Role           Role   `json:"role"`
}

AddMemberInput contains parameters for adding a member to an organization.

type Cache

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

Cache wraps the core cache adapter for organization and membership lookups.

func NewCache

func NewCache(core corecache.Cache) *Cache

NewCache creates a new Cache wrapper. If core is nil, the cache operates in no-op mode.

func (*Cache) Available

func (c *Cache) Available() bool

Available reports whether a backing cache is configured.

func (*Cache) DeleteMembership

func (c *Cache) DeleteMembership(ctx context.Context, orgID, userID string) error

DeleteMembership invalidates cached membership for an organization member.

func (*Cache) DeleteOrganization

func (c *Cache) DeleteOrganization(ctx context.Context, id, slug string) error

DeleteOrganization invalidates cache entries for an organization ID and slug.

func (*Cache) GetMembership

func (c *Cache) GetMembership(ctx context.Context, orgID, userID string) (*Membership, error)

GetMembership retrieves a membership from cache.

func (*Cache) GetOrganizationByID

func (c *Cache) GetOrganizationByID(ctx context.Context, id string) (*OrganizationRecord, error)

GetOrganizationByID retrieves an organization by ID from cache.

func (*Cache) GetOrganizationBySlug

func (c *Cache) GetOrganizationBySlug(ctx context.Context, slug string) (*OrganizationRecord, error)

GetOrganizationBySlug retrieves an organization by slug from cache.

func (*Cache) SetMembership

func (c *Cache) SetMembership(ctx context.Context, m *Membership, ttl time.Duration) error

SetMembership caches a membership record.

func (*Cache) SetOrganization

func (c *Cache) SetOrganization(ctx context.Context, org *OrganizationRecord, ttl time.Duration) error

SetOrganization caches an organization by its ID and slug.

type Capability

type Capability = Organization

Capability is an alias for Organization to support standard capability discovery conventions.

type Config

type Config struct {
	// CacheTTL controls how long organizations and memberships are cached.
	// Defaults to 5 minutes. If non-positive, caching is disabled.
	CacheTTL time.Duration

	// DefaultInvitationTTL sets the duration an invitation remains valid.
	// Defaults to 7 days.
	DefaultInvitationTTL time.Duration
}

Config configures the organizations plugin module.

type CreateInvitationInput

type CreateInvitationInput struct {
	OrganizationID string        `json:"organization_id"`
	Email          string        `json:"email"`
	Role           Role          `json:"role"`
	InviterUserID  string        `json:"inviter_user_id"`
	TTL            time.Duration `json:"ttl,omitempty"`
}

CreateInvitationInput contains parameters for inviting a user by email.

type CreateOrganizationInput

type CreateOrganizationInput struct {
	Name     string            `json:"name"`
	Slug     string            `json:"slug"`
	OwnerID  string            `json:"owner_id"`
	Metadata map[string]string `json:"metadata,omitempty"`
}

CreateOrganizationInput contains parameters for creating a new organization.

type CreateTeamInput

type CreateTeamInput struct {
	OrganizationID string `json:"organization_id"`
	Name           string `json:"name"`
	Slug           string `json:"slug"`
	Description    string `json:"description,omitempty"`
}

CreateTeamInput contains parameters for creating a new team.

type Hooks

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

Hooks provides custom interceptor hooks for organization workflows.

func NewHooks

func NewHooks() *Hooks

NewHooks creates a new Hooks container.

func (*Hooks) AfterMemberAdd

func (h *Hooks) AfterMemberAdd(fn sdk.HookFunc[*Membership])

AfterMemberAdd registers a hook executed after a member has been added.

func (*Hooks) AfterOrganizationCreate

func (h *Hooks) AfterOrganizationCreate(fn sdk.HookFunc[*OrganizationRecord])

AfterOrganizationCreate registers a hook executed after organization and owner creation commit.

func (*Hooks) BeforeMemberAdd

func (h *Hooks) BeforeMemberAdd(fn sdk.HookFunc[*AddMemberInput])

BeforeMemberAdd registers a hook executed before adding a member to an organization.

func (*Hooks) BeforeOrganizationCreate

func (h *Hooks) BeforeOrganizationCreate(fn sdk.HookFunc[*CreateOrganizationInput])

BeforeOrganizationCreate registers a hook executed prior to organization persistence.

type Invitation

type Invitation struct {
	ID             string     `json:"id"`
	OrganizationID string     `json:"organization_id"`
	Email          string     `json:"email"`
	Role           Role       `json:"role"`
	InviterUserID  string     `json:"inviter_user_id"`
	TokenHash      string     `json:"token_hash"`
	ExpiresAt      time.Time  `json:"expires_at"`
	AcceptedAt     *time.Time `json:"accepted_at,omitempty"`
	CreatedAt      time.Time  `json:"created_at"`
}

Invitation represents an invitation sent to an email address to join an organization.

func (*Invitation) Accepted

func (inv *Invitation) Accepted() bool

Accepted reports whether the invitation has already been redeemed.

func (*Invitation) Expired

func (inv *Invitation) Expired(now time.Time) bool

Expired reports whether the invitation has expired relative to the given timestamp.

type InvitationAcceptedPayload

type InvitationAcceptedPayload struct {
	OrganizationID string    `json:"organization_id"`
	InvitationID   string    `json:"invitation_id"`
	UserID         string    `json:"user_id"`
	AcceptedAt     time.Time `json:"accepted_at"`
}

InvitationAcceptedPayload describes an invitation redemption event.

type InvitationCreatedPayload

type InvitationCreatedPayload struct {
	OrganizationID string    `json:"organization_id"`
	InvitationID   string    `json:"invitation_id"`
	Email          string    `json:"email"`
	Role           Role      `json:"role"`
	InviterUserID  string    `json:"inviter_user_id"`
	ExpiresAt      time.Time `json:"expires_at"`
}

InvitationCreatedPayload describes an invitation creation event.

type InvitationRevokedPayload

type InvitationRevokedPayload struct {
	OrganizationID string `json:"organization_id"`
	InvitationID   string `json:"invitation_id"`
}

InvitationRevokedPayload describes an invitation revocation event.

type MemberAddedPayload

type MemberAddedPayload struct {
	OrganizationID string `json:"organization_id"`
	UserID         string `json:"user_id"`
	Role           Role   `json:"role"`
}

MemberAddedPayload describes a member addition event.

type MemberRemovedPayload

type MemberRemovedPayload struct {
	OrganizationID string `json:"organization_id"`
	UserID         string `json:"user_id"`
}

MemberRemovedPayload describes a member removal event.

type MemberUpdatedPayload

type MemberUpdatedPayload struct {
	OrganizationID string           `json:"organization_id"`
	UserID         string           `json:"user_id"`
	Role           Role             `json:"role"`
	Status         MembershipStatus `json:"status"`
}

MemberUpdatedPayload describes a member role or status change event.

type Membership

type Membership struct {
	ID             string           `json:"id"`
	OrganizationID string           `json:"organization_id"`
	UserID         string           `json:"user_id"`
	Role           Role             `json:"role"`
	Status         MembershipStatus `json:"status"`
	CreatedAt      time.Time        `json:"created_at"`
	UpdatedAt      time.Time        `json:"updated_at"`
}

Membership represents the association between a Core User and an Organization.

func (*Membership) Active

func (m *Membership) Active() bool

Active reports whether the membership is active.

type MembershipStatus

type MembershipStatus string

MembershipStatus indicates the lifecycle state of an organization membership.

const (
	MembershipActive    MembershipStatus = "active"
	MembershipSuspended MembershipStatus = "suspended"
)

type Module

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

Module implements auth.PluginModule using plugin-sdk.

func New

func New(opts ...Option) *Module

New creates an installable organizations PluginModule with optional configurations.

func (*Module) ID

func (m *Module) ID() string

ID returns the module's unique identifier.

func (*Module) Init

func (m *Module) Init(h *auth.Host) error

Init configures infrastructure and provides the typed Capability into GoModularAuth via plugin-sdk.

func (*Module) RegisterSchema

func (m *Module) RegisterSchema(s *schema.Schema) error

RegisterSchema contributes organizations models to the compiled schema.

type Option

type Option func(*Config)

Option configures a Config.

func WithCacheTTL

func WithCacheTTL(ttl time.Duration) Option

WithCacheTTL configures the caching TTL duration for organizations and memberships.

func WithDefaultInvitationTTL

func WithDefaultInvitationTTL(ttl time.Duration) Option

WithDefaultInvitationTTL sets the default validity duration for newly created invitations.

type Organization

type Organization interface {

	// CreateOrganization atomically creates a new Organization and adds the owner as the initial active member with RoleOwner.
	CreateOrganization(ctx context.Context, in CreateOrganizationInput) (*OrganizationRecord, error)

	// GetOrganization retrieves an organization by its unique ID.
	GetOrganization(ctx context.Context, orgID string) (*OrganizationRecord, error)

	// GetOrganizationBySlug retrieves an organization by its unique URL slug.
	GetOrganizationBySlug(ctx context.Context, slug string) (*OrganizationRecord, error)

	// UpdateOrganization modifies an existing organization's name or metadata.
	UpdateOrganization(ctx context.Context, orgID string, in UpdateOrganizationInput) (*OrganizationRecord, error)

	// DeleteOrganization deletes an organization, cascading to members, teams, and invitations.
	DeleteOrganization(ctx context.Context, orgID string) error

	// ListUserOrganizations retrieves all organizations where the specified user is an active member.
	ListUserOrganizations(ctx context.Context, userID string) ([]*OrganizationRecord, error)

	// TransferOwnership assigns the organization's ownership to another existing member.
	TransferOwnership(ctx context.Context, orgID, newOwnerUserID string) error

	// AddMember associates a Core User with the organization with a specific role.
	AddMember(ctx context.Context, in AddMemberInput) (*Membership, error)

	// GetMember retrieves a membership record for a user in an organization.
	GetMember(ctx context.Context, orgID, userID string) (*Membership, error)

	// UpdateMemberRole updates the role assigned to a member.
	UpdateMemberRole(ctx context.Context, orgID, userID string, role Role) (*Membership, error)

	// UpdateMemberStatus updates the membership status (e.g. active, suspended).
	UpdateMemberStatus(ctx context.Context, orgID, userID string, status MembershipStatus) (*Membership, error)

	// RemoveMember removes a user from an organization. Cannot remove the owner.
	RemoveMember(ctx context.Context, orgID, userID string) error

	// ListMembers retrieves all members of an organization.
	ListMembers(ctx context.Context, orgID string) ([]*Membership, error)

	// CreateTeam creates a new team within an organization.
	CreateTeam(ctx context.Context, in CreateTeamInput) (*Team, error)

	// GetTeam retrieves a team by ID within the target organization.
	GetTeam(ctx context.Context, orgID, teamID string) (*Team, error)

	// UpdateTeam updates team details within an organization.
	UpdateTeam(ctx context.Context, orgID, teamID string, in UpdateTeamInput) (*Team, error)

	// DeleteTeam removes a team and its member associations.
	DeleteTeam(ctx context.Context, orgID, teamID string) error

	// ListTeams retrieves all teams belonging to an organization.
	ListTeams(ctx context.Context, orgID string) ([]*Team, error)

	// AddTeamMember enrolls an organization member into a team.
	AddTeamMember(ctx context.Context, orgID, teamID, userID string) error

	// RemoveTeamMember removes a user from a team.
	RemoveTeamMember(ctx context.Context, orgID, teamID, userID string) error

	// ListTeamMembers retrieves all user IDs belonging to a team.
	ListTeamMembers(ctx context.Context, orgID, teamID string) ([]string, error)

	// CreateInvitation creates an invitation for an email address and returns the invitation and plaintext redemption token.
	CreateInvitation(ctx context.Context, in CreateInvitationInput) (*Invitation, string, error)

	// GetInvitation retrieves an invitation by ID within an organization.
	GetInvitation(ctx context.Context, orgID, invitationID string) (*Invitation, error)

	// ListInvitations lists all pending invitations for an organization.
	ListInvitations(ctx context.Context, orgID string) ([]*Invitation, error)

	// AcceptInvitation redeems an invitation token for a user, adding them as a member.
	AcceptInvitation(ctx context.Context, rawToken, userID string) (*Membership, error)

	// RevokeInvitation revokes an invitation before it is accepted.
	RevokeInvitation(ctx context.Context, orgID, invitationID string) error

	// Authorize checks whether the context's authenticated Core Principal has active membership with at least minRole in orgID.
	Authorize(ctx context.Context, orgID string, minRole Role) (*Membership, error)
}

Organization is the strongly typed capability provided by the organizations module. Applications access it via auth.Get[plugins.Organization](a) or auth.Get[organizations.Organization](a).

type OrganizationCreatedPayload

type OrganizationCreatedPayload struct {
	OrganizationID string    `json:"organization_id"`
	Slug           string    `json:"slug"`
	OwnerID        string    `json:"owner_id"`
	CreatedAt      time.Time `json:"created_at"`
}

OrganizationCreatedPayload describes an organization creation event.

type OrganizationDeletedPayload

type OrganizationDeletedPayload struct {
	OrganizationID string    `json:"organization_id"`
	DeletedAt      time.Time `json:"deleted_at"`
}

OrganizationDeletedPayload describes an organization deletion event.

type OrganizationRecord

type OrganizationRecord struct {
	ID        string            `json:"id"`
	Name      string            `json:"name"`
	Slug      string            `json:"slug"`
	OwnerID   string            `json:"owner_id"`
	Metadata  map[string]string `json:"metadata,omitempty"`
	CreatedAt time.Time         `json:"created_at"`
	UpdatedAt time.Time         `json:"updated_at"`
}

OrganizationRecord represents an isolated multi-tenant tenant boundary.

type OrganizationUpdatedPayload

type OrganizationUpdatedPayload struct {
	OrganizationID string    `json:"organization_id"`
	UpdatedAt      time.Time `json:"updated_at"`
}

OrganizationUpdatedPayload describes an organization modification event.

type Organizations

type Organizations = Organization

Organizations is an alias for Organization to support plural capability naming conventions.

type OwnershipTransferredPayload

type OwnershipTransferredPayload struct {
	OrganizationID string `json:"organization_id"`
	OldOwnerID     string `json:"old_owner_id"`
	NewOwnerID     string `json:"new_owner_id"`
}

OwnershipTransferredPayload describes an ownership change event.

type Role

type Role string

Role represents a member's role within an organization.

const (
	RoleOwner  Role = "owner"
	RoleAdmin  Role = "admin"
	RoleMember Role = "member"
)

func (Role) AtLeast

func (r Role) AtLeast(target Role) bool

AtLeast reports whether role r satisfies or exceeds the required target role.

func (Role) Level

func (r Role) Level() int

Level returns a numerical hierarchy level for the standard roles.

type ScopedPrincipal

type ScopedPrincipal struct {
	Principal      auth.Principal
	OrganizationID string
	Membership     *Membership
}

ScopedPrincipal binds an authenticated Core Principal with an Organization scope.

type Team

type Team struct {
	ID             string    `json:"id"`
	OrganizationID string    `json:"organization_id"`
	Name           string    `json:"name"`
	Slug           string    `json:"slug"`
	Description    string    `json:"description,omitempty"`
	CreatedAt      time.Time `json:"created_at"`
	UpdatedAt      time.Time `json:"updated_at"`
}

Team represents a logical group of members within an organization.

type TeamCreatedPayload

type TeamCreatedPayload struct {
	OrganizationID string `json:"organization_id"`
	TeamID         string `json:"team_id"`
	Slug           string `json:"slug"`
}

TeamCreatedPayload describes a team creation event.

type TeamDeletedPayload

type TeamDeletedPayload struct {
	OrganizationID string `json:"organization_id"`
	TeamID         string `json:"team_id"`
}

TeamDeletedPayload describes a team deletion event.

type TeamMemberPayload

type TeamMemberPayload struct {
	OrganizationID string `json:"organization_id"`
	TeamID         string `json:"team_id"`
	UserID         string `json:"user_id"`
}

TeamMemberPayload describes adding or removing a member from a team.

type TeamMembership

type TeamMembership struct {
	ID        string    `json:"id"`
	TeamID    string    `json:"team_id"`
	UserID    string    `json:"user_id"`
	CreatedAt time.Time `json:"created_at"`
}

TeamMembership represents a user's enrollment within a team.

type TeamUpdatedPayload

type TeamUpdatedPayload struct {
	OrganizationID string `json:"organization_id"`
	TeamID         string `json:"team_id"`
}

TeamUpdatedPayload describes a team update event.

type UpdateOrganizationInput

type UpdateOrganizationInput struct {
	Name     *string           `json:"name,omitempty"`
	Metadata map[string]string `json:"metadata,omitempty"`
}

UpdateOrganizationInput contains parameters for updating an organization.

type UpdateTeamInput

type UpdateTeamInput struct {
	Name        *string `json:"name,omitempty"`
	Description *string `json:"description,omitempty"`
}

UpdateTeamInput contains parameters for updating a team.

Directories

Path Synopsis
Package migrations contains SQL schema migrations for the organizations plugin.
Package migrations contains SQL schema migrations for the organizations plugin.
Package plugins exposes capability aliases for GoModularAuth plugins.
Package plugins exposes capability aliases for GoModularAuth plugins.

Jump to

Keyboard shortcuts

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