organization

package
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Overview

Package organization provides organization management services.

Index

Constants

View Source
const (
	EntitlementSourceSubscription = "subscription"
	EntitlementSourceLicense      = "license"
	// EntitlementSourceDefault marks an application every organization of
	// the deployment is entitled to, by configuration.
	EntitlementSourceDefault = "default"
)

Entitlement sources.

View Source
const (
	RoleOwner  = "owner"
	RoleAdmin  = "admin"
	RoleMember = "member"
)

MembershipRole constants for common roles.

View Source
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.

View Source
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

View Source
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")
)
View Source
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.

View Source
var ErrMembershipNotFound = errors.New("membership not found")

ErrMembershipNotFound is returned when a principal has no membership, active or not, in an organization.

View Source
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.

View Source
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.

View Source
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

func IsPersonal(ctx context.Context, client *ent.Client, orgID uuid.UUID) (bool, error)

IsPersonal reports whether the organization exists and is personal.

func IsReservedSlug added in v0.14.0

func IsReservedSlug(slug string) bool

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

func IsValidSlug(slug string) bool

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

func Slugify(name string) string

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.

func ValidRole added in v0.14.0

func ValidRole(role string) bool

ValidRole reports whether role is an organization role.

Types

type API

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

API provides HTTP handlers for Organization management.

func NewAPI

func NewAPI(service Service, config APIConfig) *API

NewAPI creates a new Organization API.

func (*API) Router

func (a *API) Router() chi.Router

Router returns the Chi router.

func (*API) ServeHTTP

func (a *API) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

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

type AfterCommit func(ctx context.Context) error

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

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) Delete

func (s *DefaultService) Delete(ctx context.Context, id uuid.UUID) error

Delete soft-deletes an organization.

func (*DefaultService) GenerateSlug

func (s *DefaultService) GenerateSlug(ctx context.Context, name string) (string, error)

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

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

func (s *DefaultService) SlugAvailable(ctx context.Context, slug string) (bool, error)

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

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

type EntitlementSource func(ctx context.Context, organizationID uuid.UUID) ([]Entitlement, error)

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 OrgType

type OrgType string

OrgType represents the type of organization.

const (
	OrgTypePersonal   OrgType = "personal"   // Personal namespace for a user
	OrgTypeTeam       OrgType = "team"       // Shared team organization
	OrgTypeEnterprise OrgType = "enterprise" // Enterprise 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

type UpdateMemberInput struct {
	Role        *string
	Permissions []string
	Active      *bool
}

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.

type UpdateOrganizationInput

type UpdateOrganizationInput struct {
	Name        *string
	LogoURL     *string
	Description *string
	WebsiteURL  *string
	Settings    map[string]any
}

UpdateOrganizationInput contains input for updating an organization.

Jump to

Keyboard shortcuts

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