mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 03:37:34 +00:00
feat: add scim v2 service provider configuration endpoints (#9258)
# 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>
This commit is contained in:
182
internal/api/scim/service_provider.go
Normal file
182
internal/api/scim/service_provider.go
Normal file
@@ -0,0 +1,182 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user