mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 07:57:32 +00:00
feat: create user scim v2 endpoint (#9132)
# Which Problems Are Solved - Adds infrastructure code (basic implementation, error handling, middlewares, ...) to implement the SCIM v2 interface - Adds support for the user create SCIM v2 endpoint # How the Problems Are Solved - Adds support for the user create SCIM v2 endpoint under `POST /scim/v2/{orgID}/Users` # Additional Context Part of #8140
This commit is contained in:
150
internal/api/scim/resources/user_metadata.go
Normal file
150
internal/api/scim/resources/user_metadata.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/logging"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/scim/metadata"
|
||||
"github.com/zitadel/zitadel/internal/api/scim/schemas"
|
||||
"github.com/zitadel/zitadel/internal/api/scim/serrors"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
func (h *UsersHandler) mapMetadataToCommands(ctx context.Context, user *ScimUser) ([]*command.AddMetadataEntry, error) {
|
||||
md := make([]*command.AddMetadataEntry, 0, len(metadata.ScimUserRelevantMetadataKeys))
|
||||
for _, key := range metadata.ScimUserRelevantMetadataKeys {
|
||||
value, err := getValueForMetadataKey(user, key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(value) > 0 {
|
||||
md = append(md, &command.AddMetadataEntry{
|
||||
Key: string(metadata.ScopeKey(ctx, key)),
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return md, nil
|
||||
}
|
||||
|
||||
func getValueForMetadataKey(user *ScimUser, key metadata.Key) ([]byte, error) {
|
||||
value := getRawValueForMetadataKey(user, key)
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch key {
|
||||
// json values
|
||||
case metadata.KeyEntitlements:
|
||||
fallthrough
|
||||
case metadata.KeyIms:
|
||||
fallthrough
|
||||
case metadata.KeyPhotos:
|
||||
fallthrough
|
||||
case metadata.KeyAddresses:
|
||||
fallthrough
|
||||
case metadata.KeyRoles:
|
||||
return json.Marshal(value)
|
||||
|
||||
// http url values
|
||||
case metadata.KeyProfileUrl:
|
||||
return []byte(value.(*schemas.HttpURL).String()), nil
|
||||
|
||||
// raw values
|
||||
case metadata.KeyProvisioningDomain:
|
||||
fallthrough
|
||||
case metadata.KeyExternalId:
|
||||
fallthrough
|
||||
case metadata.KeyMiddleName:
|
||||
fallthrough
|
||||
case metadata.KeyHonorificSuffix:
|
||||
fallthrough
|
||||
case metadata.KeyHonorificPrefix:
|
||||
fallthrough
|
||||
case metadata.KeyTitle:
|
||||
fallthrough
|
||||
case metadata.KeyLocale:
|
||||
fallthrough
|
||||
case metadata.KeyTimezone:
|
||||
valueStr := value.(string)
|
||||
if valueStr == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []byte(valueStr), validateValueForMetadataKey(valueStr, key)
|
||||
}
|
||||
|
||||
logging.Panicf("Unknown metadata key %s", key)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func validateValueForMetadataKey(v string, key metadata.Key) error {
|
||||
//nolint:exhaustive
|
||||
switch key {
|
||||
case metadata.KeyLocale:
|
||||
if _, err := language.Parse(v); err != nil {
|
||||
return serrors.ThrowInvalidValue(zerrors.ThrowInvalidArgument(err, "SCIM-MD11", "Could not parse locale"))
|
||||
}
|
||||
return nil
|
||||
case metadata.KeyTimezone:
|
||||
if _, err := time.LoadLocation(v); err != nil {
|
||||
return serrors.ThrowInvalidValue(zerrors.ThrowInvalidArgument(err, "SCIM-MD12", "Could not parse timezone"))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getRawValueForMetadataKey(user *ScimUser, key metadata.Key) interface{} {
|
||||
switch key {
|
||||
case metadata.KeyIms:
|
||||
return user.Ims
|
||||
case metadata.KeyPhotos:
|
||||
return user.Photos
|
||||
case metadata.KeyAddresses:
|
||||
return user.Addresses
|
||||
case metadata.KeyEntitlements:
|
||||
return user.Entitlements
|
||||
case metadata.KeyRoles:
|
||||
return user.Roles
|
||||
case metadata.KeyMiddleName:
|
||||
if user.Name == nil {
|
||||
return ""
|
||||
}
|
||||
return user.Name.MiddleName
|
||||
case metadata.KeyHonorificPrefix:
|
||||
if user.Name == nil {
|
||||
return ""
|
||||
}
|
||||
return user.Name.HonorificPrefix
|
||||
case metadata.KeyHonorificSuffix:
|
||||
if user.Name == nil {
|
||||
return ""
|
||||
}
|
||||
return user.Name.HonorificSuffix
|
||||
case metadata.KeyExternalId:
|
||||
return user.ExternalID
|
||||
case metadata.KeyProfileUrl:
|
||||
return user.ProfileUrl
|
||||
case metadata.KeyTitle:
|
||||
return user.Title
|
||||
case metadata.KeyLocale:
|
||||
return user.Locale
|
||||
case metadata.KeyTimezone:
|
||||
return user.Timezone
|
||||
case metadata.KeyProvisioningDomain:
|
||||
break
|
||||
}
|
||||
|
||||
logging.Panicf("Unknown or unsupported metadata key %s", key)
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user