Documentation
¶
Overview ¶
contract.go: Wires the organization plugin into the forge-dashboard contract surface via plugin.ContractContributor.
Package organization provides an opt-in organization management plugin for AuthSome.
When registered, this plugin contributes organization CRUD, member management, invitation workflows, team management, dashboard pages, and admin routes. Without this plugin, no organization functionality exists.
Index ¶
- Constants
- Variables
- type AcceptInvitationRequest
- type AddMemberRequest
- type AdminListOrgsRequest
- type CheckSlugRequest
- type Config
- type CreateInvitationRequest
- type CreateOrgRequest
- type CreateTeamRequest
- type DeclineInvitationRequest
- type DeleteOrgRequest
- type DeleteTeamRequest
- type GetOrgRequest
- type GetTeamRequest
- type InvitationListResponse
- type ListInvitationsRequest
- type ListMembersRequest
- type ListOrgsRequest
- type ListTeamsRequest
- type MemberListResponse
- type OrgListResponse
- type Plugin
- func (p *Plugin) AcceptInvitation(ctx context.Context, token string) (*organization.Member, error)
- func (p *Plugin) AcceptInvitationAs(ctx context.Context, token string, userID id.UserID) (*organization.Member, error)
- func (p *Plugin) AddMember(ctx context.Context, m *organization.Member) error
- func (p *Plugin) AdminListOrganizations(ctx context.Context, appID id.AppID) ([]*organization.Organization, error)
- func (p *Plugin) CreateInvitation(ctx context.Context, inv *organization.Invitation) error
- func (p *Plugin) CreateOrganization(ctx context.Context, o *organization.Organization) error
- func (p *Plugin) CreateTeam(ctx context.Context, t *organization.Team) error
- func (p *Plugin) DeclineInvitation(ctx context.Context, token string) error
- func (p *Plugin) DeleteOrganization(ctx context.Context, orgID id.OrgID) error
- func (p *Plugin) DeleteTeam(ctx context.Context, teamID id.TeamID) error
- func (p *Plugin) ExportUserData(ctx context.Context, userID id.UserID) (label string, data any, err error)
- func (p *Plugin) GetOrganization(ctx context.Context, orgID id.OrgID) (*organization.Organization, error)
- func (p *Plugin) GetTeam(ctx context.Context, teamID id.TeamID) (*organization.Team, error)
- func (p *Plugin) IsOrgSlugAvailable(ctx context.Context, appID id.AppID, slug string) (bool, error)
- func (p *Plugin) ListInvitations(ctx context.Context, orgID id.OrgID) ([]*organization.Invitation, error)
- func (p *Plugin) ListInvitationsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Invitation], error)
- func (p *Plugin) ListMembers(ctx context.Context, orgID id.OrgID) ([]*organization.Member, error)
- func (p *Plugin) ListMembersPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Member], error)
- func (p *Plugin) ListTeams(ctx context.Context, orgID id.OrgID) ([]*organization.Team, error)
- func (p *Plugin) ListTeamsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Team], error)
- func (p *Plugin) ListUserOrganizations(ctx context.Context, userID id.UserID) ([]*organization.Organization, error)
- func (p *Plugin) ListUserOrganizationsPage(ctx context.Context, userID id.UserID, opts page.Opts) (page.Page[*organization.Organization], error)
- func (p *Plugin) Name() string
- func (p *Plugin) OnInit(_ context.Context, engine plugin.Engine) error
- func (p *Plugin) RegisterContract(d *dispatcher.Dispatcher, reg contract.Registry, wreg contract.WardenRegistry, ...) error
- func (p *Plugin) RegisterRoutes(router forge.Router) error
- func (p *Plugin) RemoveMember(ctx context.Context, memberID id.MemberID) error
- func (p *Plugin) SetStore(s store.Store)
- func (p *Plugin) UpdateMemberRole(ctx context.Context, memberID id.MemberID, role organization.MemberRole) (*organization.Member, error)
- func (p *Plugin) UpdateOrganization(ctx context.Context, o *organization.Organization) error
- func (p *Plugin) UpdateTeam(ctx context.Context, t *organization.Team) error
- type RemoveMemberRequest
- type SlugAvailableResponse
- type StatusResponse
- type TeamListResponse
- type UpdateMemberRequest
- type UpdateOrgRequest
- type UpdateTeamRequest
Constants ¶
const DefaultInvitationTTL = 7 * 24 * time.Hour
DefaultInvitationTTL is how long an invitation stays acceptable when Config.InvitationTTL is left at zero.
Variables ¶
var ErrInvitationEmailMismatch = errors.New("organization: invitation was sent to a different email address")
ErrInvitationEmailMismatch is returned by AcceptInvitationAs when the signed-in user does not own the email the invitation was sent to. The HTTP layer answers it with 403 Forbidden.
var ErrInvitationExpired = errors.New("organization: invitation has expired")
ErrInvitationExpired is returned when an invitation is accepted after its ExpiresAt. The HTTP layer answers it with 410 Gone.
var ErrSlugTaken = errors.New("organization: slug already taken")
ErrSlugTaken is returned when an organization is created with a slug that another organization in the same app already holds. The HTTP layer answers it with 409 Conflict.
Functions ¶
This section is empty.
Types ¶
type AcceptInvitationRequest ¶ added in v0.0.7
type AcceptInvitationRequest struct {
Token string `json:"token" description:"Invitation token"`
}
AcceptInvitationRequest binds the body for POST /orgs/invitations/accept.
type AddMemberRequest ¶ added in v1.9.0
type AddMemberRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
UserID string `json:"user_id" description:"User identifier to add"`
Role string `json:"role,omitempty" description:"Member role (owner, admin, member)"`
}
AddMemberRequest binds path + body for POST /orgs/:orgId/members.
type AdminListOrgsRequest ¶ added in v1.9.0
type AdminListOrgsRequest struct {
AppID string `query:"app_id" description:"Application ID"`
}
AdminListOrgsRequest binds query params for GET /admin/orgs.
type CheckSlugRequest ¶ added in v1.9.0
type CheckSlugRequest struct {
AppID string `query:"app_id" description:"Application ID"`
Slug string `query:"slug" description:"Slug to check"`
}
CheckSlugRequest binds query params for GET /orgs/check-slug.
type Config ¶
type Config struct {
// PathPrefix is the HTTP path prefix for organization routes.
// Defaults to the engine's BasePath.
PathPrefix string
// InvitationTTL is how long an invitation can be accepted after it is
// created. Zero means DefaultInvitationTTL (seven days). A negative value
// turns expiry off, so invitations never lapse, which was the behaviour
// before this option existed.
InvitationTTL time.Duration
}
Config configures the organization plugin.
type CreateInvitationRequest ¶ added in v1.9.0
type CreateInvitationRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Email string `json:"email" description:"Email address to invite"`
Role string `json:"role,omitempty" description:"Member role (owner, admin, member)"`
}
CreateInvitationRequest binds path + body for POST /orgs/:orgId/invitations.
type CreateOrgRequest ¶ added in v1.9.0
type CreateOrgRequest struct {
AppID string `json:"app_id,omitempty" description:"Application ID (optional, uses default)"`
Name string `json:"name" description:"Organization name"`
Slug string `json:"slug" description:"URL-safe slug"`
Logo string `json:"logo,omitempty" description:"Organization logo URL"`
}
CreateOrgRequest binds the body for POST /orgs.
type CreateTeamRequest ¶
type CreateTeamRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Name string `json:"name" description:"Team name"`
Slug string `json:"slug" description:"URL-safe team slug"`
}
CreateTeamRequest binds path + body for POST /orgs/:orgId/teams.
type DeclineInvitationRequest ¶ added in v0.0.7
type DeclineInvitationRequest struct {
Token string `json:"token" description:"Invitation token"`
}
DeclineInvitationRequest binds the body for POST /orgs/invitations/decline.
type DeleteOrgRequest ¶ added in v1.9.0
type DeleteOrgRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
}
DeleteOrgRequest binds the path for DELETE /orgs/:orgId.
type DeleteTeamRequest ¶ added in v0.0.7
type DeleteTeamRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
TeamID string `path:"teamId" description:"Team identifier"`
}
DeleteTeamRequest binds the path for DELETE /orgs/:orgId/teams/:teamId.
type GetOrgRequest ¶ added in v1.9.0
type GetOrgRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
}
GetOrgRequest binds the path for GET /orgs/:orgId.
type GetTeamRequest ¶ added in v1.9.0
type GetTeamRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
TeamID string `path:"teamId" description:"Team identifier"`
}
GetTeamRequest binds the path for GET /orgs/:orgId/teams/:teamId.
type InvitationListResponse ¶ added in v1.9.0
type InvitationListResponse struct {
Invitations any `json:"invitations" description:"List of invitations"`
// NextCursor is set when more remain; pass it as cursor to continue.
NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}
InvitationListResponse wraps a list of invitations.
type ListInvitationsRequest ¶ added in v1.9.0
type ListInvitationsRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Limit int `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}
ListInvitationsRequest binds the path for GET /orgs/:orgId/invitations.
type ListMembersRequest ¶ added in v0.0.7
type ListMembersRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Limit int `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}
ListMembersRequest binds the path for GET /orgs/:orgId/members.
type ListOrgsRequest ¶ added in v1.9.0
type ListOrgsRequest struct {
Limit int `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}
ListOrgsRequest is an empty request for GET /orgs (user from context).
type ListTeamsRequest ¶ added in v0.0.7
type ListTeamsRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Limit int `query:"limit" description:"Page size (default 50, max 200)" optional:"true"`
Cursor string `query:"cursor" description:"Cursor from the previous page's next_cursor" optional:"true"`
}
ListTeamsRequest binds the path for GET /orgs/:orgId/teams.
type MemberListResponse ¶ added in v1.9.0
type MemberListResponse struct {
Members any `json:"members" description:"List of members"`
// NextCursor is set when more remain; pass it as cursor to continue.
NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}
MemberListResponse wraps a list of members.
type OrgListResponse ¶ added in v1.9.0
type OrgListResponse struct {
Organizations any `json:"organizations" description:"List of organizations"`
// NextCursor is set when more remain; pass it as cursor to continue.
NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}
OrgListResponse wraps a list of organizations.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is the organization management plugin.
func New ¶ added in v1.9.0
New creates a new organization plugin with optional configuration. The store is resolved automatically from the engine during OnInit.
func (*Plugin) AcceptInvitation ¶ added in v1.9.0
AcceptInvitation accepts a pending invitation by token and adds whichever account owns the invited email (primary or secondary) as a member. It does not check who is asking, so call it only from code you trust. The HTTP endpoint uses AcceptInvitationAs instead.
func (*Plugin) AcceptInvitationAs ¶ added in v1.9.0
func (p *Plugin) AcceptInvitationAs(ctx context.Context, token string, userID id.UserID) (*organization.Member, error)
AcceptInvitationAs accepts a pending invitation on behalf of userID, the signed-in caller. It succeeds only when userID owns the invited email, primary or secondary. Anyone else gets ErrInvitationEmailMismatch and the invitation stays pending for the person it was sent to.
func (*Plugin) AdminListOrganizations ¶ added in v1.9.0
func (p *Plugin) AdminListOrganizations(ctx context.Context, appID id.AppID) ([]*organization.Organization, error)
AdminListOrganizations returns all organizations for the given app.
func (*Plugin) CreateInvitation ¶ added in v1.9.0
func (p *Plugin) CreateInvitation(ctx context.Context, inv *organization.Invitation) error
CreateInvitation creates an organization invitation. An invitation with no ExpiresAt gets one from Config.InvitationTTL (seven days by default).
func (*Plugin) CreateOrganization ¶ added in v1.9.0
func (p *Plugin) CreateOrganization(ctx context.Context, o *organization.Organization) error
CreateOrganization creates a new organization and adds the creator as owner.
func (*Plugin) CreateTeam ¶ added in v1.9.0
CreateTeam creates a new team within an organization.
func (*Plugin) DeclineInvitation ¶ added in v1.9.0
DeclineInvitation declines a pending invitation by token.
func (*Plugin) DeleteOrganization ¶ added in v1.9.0
DeleteOrganization deletes an organization and all of its dependent records (members, teams, invitations) before emitting the AfterOrgDelete hook so other plugins (subscription, SCIM, …) can clean up their own org-scoped data.
The cascade is delegated to Store.DeleteOrganizationCascade, which each backend implements with its native transaction primitive (PgTx / SqliteTx / MongoTx) so a midway failure rolls back atomically. EmitAfterOrgDelete only fires once the cascade returns nil, so downstream plugins never see an event for an org that wasn't actually deleted.
Once the org is gone, every session its members hold that names it as the active org has that org cleared, the same as RemoveMember does for one member. A failure there is logged, not returned.
func (*Plugin) DeleteTeam ¶ added in v1.9.0
DeleteTeam deletes a team.
func (*Plugin) ExportUserData ¶ added in v1.9.0
func (p *Plugin) ExportUserData(ctx context.Context, userID id.UserID) (label string, data any, err error)
ExportUserData returns the user's organization data for GDPR export.
func (*Plugin) GetOrganization ¶ added in v1.9.0
func (p *Plugin) GetOrganization(ctx context.Context, orgID id.OrgID) (*organization.Organization, error)
GetOrganization returns an organization by ID.
func (*Plugin) IsOrgSlugAvailable ¶ added in v1.9.0
IsOrgSlugAvailable checks whether a slug is available for an app.
func (*Plugin) ListInvitations ¶ added in v1.9.0
func (p *Plugin) ListInvitations(ctx context.Context, orgID id.OrgID) ([]*organization.Invitation, error)
ListInvitations lists invitations for an organization.
func (*Plugin) ListInvitationsPage ¶ added in v1.9.0
func (p *Plugin) ListInvitationsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Invitation], error)
ListInvitationsPage is the bounded twin for the request path.
func (*Plugin) ListMembers ¶ added in v1.9.0
ListMembers returns all members of an organization.
func (*Plugin) ListMembersPage ¶ added in v1.9.0
func (p *Plugin) ListMembersPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Member], error)
ListMembersPage is the bounded twin for the request path.
func (*Plugin) ListTeamsPage ¶ added in v1.9.0
func (p *Plugin) ListTeamsPage(ctx context.Context, orgID id.OrgID, opts page.Opts) (page.Page[*organization.Team], error)
ListTeamsPage is the bounded twin for the request path.
func (*Plugin) ListUserOrganizations ¶ added in v1.9.0
func (p *Plugin) ListUserOrganizations(ctx context.Context, userID id.UserID) ([]*organization.Organization, error)
ListUserOrganizations returns all organizations a user belongs to.
func (*Plugin) ListUserOrganizationsPage ¶ added in v1.9.0
func (p *Plugin) ListUserOrganizationsPage(ctx context.Context, userID id.UserID, opts page.Opts) (page.Page[*organization.Organization], error)
ListUserOrganizationsPage is the bounded twin for the request path.
func (*Plugin) OnInit ¶ added in v1.9.0
OnInit captures engine capabilities for use by the plugin's service layer.
func (*Plugin) RegisterContract ¶ added in v1.9.0
func (p *Plugin) RegisterContract( d *dispatcher.Dispatcher, reg contract.Registry, wreg contract.WardenRegistry, engine plugin.Engine, ) error
func (*Plugin) RegisterRoutes ¶
RegisterRoutes registers organization management routes on a forge.Router.
func (*Plugin) RemoveMember ¶ added in v1.9.0
RemoveMember removes a member from an organization.
func (*Plugin) UpdateMemberRole ¶ added in v1.9.0
func (p *Plugin) UpdateMemberRole(ctx context.Context, memberID id.MemberID, role organization.MemberRole) (*organization.Member, error)
UpdateMemberRole updates a member's role within an organization.
func (*Plugin) UpdateOrganization ¶ added in v1.9.0
func (p *Plugin) UpdateOrganization(ctx context.Context, o *organization.Organization) error
UpdateOrganization updates an existing organization.
func (*Plugin) UpdateTeam ¶ added in v1.9.0
UpdateTeam updates an existing team.
type RemoveMemberRequest ¶ added in v0.0.7
type RemoveMemberRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
MemberID string `path:"memberId" description:"Member identifier"`
}
RemoveMemberRequest binds the path for DELETE /orgs/:orgId/members/:memberId.
type SlugAvailableResponse ¶ added in v1.9.0
type SlugAvailableResponse struct {
Available bool `json:"available" description:"Whether the slug is available"`
}
SlugAvailableResponse reports whether a slug is available.
type StatusResponse ¶
type StatusResponse struct {
Status string `json:"status" description:"Operation status"`
}
StatusResponse is a generic status response.
type TeamListResponse ¶ added in v1.9.0
type TeamListResponse struct {
Teams any `json:"teams" description:"List of teams"`
// NextCursor is set when more remain; pass it as cursor to continue.
NextCursor string `json:"next_cursor,omitempty" description:"Cursor for the next page, absent on the last"`
}
TeamListResponse wraps a list of teams.
type UpdateMemberRequest ¶
type UpdateMemberRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
MemberID string `path:"memberId" description:"Member identifier"`
Role string `json:"role" description:"New member role (owner, admin, member)"`
}
UpdateMemberRequest binds path + body for PATCH /orgs/:orgId/members/:memberId.
type UpdateOrgRequest ¶ added in v1.9.0
type UpdateOrgRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
Name *string `json:"name,omitempty" description:"Organization name"`
Logo *string `json:"logo,omitempty" description:"Organization logo URL"`
}
UpdateOrgRequest binds path + body for PATCH /orgs/:orgId.
type UpdateTeamRequest ¶
type UpdateTeamRequest struct {
OrgID string `path:"orgId" description:"Organization identifier"`
TeamID string `path:"teamId" description:"Team identifier"`
Name *string `json:"name,omitempty" description:"Team name"`
Slug *string `json:"slug,omitempty" description:"URL-safe team slug"`
}
UpdateTeamRequest binds path + body for PATCH /orgs/:orgId/teams/:teamId.