scim

package
v0.10.1 Latest Latest
Warning

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

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

Documentation

Overview

Package scim is the suite's SCIM 2.0 client (RFC 7643, RFC 7644) and the wire types it shares with a server. KySignOn provisions accounts into downstream products and third-party SCIM servers; before this package its resource types, paths and status handling were inlined in its sync engine, where a 409 on create counted as a delivered account, paths were built from local IDs the server had never issued, and generic targets got the suite signature in place of their bearer token. Each of those leaves an account that does not exist recorded as provisioned, which is the bar for a primitive living here.

Users only. Groups, listing and bulk are not built because nothing in the suite sends them.

Index

Constants

View Source
const (
	UserSchema         = "urn:ietf:params:scim:schemas:core:2.0:User"
	PatchOpSchema      = "urn:ietf:params:scim:api:messages:2.0:PatchOp"
	ListResponseSchema = "urn:ietf:params:scim:api:messages:2.0:ListResponse"
	ErrorSchema        = "urn:ietf:params:scim:api:messages:2.0:Error"
)

Variables

View Source
var (
	// ErrNotFound is a 404: the resource is not there. On a delete the caller decides
	// whether "already gone" is the outcome it wanted.
	ErrNotFound = errors.New("scim: resource not found")
	// ErrConflict is a 409: the server refused a create as a duplicate. It is never success;
	// look the account up by externalId or userName and reconcile.
	ErrConflict = errors.New("scim: conflict")
	// ErrUnauthorized is a 401 or 403: the bearer token is wrong or lacks the scope.
	ErrUnauthorized = errors.New("scim: unauthorized")
	// ErrThrottled is a 429 or 503; Error.RetryAfter carries the server's hint when it sent one.
	ErrThrottled = errors.New("scim: throttled")
	// ErrAmbiguous means a filter that should identify one user matched more than one.
	ErrAmbiguous = errors.New("scim: filter matched more than one user")
	// ErrMalformedResponse means a 2xx whose body is not the resource it should be, is
	// larger than the client will read, or lacks the id a create must return.
	ErrMalformedResponse = errors.New("scim: malformed response")
	// ErrInsecureURL means BaseURL is not an HTTPS URL with a host and nothing else.
	ErrInsecureURL = errors.New("scim: base URL must be https with a host and no credentials, query or fragment")
	// ErrNoToken means Token is empty.
	ErrNoToken = errors.New("scim: bearer token is required")
)

Functions

This section is empty.

Types

type Client

type Client struct {
	// BaseURL is the SCIM root, e.g. https://host/scim/v2. HTTPS only.
	BaseURL string
	// Token is sent as Authorization: Bearer and nowhere else.
	Token      string
	HTTPClient *http.Client
}

Client talks to one SCIM server. The zero HTTPClient is a ten-second client; products with an outbound policy (private-range refusal, dial guards) pass their own. Redirects are refused whatever client is passed.

func (*Client) CreateUser

func (c *Client) CreateUser(ctx context.Context, user User) (User, error)

CreateUser POSTs the user and returns the resource the server minted. The returned ID is the handle for every later call; the caller's ID and Meta are not sent. A 409 is ErrConflict, never success.

func (*Client) DeleteUser

func (c *Client) DeleteUser(ctx context.Context, id string) error

DeleteUser removes the user. A 404 is ErrNotFound; whether that is fine is the caller's call.

func (*Client) FindUser

func (c *Client) FindUser(ctx context.Context, attribute, value string) (User, error)

FindUser looks a user up by an equality filter on "externalId" or "userName", the two attributes the result can be checked against. Zero matches is ErrNotFound; more than one is ErrAmbiguous; one that does not carry the value is ErrMalformedResponse, because a server without filtering returns its collection, and one unrelated user in it must not become the account that is deactivated next.

func (*Client) GetUser

func (c *Client) GetUser(ctx context.Context, id string) (User, error)

GetUser fetches one user by the server's ID.

func (*Client) PatchUser

func (c *Client) PatchUser(ctx context.Context, id string, ops ...PatchOperation) (User, error)

PatchUser applies operations, e.g. {Op: "replace", Path: "active", Value: false} to deprovision. The returned User is zero when the server answers 204.

func (*Client) Ping

func (c *Client) Ping(ctx context.Context) error

Ping fetches ServiceProviderConfig, which proves the URL answers as a SCIM root and the token is accepted, without touching a user. Any HTTPS endpoint can return an empty 200, so the body must be a JSON object.

func (*Client) ReplaceUser

func (c *Client) ReplaceUser(ctx context.Context, id string, user User) (User, error)

ReplaceUser PUTs the whole user under the server's ID.

type Error

type Error struct {
	Status   int
	ScimType string
	Detail   string
	// RetryAfter is the server's Retry-After hint, zero when absent, capped at one hour.
	RetryAfter time.Duration
}

Error is a non-2xx response. Detail is the server's text with the bearer token redacted, made printable and cut short, so it can reach a log or an audit record as it is.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Is

func (e *Error) Is(target error) bool

Is maps the status to the sentinel a caller branches on.

type Meta

type Meta struct {
	ResourceType string     `json:"resourceType,omitempty"`
	Created      *time.Time `json:"created,omitempty"`
	LastModified *time.Time `json:"lastModified,omitempty"`
	Location     string     `json:"location,omitempty"`
	Version      string     `json:"version,omitempty"`
}

Meta is the server-owned resource metadata.

type MultiValue

type MultiValue struct {
	Value   string `json:"value"`
	Type    string `json:"type,omitempty"`
	Display string `json:"display,omitempty"`
	Primary bool   `json:"primary,omitempty"`
}

MultiValue is one entry of a multi-valued attribute such as emails or roles.

type Name

type Name struct {
	Formatted  string `json:"formatted,omitempty"`
	GivenName  string `json:"givenName,omitempty"`
	FamilyName string `json:"familyName,omitempty"`
}

Name is the User's name complex attribute.

type PatchOperation

type PatchOperation struct {
	Op    string `json:"op"`
	Path  string `json:"path,omitempty"`
	Value any    `json:"value,omitempty"`
}

PatchOperation is one entry of a PatchOp's Operations. Value is omitted when nil, which is what a remove wants.

type User

type User struct {
	Schemas     []string     `json:"schemas"`
	ID          string       `json:"id,omitempty"`
	ExternalID  string       `json:"externalId,omitempty"`
	UserName    string       `json:"userName"`
	DisplayName string       `json:"displayName,omitempty"`
	Name        *Name        `json:"name,omitempty"`
	Emails      []MultiValue `json:"emails,omitempty"`
	Roles       []MultiValue `json:"roles,omitempty"`
	Active      bool         `json:"active"`
	Meta        *Meta        `json:"meta,omitempty"`
}

User is the core User resource with the attributes the suite exchanges. The server owns ID and Meta; the client never sends them.

Jump to

Keyboard shortcuts

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