Documentation
¶
Index ¶
- Constants
- type AuthenticationScheme
- type AuthenticationType
- type ListRequestParams
- type Logger
- type Meta
- type Page
- type PatchOperation
- type Resource
- type ResourceAttributes
- type ResourceHandler
- type ResourceType
- type SchemaExtension
- type Server
- type ServerArgs
- type ServerOption
- type ServiceProviderConfig
Examples ¶
Constants ¶
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
// Filter 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 ¶
package main
import (
"fmt"
"math/rand"
"net/http"
"strings"
"time"
"github.com/elimity-com/scim/errors"
"github.com/elimity-com/scim/optional"
)
func main() {
var r interface{} = testResourceHandler{}
_, ok := r.(ResourceHandler)
fmt.Println(ok)
}
type testData struct {
resourceAttributes ResourceAttributes
meta map[string]string
}
// simple in-memory resource database.
type testResourceHandler struct {
data map[string]testData
}
func (h testResourceHandler) Create(r *http.Request, attributes ResourceAttributes) (Resource, error) {
// create unique identifier
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
id := fmt.Sprintf("%04d", rng.Intn(9999))
// store resource
h.data[id] = testData{
resourceAttributes: attributes,
}
now := time.Now()
// return stored resource
return Resource{
ID: id,
ExternalID: h.externalID(attributes),
Attributes: attributes,
Meta: Meta{
Created: &now,
LastModified: &now,
Version: fmt.Sprintf("v%s", id),
},
}, nil
}
func (h testResourceHandler) Delete(r *http.Request, id string) error {
// check if resource exists
_, ok := h.data[id]
if !ok {
return errors.ScimErrorResourceNotFound(id)
}
// delete resource
delete(h.data, id)
return nil
}
func (h testResourceHandler) Get(r *http.Request, id string) (Resource, error) {
// check if resource exists
data, ok := h.data[id]
if !ok {
return Resource{}, errors.ScimErrorResourceNotFound(id)
}
created, _ := time.ParseInLocation(time.RFC3339, fmt.Sprintf("%v", data.meta["created"]), time.UTC)
lastModified, _ := time.Parse(time.RFC3339, fmt.Sprintf("%v", data.meta["lastModified"]))
// return resource with given identifier
return Resource{
ID: id,
ExternalID: h.externalID(data.resourceAttributes),
Attributes: data.resourceAttributes,
Meta: Meta{
Created: &created,
LastModified: &lastModified,
Version: fmt.Sprintf("%v", data.meta["version"]),
},
}, nil
}
func (h testResourceHandler) GetAll(r *http.Request, params ListRequestParams) (Page, error) {
if params.Count == 0 {
return Page{
TotalResults: len(h.data),
}, nil
}
resources := make([]Resource, 0)
i := 1
for k, v := range h.data {
if i > (params.StartIndex + params.Count - 1) {
break
}
if i >= params.StartIndex {
resources = append(resources, Resource{
ID: k,
ExternalID: h.externalID(v.resourceAttributes),
Attributes: v.resourceAttributes,
})
}
i++
}
return Page{
TotalResults: len(h.data),
Resources: resources,
}, nil
}
func (h testResourceHandler) Patch(r *http.Request, id string, operations []PatchOperation) (Resource, error) {
if h.shouldReturnNoContent(id, operations) {
return Resource{}, nil
}
for _, op := range operations {
switch op.Op {
case PatchOperationAdd:
if op.Path != nil {
h.data[id].resourceAttributes[op.Path.String()] = op.Value
} else {
valueMap := op.Value.(map[string]interface{})
for k, v := range valueMap {
if arr, ok := h.data[id].resourceAttributes[k].([]interface{}); ok {
arr = append(arr, v)
h.data[id].resourceAttributes[k] = arr
} else {
h.data[id].resourceAttributes[k] = v
}
}
}
case PatchOperationReplace:
if op.Path != nil {
h.data[id].resourceAttributes[op.Path.String()] = op.Value
} else {
valueMap := op.Value.(map[string]interface{})
for k, v := range valueMap {
h.data[id].resourceAttributes[k] = v
}
}
case PatchOperationRemove:
h.data[id].resourceAttributes[op.Path.String()] = nil
}
}
created, _ := time.ParseInLocation(time.RFC3339, fmt.Sprintf("%v", h.data[id].meta["created"]), time.UTC)
now := time.Now()
// return resource with replaced attributes
return Resource{
ID: id,
ExternalID: h.externalID(h.data[id].resourceAttributes),
Attributes: h.data[id].resourceAttributes,
Meta: Meta{
Created: &created,
LastModified: &now,
Version: fmt.Sprintf("%s.patch", h.data[id].meta["version"]),
},
}, nil
}
func (h testResourceHandler) Replace(r *http.Request, id string, attributes ResourceAttributes) (Resource, error) {
// check if resource exists
_, ok := h.data[id]
if !ok {
return Resource{}, errors.ScimErrorResourceNotFound(id)
}
// replace (all) attributes
h.data[id] = testData{
resourceAttributes: attributes,
}
// return resource with replaced attributes
return Resource{
ID: id,
ExternalID: h.externalID(attributes),
Attributes: attributes,
}, nil
}
func (h testResourceHandler) externalID(attributes ResourceAttributes) optional.String {
if eID, ok := attributes["externalId"]; ok {
externalID, ok := eID.(string)
if !ok {
return optional.String{}
}
return optional.NewString(externalID)
}
return optional.String{}
}
func (h testResourceHandler) noContentOperation(id string, op PatchOperation) bool {
isRemoveOp := strings.EqualFold(op.Op, PatchOperationRemove)
dataValue, ok := h.data[id]
if !ok {
return isRemoveOp
}
var path string
if op.Path != nil {
path = op.Path.String()
}
attrValue, ok := dataValue.resourceAttributes[path]
if ok && attrValue == op.Value {
return true
}
if !ok && isRemoveOp {
return true
}
switch opValue := op.Value.(type) {
case map[string]interface{}:
for k, v := range opValue {
if v == dataValue.resourceAttributes[k] {
return true
}
}
case []map[string]interface{}:
for _, m := range opValue {
for k, v := range m {
if v == dataValue.resourceAttributes[k] {
return true
}
}
}
}
return false
}
func (h testResourceHandler) shouldReturnNoContent(id string, ops []PatchOperation) bool {
for _, op := range ops {
if h.noContentOperation(id, op) {
continue
}
return false
}
return true
}
Output: true
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 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 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)))
type ServerArgs ¶
type ServerArgs struct {
ServiceProviderConfig *ServiceProviderConfig
ResourceTypes []ResourceType
}
type ServerOption ¶
type ServerOption func(*Server)
func WithLogger ¶
func WithLogger(logger Logger) ServerOption
WithLogger sets the logger for the server.
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.
