Documentation
¶
Overview ¶
Package organization provides organization management services.
Index ¶
- Constants
- Variables
- func IsPersonal(ctx context.Context, client *ent.Client, orgID uuid.UUID) (bool, error)
- func IsReservedSlug(slug string) bool
- func IsValidSlug(slug string) bool
- func MembershipRoleGetter(svc Service) func(ctx context.Context, principalID, orgID uuid.UUID) (string, error)
- func RequireMembersAllowed(ctx context.Context, client *ent.Client, orgID uuid.UUID, role string) error
- func Slugify(name string) string
- func ValidRole(role string) bool
- type API
- type APIConfig
- type AddMemberInput
- type AddMemberRequest
- type AddMemberResponse
- type AfterCommit
- type AppRoleResponse
- type CheckSlugInput
- type CheckSlugOutput
- type ConvertResult
- type CreateOrgInput
- type CreateOrgOutput
- type CreateOrganizationInput
- type CreatePersonalOrgInput
- type DefaultService
- func (s *DefaultService) AddMember(ctx context.Context, input AddMemberInput) (*Membership, error)
- func (s *DefaultService) ConvertToTeam(ctx context.Context, orgID uuid.UUID, teamSlug string, actor uuid.UUID) (*ConvertResult, error)
- func (s *DefaultService) Create(ctx context.Context, input CreateOrganizationInput) (*Organization, error)
- func (s *DefaultService) CreatePersonalOrg(ctx context.Context, input CreatePersonalOrgInput) (*Organization, error)
- func (s *DefaultService) CreatePersonalOrgInTx(ctx context.Context, tx *ent.Tx, input CreatePersonalOrgInput) (*Organization, *Membership, AfterCommit, error)
- func (s *DefaultService) Delete(ctx context.Context, id uuid.UUID) error
- func (s *DefaultService) GenerateSlug(ctx context.Context, name string) (string, error)
- func (s *DefaultService) GetByID(ctx context.Context, id uuid.UUID) (*Organization, error)
- func (s *DefaultService) GetBySlug(ctx context.Context, slug string) (*Organization, error)
- func (s *DefaultService) GetMembership(ctx context.Context, organizationID, principalID uuid.UUID) (*Membership, error)
- func (s *DefaultService) GetPersonalOrg(ctx context.Context, principalID uuid.UUID) (*Organization, error)
- func (s *DefaultService) List(ctx context.Context, input ListOrganizationsInput) ([]*Organization, error)
- func (s *DefaultService) ListMembers(ctx context.Context, organizationID uuid.UUID, opts ...ListOption) ([]*Membership, error)
- func (s *DefaultService) ListMemberships(ctx context.Context, principalID uuid.UUID, opts ...ListOption) ([]*Membership, error)
- func (s *DefaultService) RemoveMember(ctx context.Context, organizationID, principalID uuid.UUID) error
- func (s *DefaultService) Rename(ctx context.Context, orgID uuid.UUID, slug string, actor uuid.UUID) error
- func (s *DefaultService) ResolveSlug(ctx context.Context, slug string) (*Organization, bool, error)
- func (s *DefaultService) SlugAvailable(ctx context.Context, slug string) (bool, error)
- func (s *DefaultService) TransferOwnership(ctx context.Context, input TransferOwnershipInput) error
- func (s *DefaultService) Update(ctx context.Context, id uuid.UUID, input UpdateOrganizationInput) (*Organization, error)
- func (s *DefaultService) UpdateMember(ctx context.Context, membershipID uuid.UUID, input UpdateMemberInput) (*Membership, error)
- type DeleteOrgInput
- type Entitlement
- type EntitlementSource
- type GetMemberAppRolesInput
- type GetMemberAppRolesOutput
- type GetMemberInput
- type GetMemberOutput
- type GetOrgInput
- type GetOrgOutput
- type ListAppRolesInput
- type ListAppRolesOutput
- type ListMembersInput
- type ListMembersOutput
- type ListOption
- type ListOrganizationsInput
- type ListOrgsInput
- type ListOrgsOutput
- type MemberResponse
- type Membership
- type OrgResponse
- type OrgType
- type Organization
- type PersonalOrgTxCreator
- type RemoveAppRoleInput
- type RemoveMemberInput
- type Service
- type ServiceOption
- func WithAccessChangeSink(sink accesschange.Sink) ServiceOption
- func WithAuthzSyncer(syncer authz.RelationshipSyncer) ServiceOption
- func WithLogger(logger *slog.Logger) ServiceOption
- func WithReservedSlugs(slugs ...string) ServiceOption
- func WithSlugCoolingOff(d time.Duration) ServiceOption
- func WithSyncMode(mode authz.SyncMode) ServiceOption
- type SetAppRoleInput
- type SetAppRoleOutput
- type TransferOwnershipInput
- type TransferOwnershipRequest
- type TransferOwnershipResponse
- type UpdateMemberInput
- type UpdateMemberRequest
- type UpdateMemberResponse
- type UpdateOrgInput
- type UpdateOrgOutput
- type UpdateOrganizationInput
Constants ¶
const ( EntitlementSourceSubscription = "subscription" EntitlementSourceLicense = "license" // EntitlementSourceDefault marks an application every organization of // the deployment is entitled to, by configuration. EntitlementSourceDefault = "default" )
Entitlement sources.
const ( RoleOwner = "owner" RoleAdmin = "admin" RoleMember = "member" )
MembershipRole constants for common roles.
const RoleCollaborator = "collaborator"
RoleCollaborator is the role of a principal granted access to individual resources rather than membership of the organization (ADR-008). It is the only role a personal organization accepts besides its owner.
const SlugCoolingOff = 7 * 24 * time.Hour
SlugCoolingOff is how long a slug an organization gave up stays reserved for the principal who released it (ADR-006 decision 4). Override per service with WithSlugCoolingOff.
Variables ¶
var ( // ErrSlugCoolingOff is returned when a slug is held by a reservation // that belongs to someone else. ErrSlugCoolingOff = errors.New("slug is in its cooling-off period") // ErrSlugTaken is returned when another organization uses the slug. ErrSlugTaken = errors.New("slug is already taken") // ErrInvalidSlug is returned for a slug that is not well formed. ErrInvalidSlug = errors.New("invalid slug format: must be lowercase alphanumeric with hyphens") // ErrNotOwner is returned when the acting principal is not an active // owner of the organization. ErrNotOwner = errors.New("principal is not an owner of the organization") // ErrNotPersonal is returned when an operation needs a personal // organization and the organization is not one. ErrNotPersonal = errors.New("organization is not a personal organization") // ErrOrganizationNotFound is returned when an organization does not exist. ErrOrganizationNotFound = errors.New("organization not found") )
var ErrInvalidRole = errors.New("invalid organization role")
ErrInvalidRole is returned for a membership role that is not an organization role. The organization roles are owner, admin, member and collaborator; editor and viewer were retired (ADR-008), and finer access to one resource is a resource role granted through the authorization provider.
var ErrMembershipNotFound = errors.New("membership not found")
ErrMembershipNotFound is returned when a principal has no membership, active or not, in an organization.
var ErrPersonalOrganization = errors.New("operation not allowed on a personal organization")
ErrPersonalOrganization is returned when an operation would give a personal organization a second member or hand it to someone else. A personal organization has exactly one member, its owner (ADR-006 decision 3), and collaborators.
var ErrSlugReserved = errors.New("slug is reserved")
ErrSlugReserved is returned when a slug is on the reserved list: a name that would collide with a route, a protocol path or a product name.
var ReservedSlugs = []string{
"about", "admin", "api", "help", "invites", "login", "logout", "new",
"o", "oauth", "organizations", "scim", "settings", "support",
"well-known", "www",
}
ReservedSlugs is the built-in list of slugs no organization may take. Deployments add their own product names with WithReservedSlugs. Entries shorter than the three-character minimum are kept so the list still documents the names that are refused.
Functions ¶
func IsPersonal ¶ added in v0.14.0
IsPersonal reports whether the organization exists and is personal.
func IsReservedSlug ¶ added in v0.14.0
IsReservedSlug reports whether slug is on the built-in reserved list. Use a service built with WithReservedSlugs to also consult deployment-configured names.
func IsValidSlug ¶ added in v0.14.0
IsValidSlug reports whether slug satisfies the organization slug rules: 3 to 63 characters matching ^[a-z][a-z0-9-]*[a-z0-9]$.
func MembershipRoleGetter ¶ added in v0.14.0
func MembershipRoleGetter(svc Service) func(ctx context.Context, principalID, orgID uuid.UUID) (string, error)
MembershipRoleGetter returns a role getter for rbac.WithRoleGetter that reads a principal's role from its active membership in the organization. A missing or deactivated membership has no role.
func RequireMembersAllowed ¶ added in v0.14.0
func RequireMembersAllowed(ctx context.Context, client *ent.Client, orgID uuid.UUID, role string) error
RequireMembersAllowed returns ErrInvalidRole when role is not an organization role, and ErrPersonalOrganization when orgID is a personal organization and role is not RoleCollaborator. It is the check every path that creates or changes a membership applies, including the invite service and SCIM group membership.
func Slugify ¶ added in v0.14.0
Slugify converts a name to slug form (lowercase, spaces and underscores to hyphens, other characters dropped, "-org" appended below three characters, truncated to 63). The result is not guaranteed to be valid: a name starting with a digit yields a slug that IsValidSlug refuses.
Types ¶
type API ¶
type API struct {
// contains filtered or unexported fields
}
API provides HTTP handlers for Organization management.
type APIConfig ¶
type APIConfig struct {
// BasePath is the base path for all endpoints (default: "/api/v1").
BasePath string
// Logger is the logger to use (default: slog.Default()).
Logger *slog.Logger
// Authorizer checks the caller's organization permissions
// (authz.PermOrgView, PermOrgSettings, PermOrgInviteMember, and so
// on). Until it is set, every endpoint except creating an
// organization and checking a slug answers 501.
Authorizer authz.Authorizer
// Caller identifies the authenticated principal (default:
// requestprincipal.PrincipalUUID, set by requestprincipal.Middleware).
Caller func(ctx context.Context) (uuid.UUID, error)
// Entitlements lists what an organization may use, derived from its
// marketplace subscriptions and licenses (see
// marketplace.EntitlementSource). Without it, responses carry an
// empty entitlements list.
Entitlements EntitlementSource
// AppRoles manages application roles. Until it is set, the
// application role endpoints answer 501.
AppRoles *approle.Service
}
APIConfig configures the Organization API.
func DefaultAPIConfig ¶
func DefaultAPIConfig() APIConfig
DefaultAPIConfig returns the default API configuration.
type AddMemberInput ¶
type AddMemberInput struct {
OrganizationID uuid.UUID
PrincipalID uuid.UUID
Role string
Permissions []string
}
AddMemberInput contains input for adding a member to an organization.
type AddMemberRequest ¶
type AddMemberRequest struct {
Slug string `path:"slug"`
Body struct {
PrincipalID string `json:"principal_id" required:"true" format:"uuid"`
Role string `json:"role" required:"true" enum:"owner,admin,member,collaborator"`
Permissions []string `json:"permissions,omitempty"`
}
}
AddMemberRequest is the request for adding a member.
type AddMemberResponse ¶
type AddMemberResponse struct {
Body *MemberResponse
}
AddMemberResponse is the response for adding a member.
type AfterCommit ¶ added in v0.14.0
AfterCommit registers a created entity with the authorization backend. It must be called only after the transaction that created the entity committed, and never when it rolled back.
type AppRoleResponse ¶ added in v0.14.0
type AppRoleResponse struct {
PrincipalID string `json:"principal_id"`
Application string `json:"application"`
Role string `json:"role"`
AssignedBy *string `json:"assigned_by,omitempty"`
}
AppRoleResponse is the API representation of a stored application role.
type CheckSlugInput ¶
type CheckSlugInput struct {
Slug string `path:"slug"`
}
CheckSlugInput is the request for checking slug availability.
type CheckSlugOutput ¶
type CheckSlugOutput struct {
Body struct {
Available bool `json:"available"`
Slug string `json:"slug"`
}
}
CheckSlugOutput is the response for checking slug availability.
type ConvertResult ¶ added in v0.14.0
type ConvertResult struct {
// Team is the converted organization: same ID, data and memberships.
Team *Organization
// Personal is the new personal organization holding the owner's handle.
Personal *Organization
}
ConvertResult is the outcome of ConvertToTeam.
type CreateOrgInput ¶
type CreateOrgInput struct {
Body struct {
Name string `json:"name" required:"true" minLength:"1" maxLength:"100"`
Slug string `json:"slug" required:"true" minLength:"3" maxLength:"63" pattern:"^[a-z][a-z0-9-]*[a-z0-9]$"`
OrgType string `json:"org_type,omitempty" enum:"team,enterprise" default:"team"`
LogoURL *string `json:"logo_url,omitempty"`
Description *string `json:"description,omitempty"`
WebsiteURL *string `json:"website_url,omitempty"`
Settings map[string]any `json:"settings,omitempty"`
}
}
CreateOrgInput is the request body for creating an organization.
type CreateOrgOutput ¶
type CreateOrgOutput struct {
Body *OrgResponse
}
CreateOrgOutput is the response for creating an organization.
type CreateOrganizationInput ¶
type CreateOrganizationInput struct {
Name string
Slug string
OrgType OrgType
OwnerPrincipalID *uuid.UUID // Required for personal orgs
LogoURL *string
Description *string
WebsiteURL *string
Settings map[string]any
}
CreateOrganizationInput contains input for creating an organization.
type CreatePersonalOrgInput ¶
type CreatePersonalOrgInput struct {
OwnerPrincipalID uuid.UUID
Name string // Display name (usually user's name)
Slug string // URL-safe username
LogoURL *string // Optional avatar
}
CreatePersonalOrgInput contains input for creating a personal organization.
type DefaultService ¶
type DefaultService struct {
// contains filtered or unexported fields
}
DefaultService implements the Service interface.
func (*DefaultService) AddMember ¶
func (s *DefaultService) AddMember(ctx context.Context, input AddMemberInput) (*Membership, error)
AddMember adds a principal as a member of an organization.
func (*DefaultService) ConvertToTeam ¶ added in v0.14.0
func (s *DefaultService) ConvertToTeam(ctx context.Context, orgID uuid.UUID, teamSlug string, actor uuid.UUID) (*ConvertResult, error)
ConvertToTeam turns a personal organization into a team without moving anything: the organization keeps its ID, data and memberships, takes teamSlug and becomes a team, and the owner gets a new personal organization with the handle the old one had. It all happens in one transaction. actor must be the organization's owner.
func (*DefaultService) Create ¶
func (s *DefaultService) Create(ctx context.Context, input CreateOrganizationInput) (*Organization, error)
Create creates a new organization.
func (*DefaultService) CreatePersonalOrg ¶
func (s *DefaultService) CreatePersonalOrg(ctx context.Context, input CreatePersonalOrgInput) (*Organization, error)
CreatePersonalOrg creates a personal organization for a user.
func (*DefaultService) CreatePersonalOrgInTx ¶ added in v0.14.0
func (s *DefaultService) CreatePersonalOrgInTx(ctx context.Context, tx *ent.Tx, input CreatePersonalOrgInput) (*Organization, *Membership, AfterCommit, error)
CreatePersonalOrgInTx implements PersonalOrgTxCreator.
func (*DefaultService) GenerateSlug ¶
GenerateSlug generates a unique slug from a name.
func (*DefaultService) GetByID ¶
func (s *DefaultService) GetByID(ctx context.Context, id uuid.UUID) (*Organization, error)
GetByID retrieves an organization by ID.
func (*DefaultService) GetBySlug ¶
func (s *DefaultService) GetBySlug(ctx context.Context, slug string) (*Organization, error)
GetBySlug retrieves an organization by slug.
func (*DefaultService) GetMembership ¶
func (s *DefaultService) GetMembership(ctx context.Context, organizationID, principalID uuid.UUID) (*Membership, error)
GetMembership retrieves a specific membership.
func (*DefaultService) GetPersonalOrg ¶
func (s *DefaultService) GetPersonalOrg(ctx context.Context, principalID uuid.UUID) (*Organization, error)
GetPersonalOrg retrieves a principal's personal organization.
func (*DefaultService) List ¶
func (s *DefaultService) List(ctx context.Context, input ListOrganizationsInput) ([]*Organization, error)
List lists organizations with optional filters.
func (*DefaultService) ListMembers ¶
func (s *DefaultService) ListMembers(ctx context.Context, organizationID uuid.UUID, opts ...ListOption) ([]*Membership, error)
ListMembers lists the active members of an organization.
func (*DefaultService) ListMemberships ¶
func (s *DefaultService) ListMemberships(ctx context.Context, principalID uuid.UUID, opts ...ListOption) ([]*Membership, error)
ListMemberships lists the organizations a principal is an active member of.
func (*DefaultService) RemoveMember ¶
func (s *DefaultService) RemoveMember(ctx context.Context, organizationID, principalID uuid.UUID) error
RemoveMember removes a principal from an organization.
func (*DefaultService) Rename ¶ added in v0.14.0
func (s *DefaultService) Rename(ctx context.Context, orgID uuid.UUID, slug string, actor uuid.UUID) error
Rename changes an organization's slug. The old slug is reserved for actor for the cooling-off period and keeps resolving to the organization meanwhile; the new slug must be free, or reserved for actor (or an organization actor owns). It works the same for personal and team organizations, in every direction.
func (*DefaultService) ResolveSlug ¶ added in v0.14.0
func (s *DefaultService) ResolveSlug(ctx context.Context, slug string) (*Organization, bool, error)
ResolveSlug returns the organization with slug. When no organization uses it but one released it within the cooling-off period, it returns that organization and alias true, so old links keep working.
func (*DefaultService) SlugAvailable ¶
SlugAvailable checks if a slug is available.
func (*DefaultService) TransferOwnership ¶
func (s *DefaultService) TransferOwnership(ctx context.Context, input TransferOwnershipInput) error
TransferOwnership transfers ownership of an organization to another principal.
func (*DefaultService) Update ¶
func (s *DefaultService) Update(ctx context.Context, id uuid.UUID, input UpdateOrganizationInput) (*Organization, error)
Update updates an organization.
func (*DefaultService) UpdateMember ¶
func (s *DefaultService) UpdateMember(ctx context.Context, membershipID uuid.UUID, input UpdateMemberInput) (*Membership, error)
UpdateMember updates a membership.
type DeleteOrgInput ¶
type DeleteOrgInput struct {
Slug string `path:"slug"`
}
DeleteOrgInput is the request for deleting an organization.
type Entitlement ¶ added in v0.14.0
type Entitlement struct {
// Source is EntitlementSourceSubscription, EntitlementSourceLicense or
// EntitlementSourceDefault.
Source string `json:"source"`
// Application is the registered application identifier the entitlement
// grants, when it grants one. A platform-wide subscription has none.
Application string `json:"application,omitempty"`
// Seats caps the principals who may hold a role in Application, for a
// seat-licensed entitlement. Nil means unlimited.
Seats *int `json:"seats,omitempty"`
// ListingID is the marketplace listing a license is for. It is empty
// for a subscription, which is organization-wide.
ListingID string `json:"listing_id,omitempty"`
// Tier is the subscription's plan tier or the license type.
Tier string `json:"tier,omitempty"`
// ValidUntil is when the entitlement ends, when it does.
ValidUntil *time.Time `json:"valid_until,omitempty"`
}
Entitlement summarizes one thing an organization may use, derived from its marketplace subscriptions and licenses. An organization has no single plan: what it may use is the set of its entitlements.
type EntitlementSource ¶ added in v0.14.0
EntitlementSource lists the active entitlements of an organization. marketplace.EntitlementSource builds one from the marketplace services.
type GetMemberAppRolesInput ¶ added in v0.14.0
type GetMemberAppRolesInput struct {
Slug string `path:"slug"`
PrincipalID string `path:"principal_id" format:"uuid"`
}
GetMemberAppRolesInput is the request for a member's application roles.
type GetMemberAppRolesOutput ¶ added in v0.14.0
type GetMemberAppRolesOutput struct {
Body struct {
AppRoles []*AppRoleResponse `json:"app_roles"`
Effective map[string]string `json:"effective"`
}
}
GetMemberAppRolesOutput is the response for a member's application roles. Effective includes the admin role owners hold implicitly.
type GetMemberInput ¶
type GetMemberInput struct {
Slug string `path:"slug"`
PrincipalID string `path:"principal_id" format:"uuid"`
}
GetMemberInput is the request for getting a specific member.
type GetMemberOutput ¶
type GetMemberOutput struct {
Body *MemberResponse
}
GetMemberOutput is the response for getting a member.
type GetOrgInput ¶
type GetOrgInput struct {
Slug string `path:"slug"`
}
GetOrgInput is the request for getting an organization.
type GetOrgOutput ¶
type GetOrgOutput struct {
Body *OrgResponse
}
GetOrgOutput is the response for getting an organization.
type ListAppRolesInput ¶ added in v0.14.0
type ListAppRolesInput struct {
Slug string `path:"slug"`
Application string `query:"application" doc:"Only roles in this application"`
}
ListAppRolesInput is the request for listing an organization's application roles.
type ListAppRolesOutput ¶ added in v0.14.0
type ListAppRolesOutput struct {
Body struct {
AppRoles []*AppRoleResponse `json:"app_roles"`
}
}
ListAppRolesOutput is the response for listing application roles.
type ListMembersInput ¶
type ListMembersInput struct {
Slug string `path:"slug"`
IncludeInactive bool `query:"includeInactive" doc:"Also list deactivated memberships"`
}
ListMembersInput is the request for listing members.
type ListMembersOutput ¶
type ListMembersOutput struct {
Body struct {
Members []*MemberResponse `json:"members"`
}
}
ListMembersOutput is the response for listing members.
type ListOption ¶ added in v0.14.0
type ListOption func(*listOptions)
ListOption adjusts membership listing.
func IncludeInactive ¶ added in v0.14.0
func IncludeInactive() ListOption
IncludeInactive makes a membership listing include deactivated memberships, which are excluded by default.
type ListOrganizationsInput ¶
type ListOrganizationsInput struct {
OrgType *OrgType
Active *bool
// MemberPrincipalID limits the list to organizations the principal is
// an active member of.
MemberPrincipalID *uuid.UUID
Limit int
Offset int
}
ListOrganizationsInput contains filters for listing organizations.
type ListOrgsInput ¶
type ListOrgsInput struct {
// Huma does not support pointer parameters; an empty value means
// the filter is not applied.
OrgType string `query:"org_type" enum:"personal,team,enterprise"`
Active string `query:"active" enum:"true,false"`
Limit int `query:"limit" default:"20" minimum:"1" maximum:"100"`
Offset int `query:"offset" default:"0" minimum:"0"`
}
ListOrgsInput is the request for listing organizations.
type ListOrgsOutput ¶
type ListOrgsOutput struct {
Body struct {
Organizations []*OrgResponse `json:"organizations"`
Total int `json:"total"`
}
}
ListOrgsOutput is the response for listing organizations.
type MemberResponse ¶
type MemberResponse struct {
ID string `json:"id"`
PrincipalID string `json:"principal_id"`
OrganizationID string `json:"organization_id"`
Role string `json:"role"`
Permissions []string `json:"permissions,omitempty"`
Active bool `json:"active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
MemberResponse is the API representation of a membership.
type Membership ¶
type Membership struct {
ID uuid.UUID
PrincipalID uuid.UUID
OrganizationID uuid.UUID
Role string
Permissions []string
Active bool
CreatedAt time.Time
UpdatedAt time.Time
}
Membership represents a principal's membership in an organization.
type OrgResponse ¶
type OrgResponse struct {
ID string `json:"id"`
Name string `json:"name"`
Slug string `json:"slug"`
OrgType string `json:"org_type"`
OwnerPrincipalID *string `json:"owner_principal_id,omitempty"`
LogoURL *string `json:"logo_url,omitempty"`
Description *string `json:"description,omitempty"`
WebsiteURL *string `json:"website_url,omitempty"`
Active bool `json:"active"`
Settings map[string]any `json:"settings,omitempty"`
// Entitlements summarizes what the organization may use, derived from
// its marketplace subscriptions and licenses.
Entitlements []Entitlement `json:"entitlements"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
OrgResponse is the API representation of an organization.
type Organization ¶
type Organization struct {
ID uuid.UUID
Name string
Slug string
OrgType OrgType
OwnerPrincipalID *uuid.UUID // For personal orgs
LogoURL *string
Description *string
WebsiteURL *string
Settings map[string]any
Active bool
CreatedAt time.Time
UpdatedAt time.Time
}
Organization represents an organization in the system.
func (*Organization) IsPersonal ¶
func (o *Organization) IsPersonal() bool
IsPersonal returns true if this is a personal organization.
type PersonalOrgTxCreator ¶ added in v0.14.0
type PersonalOrgTxCreator interface {
// CreatePersonalOrgInTx writes the personal organization and its owner
// membership in tx and leaves committing to the caller. The owner
// principal may be a row created earlier in the same transaction. The
// returned AfterCommit registers the organization with the
// authorization syncer; call it after a successful commit.
CreatePersonalOrgInTx(ctx context.Context, tx *ent.Tx, input CreatePersonalOrgInput) (*Organization, *Membership, AfterCommit, error)
}
PersonalOrgTxCreator is implemented by services that can create a personal organization inside a caller's transaction, so a larger operation such as signup is atomic.
type RemoveAppRoleInput ¶ added in v0.14.0
type RemoveAppRoleInput struct {
Slug string `path:"slug"`
PrincipalID string `path:"principal_id" format:"uuid"`
Application string `path:"application"`
}
RemoveAppRoleInput is the request for removing a member's role in an application.
type RemoveMemberInput ¶
type RemoveMemberInput struct {
Slug string `path:"slug"`
PrincipalID string `path:"principal_id" format:"uuid"`
}
RemoveMemberInput is the request for removing a member.
type Service ¶
type Service interface {
// Create creates a new organization.
Create(ctx context.Context, input CreateOrganizationInput) (*Organization, error)
// CreatePersonalOrg creates a personal organization for a user.
CreatePersonalOrg(ctx context.Context, input CreatePersonalOrgInput) (*Organization, error)
// GetByID retrieves an organization by ID.
GetByID(ctx context.Context, id uuid.UUID) (*Organization, error)
// GetBySlug retrieves an organization by slug.
GetBySlug(ctx context.Context, slug string) (*Organization, error)
// GetPersonalOrg retrieves a principal's personal organization.
GetPersonalOrg(ctx context.Context, principalID uuid.UUID) (*Organization, error)
// Update updates an organization.
Update(ctx context.Context, id uuid.UUID, input UpdateOrganizationInput) (*Organization, error)
// Delete soft-deletes an organization.
Delete(ctx context.Context, id uuid.UUID) error
// List lists organizations with optional filters.
List(ctx context.Context, input ListOrganizationsInput) ([]*Organization, error)
// AddMember adds a principal as a member of an organization.
AddMember(ctx context.Context, input AddMemberInput) (*Membership, error)
// UpdateMember updates a membership.
UpdateMember(ctx context.Context, membershipID uuid.UUID, input UpdateMemberInput) (*Membership, error)
// RemoveMember removes a principal from an organization.
RemoveMember(ctx context.Context, organizationID, principalID uuid.UUID) error
// GetMembership retrieves a specific membership.
GetMembership(ctx context.Context, organizationID, principalID uuid.UUID) (*Membership, error)
// ListMembers lists the active members of an organization. Pass
// IncludeInactive to also list deactivated memberships.
ListMembers(ctx context.Context, organizationID uuid.UUID, opts ...ListOption) ([]*Membership, error)
// ListMemberships lists the organizations a principal is an active
// member of. Pass IncludeInactive to also list deactivated memberships.
ListMemberships(ctx context.Context, principalID uuid.UUID, opts ...ListOption) ([]*Membership, error)
// ResolveSlug returns the organization using slug or, within the
// cooling-off period after a rename, the organization that released it
// (alias true).
ResolveSlug(ctx context.Context, slug string) (*Organization, bool, error)
// Rename changes an organization's slug on behalf of actor, who must
// own it. The old slug is held for actor for the cooling-off period.
Rename(ctx context.Context, orgID uuid.UUID, slug string, actor uuid.UUID) error
// ConvertToTeam turns a personal organization into a team organization
// under teamSlug and gives its owner a new personal organization with
// the old handle, in one transaction.
ConvertToTeam(ctx context.Context, orgID uuid.UUID, teamSlug string, actor uuid.UUID) (*ConvertResult, error)
// SlugAvailable checks if a slug is available. A reserved slug is
// refused with ErrSlugReserved, and one in its cooling-off period with
// ErrSlugCoolingOff (both with false).
SlugAvailable(ctx context.Context, slug string) (bool, error)
// GenerateSlug generates a unique slug from a name.
GenerateSlug(ctx context.Context, name string) (string, error)
// TransferOwnership transfers ownership of an organization to another principal.
TransferOwnership(ctx context.Context, input TransferOwnershipInput) error
}
Service defines the organization service interface.
func NewService ¶
func NewService(client *ent.Client, opts ...ServiceOption) Service
NewService creates a new organization service.
type ServiceOption ¶
type ServiceOption func(*DefaultService)
ServiceOption configures a DefaultService.
func WithAccessChangeSink ¶ added in v0.14.0
func WithAccessChangeSink(sink accesschange.Sink) ServiceOption
WithAccessChangeSink announces the access changes the service records (member removal, role change, ownership transfer) to sink after their transaction commits (ADR-010). The records are written whether or not a sink is set.
func WithAuthzSyncer ¶
func WithAuthzSyncer(syncer authz.RelationshipSyncer) ServiceOption
WithAuthzSyncer sets the authorization syncer for keeping authz in sync with identity changes.
func WithLogger ¶
func WithLogger(logger *slog.Logger) ServiceOption
WithLogger sets the logger for the service.
func WithReservedSlugs ¶ added in v0.14.0
func WithReservedSlugs(slugs ...string) ServiceOption
WithReservedSlugs adds deployment-configured slugs (for example product names) to the built-in reserved list.
func WithSlugCoolingOff ¶ added in v0.14.0
func WithSlugCoolingOff(d time.Duration) ServiceOption
WithSlugCoolingOff sets how long a released slug stays reserved (default SlugCoolingOff).
func WithSyncMode ¶
func WithSyncMode(mode authz.SyncMode) ServiceOption
WithSyncMode sets the sync mode (strict or eventual).
type SetAppRoleInput ¶ added in v0.14.0
type SetAppRoleInput struct {
Slug string `path:"slug"`
PrincipalID string `path:"principal_id" format:"uuid"`
Application string `path:"application"`
Body struct {
Role string `json:"role" required:"true" minLength:"1"`
}
}
SetAppRoleInput is the request for assigning or changing a member's role in an application.
type SetAppRoleOutput ¶ added in v0.14.0
type SetAppRoleOutput struct {
Body *AppRoleResponse
}
SetAppRoleOutput is the response for assigning or changing a role.
type TransferOwnershipInput ¶
type TransferOwnershipInput struct {
OrganizationID uuid.UUID
FromPrincipal uuid.UUID // Current owner
ToPrincipal uuid.UUID // New owner
NewRoleForOld string // Role for the previous owner (default: "admin")
}
TransferOwnershipInput contains input for transferring organization ownership.
type TransferOwnershipRequest ¶
type TransferOwnershipRequest struct {
Slug string `path:"slug"`
Body struct {
NewOwnerPrincipalID string `json:"new_owner_principal_id" required:"true" format:"uuid"`
NewRoleForOldOwner string `json:"new_role_for_old_owner,omitempty" enum:"admin,member"`
}
}
TransferOwnershipRequest is the request for transferring ownership.
type TransferOwnershipResponse ¶
type TransferOwnershipResponse struct {
Body struct {
Message string `json:"message"`
}
}
TransferOwnershipResponse is the response for transferring ownership.
type UpdateMemberInput ¶
UpdateMemberInput contains input for updating a membership.
type UpdateMemberRequest ¶
type UpdateMemberRequest struct {
Slug string `path:"slug"`
PrincipalID string `path:"principal_id" format:"uuid"`
Body struct {
Role *string `json:"role,omitempty" enum:"owner,admin,member,collaborator"`
Permissions []string `json:"permissions,omitempty"`
Active *bool `json:"active,omitempty"`
}
}
UpdateMemberRequest is the request for updating a member.
type UpdateMemberResponse ¶
type UpdateMemberResponse struct {
Body *MemberResponse
}
UpdateMemberResponse is the response for updating a member.
type UpdateOrgInput ¶
type UpdateOrgInput struct {
Slug string `path:"slug"`
Body struct {
Name *string `json:"name,omitempty" maxLength:"100"`
LogoURL *string `json:"logo_url,omitempty"`
Description *string `json:"description,omitempty"`
WebsiteURL *string `json:"website_url,omitempty"`
Settings map[string]any `json:"settings,omitempty"`
}
}
UpdateOrgInput is the request for updating an organization.
type UpdateOrgOutput ¶
type UpdateOrgOutput struct {
Body *OrgResponse
}
UpdateOrgOutput is the response for updating an organization.