Documentation
¶
Overview ¶
Package scim provides optional organization-bound SCIM 2.0 provisioning and deprovisioning for PostgreSQL and SQLite (RFC 7643 and RFC 7644). An organization's owner creates a directory and issues it a bearer token; the organization's identity provider then creates, updates, deactivates and deletes users and groups through the SCIM endpoint the host mounts. A directory provisions accounts only for addresses in domains its organization verified through DNS, binds each account to at most one directory, and never takes over an account unless its owner opted in. It depends on the organization and sso plugins. See docs/scim.md.
Index ¶
- Constants
- func Contract() plugin.Definition
- func Method(directoryID string) sigil.AuditMethod
- type Config
- type Directory
- type DirectoryInput
- type Error
- type Group
- type IssuedToken
- type LinkSubject
- type Plugin
- func (p *Plugin) Bind(a *sigil.Auth) error
- func (p *Plugin) CreateDirectory(ctx context.Context, organizationID string, in DirectoryInput) (Directory, error)
- func (p *Plugin) Definition() plugin.Definition
- func (p *Plugin) DeleteDirectory(ctx context.Context, organizationID, id string) error
- func (p *Plugin) Directories(ctx context.Context, organizationID string) ([]Directory, error)
- func (p *Plugin) Handler() http.Handler
- func (p *Plugin) IssueToken(ctx context.Context, organizationID, directoryID string) (IssuedToken, error)
- func (p *Plugin) RevokeToken(ctx context.Context, organizationID, directoryID, tokenID string) error
- func (p *Plugin) UpdateDirectory(ctx context.Context, organizationID, id string, revision int, ...) (Directory, error)
- type Token
Constants ¶
const ( DirectoryCreateOperation plugin.Operation = "scim.directory.create" DirectoryUpdateOperation plugin.Operation = "scim.directory.update" DirectoryDeleteOperation plugin.Operation = "scim.directory.delete" TokenIssueOperation plugin.Operation = "scim.token.issue" TokenRevokeOperation plugin.Operation = "scim.token.revoke" // UserCreateOperation records a new account the directory created. UserCreateOperation plugin.Operation = "scim.user.create" // UserBindOperation records an existing account the directory adopted. UserBindOperation plugin.Operation = "scim.user.bind" UserUpdateOperation plugin.Operation = "scim.user.update" UserDeactivateOperation plugin.Operation = "scim.user.deactivate" UserReactivateOperation plugin.Operation = "scim.user.reactivate" UserDeleteOperation plugin.Operation = "scim.user.delete" GroupCreateOperation plugin.Operation = "scim.group.create" GroupUpdateOperation plugin.Operation = "scim.group.update" GroupDeleteOperation plugin.Operation = "scim.group.delete" // AuthenticateOperation records a refused provisioning token. AuthenticateOperation plugin.Operation = "scim.authenticate" )
Operations recorded in the audit trail. Management operations are attributed to the organization owner's session. Provisioning operations are attributed to the host with the directory's method, "scim.<directory id>", and the affected account as subject.
const ( // ReasonDomain: the address is not in a domain the organization verified. ReasonDomain sigil.AuditReason = "domain" // ReasonConflict: the address, account or linked identity belongs to // someone the directory may not take over. ReasonConflict sigil.AuditReason = "conflict" )
Refusal reasons on provisioning failures, beside sigil.ReasonForbidden (a deprovisioning request for an organization owner) and sigil.ReasonInvalidCredentials (an unknown, forged or revoked token).
const ( MaxDirectories = 5 // per organization MaxTokens = 2 // live tokens per directory, for rotation MaxPage = 100 // resources per list response MaxGroups = 1000 // per directory MaxGroupMembers = 10000 // per group // MaxMemberPage bounds the groups of one list page that include members. MaxMemberPage = 10 MaxOperations = 100 // per PATCH request MaxEmails = 10 // per user )
Limits.
const ID = "scim"
Variables ¶
This section is empty.
Functions ¶
func Contract ¶
func Contract() plugin.Definition
Contract is the offline declaration for tooling: routes, schema names and migrations are independent of configuration. Its handlers fail closed.
func Method ¶
func Method(directoryID string) sigil.AuditMethod
Method names a directory on provisioning events: "scim.<id>".
Types ¶
type Config ¶
type Config struct {
Store storage.Store
// BaseURL is the absolute HTTPS URL at which the host mounts Handler, for
// example "https://auth.example.com/scim/v2". Resource locations are
// derived from it and the handler serves only paths below it.
BaseURL string
// ManageAssurance raises the assurance directory and token management need
// above sigil.Config.SensitiveAssurance; it never lowers it.
ManageAssurance sigil.AssuranceLevel
}
Config resources belong to the Auth passed to Bind.
type Directory ¶
type Directory struct {
ID string `json:"id"`
OrganizationID string `json:"organizationId"`
// ConnectionID, when set, names one of the organization's SSO connections:
// every account the directory provisions is linked to it with the
// LinkSubject attribute as subject, so it can sign in with no password.
ConnectionID string `json:"connectionId"`
LinkSubject LinkSubject `json:"linkSubject"`
AdoptExisting bool `json:"adoptExisting"`
Enabled bool `json:"enabled"`
Revision int `json:"revision"`
Tokens []Token `json:"tokens"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
Directory is an organization's provisioning client. Tokens lists live tokens without their secrets.
type DirectoryInput ¶
type DirectoryInput struct {
ConnectionID string
LinkSubject LinkSubject
AdoptExisting bool
Enabled bool
}
DirectoryInput is the configuration an owner submits. ConnectionID and LinkSubject are set together or not at all.
type Error ¶
Error is a SCIM error response (RFC 7644 section 3.12). Detail is fixed, library-controlled text; it never echoes request values.
type Group ¶
type Group struct {
ID string `json:"id"`
DirectoryID string `json:"directoryId"`
DisplayName string `json:"displayName"`
ExternalID string `json:"externalId"`
}
Group is a directory group as the host reads it for its own authorization. Groups grant nothing in Sigil: they do not change organization roles.
func MemberGroups ¶
MemberGroups lists, inside a trusted transaction, the groups of the directory of organizationID that holds userID's account, by group ID. An account no directory of the organization holds has none. It is for host authorization decisions made in the same transaction as the work they guard; groups follow the directory, not the account's membership.
type IssuedToken ¶
IssuedToken carries the one-time secret, which sigil.Secret redacts from logs and fmt output.
type LinkSubject ¶
type LinkSubject string
LinkSubject names the SCIM attribute whose value is the subject a directory links for its SSO connection.
const ( // LinkUserName links the userName, which is the SAML NameID of Microsoft // Entra ID (UPN), Okta (username) and Google Workspace (primary email). LinkUserName LinkSubject = "userName" // LinkExternalID links the externalId, for an IdP that maps its immutable // user identifier to both externalId and the SSO subject. LinkExternalID LinkSubject = "externalId" )
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin binds to exactly one Auth. Methods are safe for concurrent use.
func (*Plugin) CreateDirectory ¶
func (p *Plugin) CreateDirectory(ctx context.Context, organizationID string, in DirectoryInput) (Directory, error)
CreateDirectory adds a directory to the organization. The caller must be its live owner, recently authenticated at Config.ManageAssurance. A linked SSO connection must belong to the same organization. The directory has no token until IssueToken.
func (*Plugin) Definition ¶
func (p *Plugin) Definition() plugin.Definition
func (*Plugin) DeleteDirectory ¶
DeleteDirectory removes a directory with its tokens, user records and groups. Accounts it provisioned or adopted are not changed: they remain ordinary accounts with their memberships and SSO links, and no longer follow the directory.
func (*Plugin) Directories ¶
Directories lists the organization's directories, oldest first, for its live owner. Token secrets are never included.
func (*Plugin) Handler ¶
Handler serves the SCIM 2.0 protocol at Config.BaseURL for identity providers. Every request needs one "Authorization: Bearer" provisioning token; cookies, query strings and bodies never carry credentials, so the endpoint needs no CSRF protection and must not be given CORS headers. The token selects the directory, and every resource is scoped to it. Mount it outside Auth.Handler, for example:
mux.Handle("/scim/v2/", scim.Handler())
The host owns TLS, request-rate and client-address policy for the route.
func (*Plugin) IssueToken ¶
func (p *Plugin) IssueToken(ctx context.Context, organizationID, directoryID string) (IssuedToken, error)
IssueToken creates a provisioning token for the directory and returns its secret once. A directory holds at most MaxTokens live tokens: to rotate, issue the next token, configure it at the identity provider, then revoke the old one. Tokens do not expire.
func (*Plugin) RevokeToken ¶
func (p *Plugin) RevokeToken(ctx context.Context, organizationID, directoryID, tokenID string) error
RevokeToken deletes one of the directory's tokens. Every request made with it afterwards, including one in flight that has not yet written, is refused. Revoking an unknown or already revoked token is forbidden.
func (*Plugin) UpdateDirectory ¶
func (p *Plugin) UpdateDirectory(ctx context.Context, organizationID, id string, revision int, in DirectoryInput) (Directory, error)
UpdateDirectory replaces a directory's configuration at the expected revision, under the same authorization as CreateDirectory. Disabling refuses every token at once. A changed connection or link subject applies to each user record the next time the directory writes it; links created earlier remain until then.