mirror of
https://github.com/zitadel/zitadel.git
synced 2025-03-02 22:11:46 +00:00

# Which Problems Are Solved * Adds support for the service provider configuration SCIM v2 endpoints # How the Problems Are Solved * Adds support for the service provider configuration SCIM v2 endpoints * `GET /scim/v2/{orgId}/ServiceProviderConfig` * `GET /scim/v2/{orgId}/ResourceTypes` * `GET /scim/v2/{orgId}/ResourceTypes/{name}` * `GET /scim/v2/{orgId}/Schemas` * `GET /scim/v2/{orgId}/Schemas/{id}` # Additional Context Part of #8140 Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
183 lines
6.6 KiB
Go
183 lines
6.6 KiB
Go
package scim
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
zhttp "github.com/zitadel/zitadel/internal/api/http"
|
|
scim_config "github.com/zitadel/zitadel/internal/api/scim/config"
|
|
sresources "github.com/zitadel/zitadel/internal/api/scim/resources"
|
|
sschemas "github.com/zitadel/zitadel/internal/api/scim/schemas"
|
|
"github.com/zitadel/zitadel/internal/query"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
type serviceProviderHandler struct {
|
|
config *scim_config.Config
|
|
schemas []*sschemas.ResourceSchema
|
|
schemasByID map[sschemas.ScimSchemaType]*sschemas.ResourceSchema
|
|
schemasByResourceName map[sschemas.ScimResourceTypeSingular]*sschemas.ResourceSchema
|
|
}
|
|
|
|
type serviceProviderConfig struct {
|
|
*sschemas.Resource
|
|
DocumentationUri string `json:"documentationUri"`
|
|
Patch serviceProviderConfigSupported `json:"patch"`
|
|
Bulk serviceProviderConfigBulk `json:"bulk"`
|
|
Filter serviceProviderFilterSupported `json:"filter"`
|
|
ChangePassword serviceProviderConfigSupported `json:"changePassword"`
|
|
Sort serviceProviderConfigSupported `json:"sort"`
|
|
ETag serviceProviderConfigSupported `json:"etag"`
|
|
AuthenticationSchemes []*scim_config.ServiceProviderConfigAuthenticationScheme `json:"authenticationSchemes,omitempty"`
|
|
}
|
|
|
|
type serviceProviderConfigSupported struct {
|
|
Supported bool `json:"supported"`
|
|
}
|
|
|
|
type serviceProviderFilterSupported struct {
|
|
Supported bool `json:"supported"`
|
|
MaxResults int `json:"maxResults"`
|
|
}
|
|
|
|
type serviceProviderConfigBulk struct {
|
|
Supported bool `json:"supported"`
|
|
MaxOperations int `json:"maxOperations"`
|
|
MaxPayloadSize int64 `json:"maxPayloadSize"`
|
|
}
|
|
|
|
var (
|
|
defaultConfigSearchRequest = query.SearchRequest{
|
|
Offset: 0,
|
|
Limit: 100,
|
|
}
|
|
)
|
|
|
|
func newServiceProviderHandler(cfg *scim_config.Config, handlers ...sresources.RawResourceHandlerAdapter) *serviceProviderHandler {
|
|
schemas := make([]*sschemas.ResourceSchema, len(handlers))
|
|
schemasByID := make(map[sschemas.ScimSchemaType]*sschemas.ResourceSchema, len(handlers))
|
|
schemasByResourceName := make(map[sschemas.ScimResourceTypeSingular]*sschemas.ResourceSchema, len(handlers))
|
|
for i, handler := range handlers {
|
|
schema := handler.Schema()
|
|
schemas[i] = schema
|
|
schemasByID[schema.ID] = schema
|
|
schemasByResourceName[schema.Name] = schema
|
|
}
|
|
|
|
return &serviceProviderHandler{
|
|
config: cfg,
|
|
schemas: schemas,
|
|
schemasByID: schemasByID,
|
|
schemasByResourceName: schemasByResourceName,
|
|
}
|
|
}
|
|
|
|
func (h *serviceProviderHandler) GetConfig(r *http.Request) (*serviceProviderConfig, error) {
|
|
// the request is unauthenticated, read the orgID from the url instead of the context
|
|
orgID := mux.Vars(r)[zhttp.OrgIdInPathVariableName]
|
|
return &serviceProviderConfig{
|
|
Resource: &sschemas.Resource{
|
|
Schemas: []sschemas.ScimSchemaType{sschemas.IdServiceProviderConfig},
|
|
Meta: &sschemas.ResourceMeta{
|
|
ResourceType: sschemas.ServiceProviderConfigResourceType,
|
|
Location: sschemas.BuildLocationWithOrg(r.Context(), orgID, sschemas.ServiceProviderConfigsResourceType, ""),
|
|
},
|
|
},
|
|
DocumentationUri: h.config.DocumentationUrl,
|
|
Patch: serviceProviderConfigSupported{
|
|
Supported: true,
|
|
},
|
|
Bulk: serviceProviderConfigBulk{
|
|
Supported: true,
|
|
MaxOperations: h.config.Bulk.MaxOperationsCount,
|
|
MaxPayloadSize: h.config.MaxRequestBodySize,
|
|
},
|
|
Filter: serviceProviderFilterSupported{
|
|
Supported: true,
|
|
MaxResults: sresources.MaxListCount,
|
|
},
|
|
ChangePassword: serviceProviderConfigSupported{
|
|
Supported: true,
|
|
},
|
|
Sort: serviceProviderConfigSupported{
|
|
Supported: true,
|
|
},
|
|
ETag: serviceProviderConfigSupported{
|
|
Supported: false,
|
|
},
|
|
AuthenticationSchemes: h.config.AuthenticationSchemes,
|
|
}, nil
|
|
}
|
|
|
|
func (h *serviceProviderHandler) ListResourceTypes(r *http.Request) (*sresources.ListResponse[*sschemas.ResourceType], error) {
|
|
// the request is unauthenticated, read the orgID from the url instead of the context
|
|
ctx := r.Context()
|
|
orgID := mux.Vars(r)[zhttp.OrgIdInPathVariableName]
|
|
|
|
resourceTypes := make([]*sschemas.ResourceType, len(h.schemas))
|
|
for i, schema := range h.schemas {
|
|
resourceTypes[i] = schema.ToResourceType(ctx, orgID)
|
|
}
|
|
|
|
return sresources.NewListResponse(uint64(len(resourceTypes)), defaultConfigSearchRequest, resourceTypes), nil
|
|
}
|
|
|
|
func (h *serviceProviderHandler) GetResourceType(r *http.Request) (*sschemas.ResourceType, error) {
|
|
// the request is unauthenticated, read the orgID from the url instead of the context
|
|
ctx := r.Context()
|
|
vars := mux.Vars(r)
|
|
orgID := vars[zhttp.OrgIdInPathVariableName]
|
|
name := sschemas.ScimResourceTypeSingular(vars["name"])
|
|
|
|
schema, ok := h.schemasByResourceName[name]
|
|
if !ok {
|
|
return nil, zerrors.ThrowNotFoundf(nil, "SCIMSP-148z", "Scim resource type %s not found", name)
|
|
}
|
|
|
|
return schema.ToResourceType(ctx, orgID), nil
|
|
}
|
|
|
|
func (h *serviceProviderHandler) ListSchemas(r *http.Request) (*sresources.ListResponse[*sschemas.ResourceSchema], error) {
|
|
// the request is unauthenticated, read the orgID from the url instead of the context
|
|
ctx := r.Context()
|
|
orgID := mux.Vars(r)[zhttp.OrgIdInPathVariableName]
|
|
|
|
schemas := make([]*sschemas.ResourceSchema, len(h.schemas))
|
|
for i, schema := range h.schemas {
|
|
schemas[i] = buildSchema(ctx, orgID, schema)
|
|
}
|
|
|
|
return sresources.NewListResponse(uint64(len(h.schemas)), defaultConfigSearchRequest, schemas), nil
|
|
}
|
|
|
|
func (h *serviceProviderHandler) GetSchema(r *http.Request) (*sschemas.ResourceSchema, error) {
|
|
// the request is unauthenticated, read the orgID from the url instead of the context
|
|
ctx := r.Context()
|
|
vars := mux.Vars(r)
|
|
orgID := vars[zhttp.OrgIdInPathVariableName]
|
|
id := sschemas.ScimSchemaType(vars["id"])
|
|
|
|
schema, ok := h.schemasByID[id]
|
|
if !ok {
|
|
return nil, zerrors.ThrowNotFoundf(nil, "SCIMSP-148y", "Scim schema %s not found", id)
|
|
}
|
|
|
|
return buildSchema(ctx, orgID, schema), nil
|
|
}
|
|
|
|
// buildSchema shallow copies the provided schema and sets the correct location based on the provided context information.
|
|
func buildSchema(ctx context.Context, orgID string, schema *sschemas.ResourceSchema) *sschemas.ResourceSchema {
|
|
newSchema := *schema
|
|
newSchema.Resource = &sschemas.Resource{
|
|
ID: schema.Resource.ID,
|
|
Schemas: schema.Resource.Schemas,
|
|
Meta: &sschemas.ResourceMeta{
|
|
ResourceType: schema.Resource.Meta.ResourceType,
|
|
Location: sschemas.BuildLocationWithOrg(ctx, orgID, sschemas.SchemasResourceType, string(schema.ID)),
|
|
},
|
|
}
|
|
return &newSchema
|
|
}
|