Documentation
¶
Overview ¶
Package scim implements the inbound (server) side of SCIM 2.0 (RFC 7643 schema, RFC 7644 protocol) for the /scim/v2 surface: an external IdP (Okta, Entra ID, Google Workspace) lifecycle-manages users in a project by POST/GET/PUT/PATCH/DELETE-ing the /Users resource and the discovery endpoints (/ServiceProviderConfig, /Schemas, /ResourceTypes).
The package is deliberately transport- and storage-agnostic: it depends only on the Store interface (a thin slice of the host's user repository) so it can be unit-tested with an in-memory fake and mounted by the host over its own project-scoped repository. The host gates the whole surface behind config + a bearer token and resolves the project before calling in; this package never sees credentials or projects.
Index ¶
Constants ¶
const ( SchemaListResponse = "urn:ietf:params:scim:api:messages:2.0:ListResponse" SchemaError = "urn:ietf:params:scim:api:messages:2.0:Error" SchemaPatchOp = "urn:ietf:params:scim:api:messages:2.0:PatchOp" SchemaServiceProviderConfig = "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig" SchemaResourceTypeURN = "urn:ietf:params:scim:schemas:core:2.0:ResourceType" SchemaSchemaURN = "urn:ietf:params:scim:schemas:core:2.0:Schema" )
SchemaListResponse, SchemaError, SchemaPatchOp, SchemaServiceProviderConfig, SchemaResourceType, and SchemaSchema are the SCIM message schema URNs.
const SchemaUser = "urn:ietf:params:scim:schemas:core:2.0:User"
SchemaUser is the SCIM core User schema URN (RFC 7643 §8.7.1).
Variables ¶
var ( ErrNotFound = errors.New("scim: resource not found") ErrConflict = errors.New("scim: uniqueness conflict") )
Sentinel errors a Store returns; the provider maps them to SCIM HTTP statuses.
Functions ¶
This section is empty.
Types ¶
type Email ¶
type Email struct {
Value string `json:"value"`
Primary bool `json:"primary,omitempty"`
Type string `json:"type,omitempty"`
}
Email is a SCIM multi-valued email entry.
type ErrorResponse ¶
type ErrorResponse struct {
Schemas []string `json:"schemas"`
Detail string `json:"detail"`
Status string `json:"status"`
SCIMType string `json:"scimType,omitempty"`
}
ErrorResponse is the SCIM error envelope (RFC 7644 §3.12).
type ListFilter ¶
type ListFilter struct {
UserName string
Email string
ExternalID string
StartIndex int // 1-based (SCIM); the provider converts to a 0-based offset
Count int
}
ListFilter narrows a ListUsers query. The provider parses the subset of the SCIM filter grammar enterprises actually send (userName eq, email eq, externalId eq) into this struct; an unsupported filter is rejected before it reaches the Store.
type ListResponse ¶
type ListResponse struct {
Schemas []string `json:"schemas"`
TotalResults int `json:"totalResults"`
StartIndex int `json:"startIndex"`
ItemsPerPage int `json:"itemsPerPage"`
Resources []Resource `json:"Resources"`
}
ListResponse is the SCIM paginated list envelope (RFC 7644 §3.4.2).
type Meta ¶
type Meta struct {
ResourceType string `json:"resourceType"`
Created string `json:"created,omitempty"`
LastModified string `json:"lastModified,omitempty"`
Location string `json:"location,omitempty"`
}
Meta is the SCIM common resource metadata.
type Name ¶
type Name struct {
Formatted string `json:"formatted,omitempty"`
GivenName string `json:"givenName,omitempty"`
FamilyName string `json:"familyName,omitempty"`
}
Name is the SCIM complex name attribute.
type Operation ¶
type Operation struct {
Op string `json:"op"`
Path string `json:"path,omitempty"`
Value json.RawMessage `json:"value,omitempty"`
}
Operation is one entry in a PatchOp's Operations array.
type PatchRequest ¶
type PatchRequest struct {
Schemas []string `json:"schemas"`
Operations []Operation `json:"Operations"`
}
PatchRequest is the SCIM PatchOp message (RFC 7644 §3.5.2). The provider supports the attribute set enterprises drive through PATCH — Microsoft Entra ID, for one, performs ALL profile updates (and de/re-provisioning) via PATCH replace, never PUT — so the mapped attributes (userName, emails/email, name, externalId, active) are all patchable. Operations targeting an attribute this server does not model are surfaced as errors rather than silently dropped.
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider serves the SCIM /Users surface and the discovery endpoints over a Store. It is mounted by the host under /scim/v2/ once the host has authenticated the request and resolved its project.
func NewProvider ¶
NewProvider returns a Provider backed by store. store is required.
type Resource ¶
type Resource struct {
Schemas []string `json:"schemas"`
ID string `json:"id"`
ExternalID string `json:"externalId,omitempty"`
UserName string `json:"userName"`
Name *Name `json:"name,omitempty"`
Emails []Email `json:"emails,omitempty"`
Active bool `json:"active"`
Meta *Meta `json:"meta,omitempty"`
}
Resource is the SCIM JSON representation of a core User. Only the attributes the host can populate are emitted; SCIM clients tolerate the absence of optional attributes.
type Store ¶
type Store interface {
CreateUser(ctx context.Context, u User) (User, error)
GetUser(ctx context.Context, id string) (User, error)
ReplaceUser(ctx context.Context, id string, u User) (User, error)
// PatchUser applies the non-nil fields of patch to the user (SCIM
// PATCH partial update), returning the updated user. Setting Active to
// false maps to the host's deactivation path (which also revokes
// sessions/refresh tokens); true reactivates. Implementations MUST return
// ErrNotFound for a missing user and ErrConflict on a uniqueness violation
// (userName/email/externalId).
PatchUser(ctx context.Context, id string, patch UserPatch) (User, error)
DeleteUser(ctx context.Context, id string) error
// ListUsers returns users matching filter (zero value = all), ordered
// stably, plus the total number of matches (for SCIM totalResults).
ListUsers(ctx context.Context, filter ListFilter) (users []User, total int, err error)
}
Store is the slice of the host's user repository the SCIM provider needs. All methods operate within whatever project/tenant scope the host bound before constructing the provider. Implementations MUST return ErrNotFound for a missing user and ErrConflict when a uniqueness constraint (userName or externalId) would be violated, so the provider can map them to the correct SCIM HTTP status (404 / 409).
type User ¶
type User struct {
ID string
ExternalID string
UserName string // SCIM userName — mapped to the host email
Email string
GivenName string
FamilyName string
Active bool
CreatedAt time.Time
UpdatedAt time.Time
}
User is the host's representation of a user, mapped to/from the SCIM core User schema by this package. It is intentionally a small value type so the Store contract does not leak the host's full domain model.
type UserPatch ¶
type UserPatch struct {
Active *bool
UserName *string
Email *string
ExternalID *string
GivenName *string
FamilyName *string
}
UserPatch is a partial update to a User: every field is a pointer so the caller can distinguish "leave unchanged" (nil) from "set to this value" (non-nil). The SCIM provider builds it from a PATCH request's operations and the host Store maps the set fields onto its repository, so a PATCH replace of a single attribute never clobbers the others. The mapping mirrors the PUT (ReplaceUser) attribute mapping: userName/email → the host email, given/family → the host display name, externalId → external_id, active → the account status.