scim

package module
v0.0.0-...-8304ef6 Latest Latest
Warning

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

Go to latest
Published: Mar 26, 2026 License: MIT Imports: 16 Imported by: 18

README

scim-logo

GoVersion GoDoc

Tag

This is an open source implementation of the SCIM v2.0 specification for use in Golang. SCIM defines a flexible schema mechanism and REST API for managing identity data. The goal is to reduce the complexity of user management operations by providing patterns for exchanging schemas using HTTP.

In this implementation it is easy to add custom schemas and extensions with the provided structures. Incoming resources will be validated by their corresponding schemas before being passed on to their callbacks.

The following features are supported:

  • GET for /Schemas, /ServiceProviderConfig and /ResourceTypes
  • CRUD (POST/GET/PUT/DELETE and PATCH) for your own resource types (i.e. /Users, /Groups, /Employees, ...)

Other optional features such as sorting, bulk, etc. are not supported in this version.

Installation

Assuming you already have a (recent) version of Go installed, you can get the code with go get:

$ go get github.com/elimity-com/scim

Usage

! errors are ignored for simplicity.

1. Create a service provider configuration.

RFC Config | Example Config

config := scim.ServiceProviderConfig{
    DocumentationURI: optional.NewString("www.example.com/scim"),
}

! no additional features/operations are supported in this version.

2. Create all supported schemas and extensions.

RFC Schema | User Schema | Group Schema | Extension Schema

schema := schema.Schema{
    ID:          "urn:ietf:params:scim:schemas:core:2.0:User",
    Name:        optional.NewString("User"),
    Description: optional.NewString("User Account"),
    Attributes:  []schema.CoreAttribute{
        schema.SimpleCoreAttribute(schema.SimpleStringParams(schema.StringParams{
            Name:       "userName",
            Required:   true,
            Uniqueness: schema.AttributeUniquenessServer(),
        })),
    },
}

extension := schema.Schema{
    ID:          "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User",
    Name:        optional.NewString("EnterpriseUser"),
    Description: optional.NewString("Enterprise User"),
    Attributes: []schema.CoreAttribute{
        schema.SimpleCoreAttribute(schema.SimpleStringParams(schema.StringParams{
            Name: "employeeNumber",
        })),
        schema.SimpleCoreAttribute(schema.SimpleStringParams(schema.StringParams{
            Name: "organization",
        })),
    },
}

3. Create all resource types and their callbacks.

RFC Resource Type | Example Resource Type

3.1 Callback (implementation of ResourceHandler)

Simple In Memory Example

var userResourceHandler scim.ResourceHandler
// initialize w/ own implementation

! each resource type should have its own resource handler.

3.2 Resource Type
resourceTypes := []ResourceType{
    {
        ID:          optional.NewString("User"),
        Name:        "User",
        Endpoint:    "/Users",
        Description: optional.NewString("User Account"),
        Schema:      schema,
        SchemaExtensions: []SchemaExtension{
            {Schema: extension},
        },
        Handler:     userResourceHandler,
    },
},

4. Create Server

serverArgs := &ServerArgs{
    ServiceProviderConfig: config,
    ResourceTypes: resourceTypes,
}

serverOpts := []ServerOption{
    WithLogger(logger), // optional, default is no logging
}

server, err := NewServer(serverArgs, serverOpts...)

Backwards Compatibility

Even though the SCIM package has been running in some production environments, it is still in an early stage, and not all features are supported. So be aware that a change in the minor version could break your implementation. We will not make any breaking changes that takes hours to fix, but some functions might change name or signature.

This was the case for v0.1 to v0.2.0.

String Values for Attributes

By default, the SCIM server will NOT use the string type for all attributes, since this is NOT compliant with the SCIM specification. It is still possible to enable this behavior by toggling a flag within the schema package.

import "github.com/elimity-com/scim/schema"

schema.SetAllowStringValues(true)

Addition Checks/Tests

Not everything can be checked by the SCIM server itself. Below are some things listed that we expect that the implementation covers.

! this list is currently incomplete!

We want to keep this list as short as possible. If you have ideas how we could enforce these rules in the server itself do not hesitate to open an issue or a PR.

Mutability

Immutable Attributes

PUT Handler: If one or more values are already set for the attribute, the input value(s) MUST match.

WriteOnly Attributes

ALL Handlers: Attribute values SHALL NOT be returned.
Note: These attributes usually also has a returned setting of "never".

Contributing

Contributors

We are happy to review pull requests, but please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change.

If you would like to propose a change please ensure the following:

  • All checks of GitHub Actions are passing (GolangCI-Lint: misspell, godot and whitespace)
  • All already existing tests are passing.
  • You have written tests that cover the code you are making, make sure to include edge cases.
  • There is documentation for at least all public functions you have added.
  • New public functions and structures are kept to a minimum.
  • The same practices are applied (such as the anatomy of methods, names, etc.)
  • Your changes are compliant with SCIM v2.0 (released as RFC7642, RFC7643 and RFC7644 under IETF).

Documentation

Index

Examples

Constants

View Source
const (
	// PatchOperationAdd is used to add a new attribute value to an existing resource.
	PatchOperationAdd = "add"
	// PatchOperationRemove removes the value at the target location specified by the required attribute "path".
	PatchOperationRemove = "remove"
	// PatchOperationReplace replaces the value at the target location specified by the "path".
	PatchOperationReplace = "replace"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type AuthenticationScheme

type AuthenticationScheme struct {
	// Type is the authentication scheme. This specification defines the values "oauth", "oauth2", "oauthbearertoken",
	// "httpbasic", and "httpdigest".
	Type AuthenticationType
	// Name is the common authentication scheme name, e.g., HTTP Basic.
	Name string
	// Description of the authentication scheme.
	Description string
	// SpecURI is an HTTP-addressable URL pointing to the authentication scheme's specification.
	SpecURI optional.String
	// DocumentationURI is an HTTP-addressable URL pointing to the authentication scheme's usage documentation.
	DocumentationURI optional.String
	// Primary is a boolean value indicating the 'primary' or preferred authentication scheme.
	Primary bool
}

AuthenticationScheme specifies a supported authentication scheme property.

type AuthenticationType

type AuthenticationType string

AuthenticationType is a single keyword indicating the authentication type of the authentication scheme.

const (
	// AuthenticationTypeOauth indicates that the authentication type is OAuth.
	AuthenticationTypeOauth AuthenticationType = "oauth"
	// AuthenticationTypeOauth2 indicates that the authentication type is OAuth2.
	AuthenticationTypeOauth2 AuthenticationType = "oauth2"
	// AuthenticationTypeOauthBearerToken indicates that the authentication type is OAuth2 Bearer Token.
	AuthenticationTypeOauthBearerToken AuthenticationType = "oauthbearertoken"
	// AuthenticationTypeHTTPBasic indicated that the authentication type is Basic Access Authentication.
	AuthenticationTypeHTTPBasic AuthenticationType = "httpbasic"
	// AuthenticationTypeHTTPDigest indicated that the authentication type is Digest Access Authentication.
	AuthenticationTypeHTTPDigest AuthenticationType = "httpdigest"
)

type ListRequestParams

type ListRequestParams struct {
	// Count specifies the desired maximum number of query results per page. A negative value SHALL be interpreted as "0".
	// A value of "0" indicates that no resource results are to be returned except for "totalResults".
	Count int

	// FilterValidator represents the parsed and tokenized filter query parameter.
	// It is an optional parameter and thus will be nil when the parameter is not present.
	FilterValidator *filter.Validator

	// StartIndex The 1-based index of the first query result. A value less than 1 SHALL be interpreted as 1.
	StartIndex int
}

ListRequestParams request parameters sent to the API via a "GetAll" route.

type Logger

type Logger interface {
	Error(args ...interface{})
}

Logger defines an interface for logging errors.

type Meta

type Meta struct {
	// Created is the time that the resource was added to the service provider.
	Created *time.Time
	// LastModified is the most recent time that the details of this resource were updated at the service provider.
	LastModified *time.Time
	// Version is the version / entity-tag of the resource
	Version string
}

Meta represents the metadata of a resource.

type Page

type Page struct {
	// TotalResults is the total number of results returned by the list or query operation.
	TotalResults int
	// Resources is a multi-valued list of complex objects containing the requested resources.
	Resources []Resource
}

Page represents a paginated resource query response.

type PatchOperation

type PatchOperation struct {
	// Op indicates the operation to perform and MAY be one of "add", "remove", or "replace".
	Op string
	// Path contains an attribute path describing the target of the operation. The "path" attribute is OPTIONAL for
	// "add" and "replace" and is REQUIRED for "remove" operations.
	Path *filter.Path
	// Value specifies the value to be added or replaced.
	Value interface{}
}

PatchOperation represents a single PATCH operation.

type Resource

type Resource struct {
	// ID is the unique identifier created by the callback method "Create".
	ID string
	// ExternalID is an identifier for the resource as defined by the provisioning client.
	ExternalID optional.String
	// Attributes is a list of attributes defining the resource.
	Attributes ResourceAttributes
	// Meta contains dates and the version of the resource.
	Meta Meta
}

Resource represents an entity returned by a callback method.

type ResourceAttributes

type ResourceAttributes map[string]interface{}

ResourceAttributes represents a list of attributes given to the callback method to create or replace a resource based on the given attributes.

type ResourceHandler

type ResourceHandler interface {
	// Create stores given attributes. Returns a resource with the attributes that are stored and a (new) unique identifier.
	Create(r *http.Request, attributes ResourceAttributes) (Resource, error)
	// Get returns the resource corresponding with the given identifier.
	Get(r *http.Request, id string) (Resource, error)
	// GetAll returns a paginated list of resources.
	// An empty list of resources will be represented as `null` in the JSON response if `nil` is assigned to the
	// Page.Resources. Otherwise, is an empty slice is assigned, an empty list will be represented as `[]`.
	GetAll(r *http.Request, params ListRequestParams) (Page, error)
	// Replace replaces ALL existing attributes of the resource with given identifier. Given attributes that are empty
	// are to be deleted. Returns a resource with the attributes that are stored.
	Replace(r *http.Request, id string, attributes ResourceAttributes) (Resource, error)
	// Delete removes the resource with corresponding ID.
	Delete(r *http.Request, id string) error
	// Patch update one or more attributes of a SCIM resource using a sequence of
	// operations to "add", "remove", or "replace" values.
	// If you return no Resource.Attributes, a 204 No Content status code will be returned.
	// This case is only valid in the following scenarios:
	// 1. the Add/Replace operation should return No Content only when the value already exists AND is the same.
	// 2. the Remove operation should return No Content when the value to be remove is already absent.
	// More information in Section 3.5.2 of RFC 7644: https://tools.ietf.org/html/rfc7644#section-3.5.2
	Patch(r *http.Request, id string, operations []PatchOperation) (Resource, error)
}

ResourceHandler represents a set of callback method that connect the SCIM server with a provider of a certain resource.

Example
var r interface{} = testResourceHandler{}
_, ok := r.(ResourceHandler)
fmt.Println(ok)
Output:
true

type ResourceSearcher

type ResourceSearcher interface {
	Search(r *http.Request, params SearchParams) (Page, error)
}

ResourceSearcher is an optional interface that a ResourceHandler can implement to support POST /.search on a resource endpoint (e.g. POST /Users/.search). Per RFC 7644 Section 3.4.3, this provides an alternative to GET with query parameters.

type ResourceType

type ResourceType struct {
	// ID is the resource type's server unique id. This is often the same value as the "name" attribute.
	ID optional.String
	// Name is the resource type name. This name is referenced by the "meta.resourceType" attribute in all resources.
	Name string
	// Description is the resource type's human-readable description.
	Description optional.String
	// Endpoint is the resource type's HTTP-addressable endpoint relative to the Base URL of the service provider,
	// e.g., "/Users".
	Endpoint string
	// Schema is the resource type's primary/base schema.
	Schema schema.Schema
	// SchemaExtensions is a list of the resource type's schema extensions.
	SchemaExtensions []SchemaExtension

	// Handler is the set of callback method that connect the SCIM server with a provider of the resource type.
	Handler ResourceHandler
}

ResourceType specifies the metadata about a resource type.

type ResourceTypeFilter

type ResourceTypeFilter struct {
	// ResourceType is the resource type whose schema the filter validated against.
	ResourceType ResourceType
	// Validator is the filter validator for this resource type's schema.
	Validator filter.Validator
}

ResourceTypeFilter associates a resource type with a validated filter.

func ValidateFilterForResourceTypes

func ValidateFilterForResourceTypes(rawFilter string, resourceTypes []ResourceType) []ResourceTypeFilter

ValidateFilterForResourceTypes validates a raw filter expression against each of the given resource types' schemas. It returns a ResourceTypeFilter for each resource type whose schema the filter is valid for. This is useful for RootQueryHandler implementations to determine which resource types a filter applies to. An empty result means the filter is not valid for any of the given resource types. A parse error in the filter expression results in an empty result.

type RootQueryHandler

type RootQueryHandler interface {
	// GetAll returns a paginated list of resources across all resource types.
	GetAll(r *http.Request, params ListRequestParams) (Page, error)
}

RootQueryHandler represents an optional callback that handles queries against the server root endpoint (GET /). Per RFC 7644 Section 3.4.2.1, a query against the server root indicates that all resources within the server shall be included, subject to filtering.

The server does not validate or parse the filter for root queries because there is no single target schema. ListRequestParams.FilterValidator will always be nil for root queries. The raw filter string can be obtained from the request via r.URL.Query().Get("filter"). The handler is responsible for interpreting the filter (e.g. meta.resourceType eq "User") as appropriate for its backing store.

type SchemaExtension

type SchemaExtension struct {
	// Schema is the URI of an extended schema, e.g., "urn:edu:2.0:Staff".
	Schema schema.Schema
	// Required is a boolean value that specifies whether or not the schema extension is required for the resource
	// type. If true, a resource of this type MUST include this schema extension and also include any attributes
	// declared as required in this schema extension. If false, a resource of this type MAY omit this schema
	// extension.
	Required bool
}

SchemaExtension is one of the resource type's schema extensions.

type SearchParams

type SearchParams struct {
	// Attributes is a list of attribute names to return in the response.
	// If empty, all attributes are returned.
	Attributes []string

	// Count specifies the desired maximum number of query results per page.
	Count int

	// ExcludedAttributes is a list of attribute names to exclude from the response.
	ExcludedAttributes []string

	// Filter is the raw filter expression string.
	Filter string

	// FilterValidator represents the parsed and tokenized filter.
	// For resource-level search, this is set by the server after validating
	// against the resource type's schema. For root-level search, this is nil.
	FilterValidator *filter.Validator

	// SortBy specifies the attribute whose value will be used to order the returned responses.
	SortBy string

	// SortOrder specifies the order in which the sortBy parameter is applied. Allowed values
	// are "ascending" and "descending".
	SortOrder string

	// StartIndex is the 1-based index of the first query result.
	StartIndex int
}

SearchParams contains the parameters from a POST /.search request body. Per RFC 7644 Section 3.4.3, this includes all query parameters that would normally be sent as URL query parameters in a GET request.

type Server

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

Server represents a SCIM server which implements the HTTP-based SCIM protocol that makes managing identities in multi-domain scenarios easier to support via a standardized service.

func NewServer

func NewServer(args *ServerArgs, opts ...ServerOption) (Server, error)
Example
args := &ServerArgs{
	ServiceProviderConfig: &ServiceProviderConfig{},
	ResourceTypes:         []ResourceType{},
}
server, err := NewServer(args)
if err != nil {
	logger.Fatal(err)
}
logger.Fatal(http.ListenAndServe(":7643", server))
Example (BasePath)
args := &ServerArgs{
	ServiceProviderConfig: &ServiceProviderConfig{},
	ResourceTypes:         []ResourceType{},
}
server, err := NewServer(args)
if err != nil {
	logger.Fatal(err)
}
// You can host the SCIM server on a custom path, make sure to strip the prefix, so only `/v2/` is left.
http.Handle("/scim/", http.StripPrefix("/scim", server))
logger.Fatal(http.ListenAndServe(":7643", nil))
Example (Logger)
loggingMiddleware := func(next http.Handler) http.Handler {
	fn := func(w http.ResponseWriter, r *http.Request) {
		logger.Println(r.Method, r.URL.Path)

		next.ServeHTTP(w, r)
	}

	return http.HandlerFunc(fn)
}
args := &ServerArgs{
	ServiceProviderConfig: &ServiceProviderConfig{},
	ResourceTypes:         []ResourceType{},
}
server, err := NewServer(args)
if err != nil {
	logger.Fatal(err)
}
logger.Fatal(http.ListenAndServe(":7643", loggingMiddleware(server)))

func (Server) ServeHTTP

func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP dispatches the request to the handler whose pattern most closely matches the request URL.

type ServerArgs

type ServerArgs struct {
	ServiceProviderConfig *ServiceProviderConfig
	ResourceTypes         []ResourceType
}

type ServerOption

type ServerOption func(*Server)

func WithBaseURL

func WithBaseURL(baseURL string) ServerOption

WithBaseURL configures the server to use absolute URIs for resource locations. The base URL is prepended to all meta.location values and Location headers. For example, "https://example.com/v2".

func WithLogger

func WithLogger(logger Logger) ServerOption

WithLogger sets the logger for the server.

func WithRootQueryHandler

func WithRootQueryHandler(h RootQueryHandler) ServerOption

WithRootQueryHandler sets a handler for queries against the server root endpoint (GET /). Per RFC 7644 Section 3.4.2.1, a query against the server root indicates that all resources within the server shall be included, subject to filtering.

type ServiceProviderConfig

type ServiceProviderConfig struct {
	// DocumentationURI is an HTTP-addressable URL pointing to the service provider's human-consumable help
	// documentation.
	DocumentationURI optional.String
	// AuthenticationSchemes is a multi-valued complex type that specifies supported authentication scheme properties.
	AuthenticationSchemes []AuthenticationScheme
	// MaxResults denotes the the integer value specifying the maximum number of resources returned in a response. It defaults to 100.
	MaxResults int
	// SupportFiltering whether you SCIM implementation will support filtering.
	SupportFiltering bool
	// SupportPatch whether your SCIM implementation will support patch requests.
	SupportPatch bool
}

ServiceProviderConfig enables a service provider to discover SCIM specification features in a standardized form as well as provide additional implementation details to clients.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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