feat: get user scim v2 endpoint (#9161)

# Which Problems Are Solved
- Adds support for the get user SCIM v2 endpoint

# How the Problems Are Solved
- Adds support for the get user SCIM v2 endpoint under `GET
/scim/v2/{orgID}/Users/{id}`

# Additional Context
Part of #8140
Replaces https://github.com/zitadel/zitadel/pull/9154 as requested by
the maintainers, discussions see
https://github.com/zitadel/zitadel/pull/9154.
This commit is contained in:
Lars
2025-01-10 12:15:06 +01:00
committed by GitHub
parent b0bcb051fc
commit 9c7f2a7d50
11 changed files with 744 additions and 15 deletions

View File

@@ -12,9 +12,49 @@ import (
"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/query"
"github.com/zitadel/zitadel/internal/zerrors"
)
func (h *UsersHandler) queryMetadataForUser(ctx context.Context, id string) (map[metadata.ScopedKey][]byte, error) {
queries := h.buildMetadataQueries(ctx)
md, err := h.query.SearchUserMetadata(ctx, false, id, queries, false)
if err != nil {
return nil, err
}
metadataMap := make(map[metadata.ScopedKey][]byte, len(md.Metadata))
for _, entry := range md.Metadata {
metadataMap[metadata.ScopedKey(entry.Key)] = entry.Value
}
return metadataMap, nil
}
func (h *UsersHandler) buildMetadataQueries(ctx context.Context) *query.UserMetadataSearchQueries {
keyQueries := make([]query.SearchQuery, len(metadata.ScimUserRelevantMetadataKeys))
for i, key := range metadata.ScimUserRelevantMetadataKeys {
keyQueries[i] = buildMetadataKeyQuery(ctx, key)
}
queries := &query.UserMetadataSearchQueries{
SearchRequest: query.SearchRequest{},
Queries: []query.SearchQuery{query.Or(keyQueries...)},
}
return queries
}
func buildMetadataKeyQuery(ctx context.Context, key metadata.Key) query.SearchQuery {
scopedKey := metadata.ScopeKey(ctx, key)
q, err := query.NewUserMetadataKeySearchQuery(string(scopedKey), query.TextEquals)
if err != nil {
logging.Panic("Error build user metadata query for key " + key)
}
return q
}
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 {
@@ -51,7 +91,17 @@ func getValueForMetadataKey(user *ScimUser, key metadata.Key) ([]byte, error) {
case metadata.KeyAddresses:
fallthrough
case metadata.KeyRoles:
return json.Marshal(value)
val, err := json.Marshal(value)
if err != nil {
return nil, err
}
// null is considered no value
if len(val) == 4 && string(val) == "null" {
return nil, nil
}
return val, nil
// http url values
case metadata.KeyProfileUrl:
@@ -148,3 +198,36 @@ func getRawValueForMetadataKey(user *ScimUser, key metadata.Key) interface{} {
logging.Panicf("Unknown or unsupported metadata key %s", key)
return nil
}
func extractScalarMetadata(ctx context.Context, md map[metadata.ScopedKey][]byte, key metadata.Key) string {
val, ok := md[metadata.ScopeKey(ctx, key)]
if !ok {
return ""
}
return string(val)
}
func extractHttpURLMetadata(ctx context.Context, md map[metadata.ScopedKey][]byte, key metadata.Key) *schemas.HttpURL {
val, ok := md[metadata.ScopeKey(ctx, key)]
if !ok {
return nil
}
url, err := schemas.ParseHTTPURL(string(val))
if err != nil {
logging.OnError(err).Warn("Failed to parse scim url metadata for " + key)
return nil
}
return url
}
func extractJsonMetadata(ctx context.Context, md map[metadata.ScopedKey][]byte, key metadata.Key, v interface{}) error {
val, ok := md[metadata.ScopeKey(ctx, key)]
if !ok {
return nil
}
return json.Unmarshal(val, v)
}