mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-23 02:35:49 +08:00

* 111 * fix: use user.UpdatedTime as scim.Meta.Version instead of user.Id --------- Co-authored-by: hsluoyz <hsluoyz@qq.com>
239 lines
6.0 KiB
Go
239 lines
6.0 KiB
Go
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package scim
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/casdoor/casdoor/object"
|
|
"github.com/casdoor/casdoor/util"
|
|
"github.com/elimity-com/scim"
|
|
"github.com/elimity-com/scim/optional"
|
|
"github.com/elimity-com/scim/schema"
|
|
)
|
|
|
|
type AnyMap map[string]interface{}
|
|
|
|
type AnyArray []interface{}
|
|
|
|
func ToString(v interface{}, defaultV ...interface{}) string {
|
|
if v == nil {
|
|
if len(defaultV) > 0 {
|
|
v = defaultV[0]
|
|
}
|
|
}
|
|
return v.(string)
|
|
}
|
|
|
|
func ToAnyMap(v interface{}, defaultV ...interface{}) AnyMap {
|
|
if v == nil {
|
|
if len(defaultV) > 0 {
|
|
v = defaultV[0]
|
|
}
|
|
}
|
|
m, ok := v.(map[string]interface{})
|
|
if !ok {
|
|
m = v.(AnyMap)
|
|
}
|
|
return m
|
|
}
|
|
|
|
func ToAnyArray(v interface{}, defaultV ...interface{}) AnyArray {
|
|
if v == nil {
|
|
if len(defaultV) > 0 {
|
|
v = defaultV[0]
|
|
}
|
|
}
|
|
m, ok := v.([]interface{})
|
|
if !ok {
|
|
m = v.(AnyArray)
|
|
}
|
|
return m
|
|
}
|
|
|
|
func newStringParams(name string, required, unique bool) schema.SimpleParams {
|
|
uniqueness := schema.AttributeUniquenessNone()
|
|
if unique {
|
|
uniqueness = schema.AttributeUniquenessServer()
|
|
}
|
|
return schema.SimpleStringParams(schema.StringParams{
|
|
Name: name,
|
|
Required: required,
|
|
Uniqueness: uniqueness,
|
|
})
|
|
}
|
|
|
|
func newComplexParams(name string, required bool, multi bool, subAttributes []schema.SimpleParams) schema.ComplexParams {
|
|
return schema.ComplexParams{
|
|
Name: name,
|
|
Required: required,
|
|
MultiValued: multi,
|
|
SubAttributes: subAttributes,
|
|
}
|
|
}
|
|
|
|
func buildExternalId(user *object.User) optional.String {
|
|
if user.ExternalId != "" {
|
|
return optional.NewString(user.ExternalId)
|
|
} else {
|
|
return optional.String{}
|
|
}
|
|
}
|
|
|
|
func buildMeta(user *object.User) scim.Meta {
|
|
createdTime := util.String2Time(user.CreatedTime)
|
|
updatedTime := util.String2Time(user.UpdatedTime)
|
|
if user.UpdatedTime == "" {
|
|
updatedTime = createdTime
|
|
}
|
|
return scim.Meta{
|
|
Created: &createdTime,
|
|
LastModified: &updatedTime,
|
|
Version: util.Time2String(updatedTime),
|
|
}
|
|
}
|
|
|
|
func getAttrString(attrs scim.ResourceAttributes, key string) string {
|
|
if attrs[key] == nil {
|
|
return ""
|
|
} else {
|
|
return attrs[key].(string)
|
|
}
|
|
}
|
|
|
|
func getAttrJson(attrs scim.ResourceAttributes, key string) scim.ResourceAttributes {
|
|
if attrs[key] == nil {
|
|
return nil
|
|
} else {
|
|
if v, ok := attrs[key].(map[string]interface{}); ok {
|
|
return v
|
|
} else if v, ok := attrs[key].([]interface{}); ok {
|
|
if len(v) > 0 {
|
|
return v[0].(map[string]interface{})
|
|
} else {
|
|
return nil
|
|
}
|
|
} else {
|
|
panic("invalid attribute type")
|
|
}
|
|
}
|
|
}
|
|
|
|
func getAttrJsonValue(attrs scim.ResourceAttributes, key1 string, key2 string) string {
|
|
attr := getAttrJson(attrs, key1)
|
|
if attr == nil {
|
|
return ""
|
|
} else {
|
|
return getAttrString(attr, key2)
|
|
}
|
|
}
|
|
|
|
func user2resource(user *object.User) *scim.Resource {
|
|
attrs := make(map[string]interface{})
|
|
// Singular attributes
|
|
attrs["userName"] = user.Name
|
|
// The cleartext value or the hashed value of a password SHALL NOT be returnable by a service provider.
|
|
// attrs["password"] = user.Password
|
|
formatted := fmt.Sprintf("%s %s", user.FirstName, user.LastName)
|
|
if user.FirstName == "" {
|
|
formatted = user.LastName
|
|
}
|
|
if user.LastName == "" {
|
|
formatted = user.FirstName
|
|
}
|
|
attrs["name"] = scim.ResourceAttributes{
|
|
"formatted": formatted,
|
|
"familyName": user.LastName,
|
|
"givenName": user.FirstName,
|
|
}
|
|
attrs["displayName"] = user.DisplayName
|
|
attrs["nickName"] = user.DisplayName
|
|
attrs["userType"] = user.Type
|
|
attrs["profileUrl"] = user.Homepage
|
|
attrs["active"] = !user.IsForbidden && !user.IsDeleted
|
|
|
|
// Multi-Valued attributes
|
|
attrs["emails"] = []scim.ResourceAttributes{
|
|
{
|
|
"value": user.Email,
|
|
},
|
|
}
|
|
attrs["phoneNumbers"] = []scim.ResourceAttributes{
|
|
{
|
|
"value": user.Phone,
|
|
},
|
|
}
|
|
attrs["photos"] = []scim.ResourceAttributes{
|
|
{
|
|
"value": user.Avatar,
|
|
},
|
|
}
|
|
attrs["addresses"] = []scim.ResourceAttributes{
|
|
{
|
|
"locality": user.Location, // e.g. Hollywood
|
|
"region": user.Region, // e.g. CN
|
|
"country": user.CountryCode, // e.g. USA
|
|
},
|
|
}
|
|
|
|
// Enterprise user schema extension
|
|
attrs[UserExtensionKey] = scim.ResourceAttributes{
|
|
"organization": user.Owner,
|
|
}
|
|
|
|
return &scim.Resource{
|
|
ID: user.Id,
|
|
ExternalID: buildExternalId(user),
|
|
Attributes: attrs,
|
|
Meta: buildMeta(user),
|
|
}
|
|
}
|
|
|
|
func resource2user(attrs scim.ResourceAttributes) (user *object.User, err error) {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
log.Printf("failed to parse attrs: %v", r)
|
|
err = fmt.Errorf("%v", r)
|
|
}
|
|
}()
|
|
user = &object.User{
|
|
ExternalId: getAttrString(attrs, "externalId"),
|
|
Name: getAttrString(attrs, "userName"),
|
|
Password: getAttrString(attrs, "password"),
|
|
DisplayName: getAttrString(attrs, "displayName"),
|
|
Homepage: getAttrString(attrs, "profileUrl"),
|
|
Type: getAttrString(attrs, "userType"),
|
|
|
|
Owner: getAttrJsonValue(attrs, UserExtensionKey, "organization"),
|
|
FirstName: getAttrJsonValue(attrs, "name", "givenName"),
|
|
LastName: getAttrJsonValue(attrs, "name", "familyName"),
|
|
Email: getAttrJsonValue(attrs, "emails", "value"),
|
|
Phone: getAttrJsonValue(attrs, "phoneNumbers", "value"),
|
|
Avatar: getAttrJsonValue(attrs, "photos", "value"),
|
|
Location: getAttrJsonValue(attrs, "addresses", "locality"),
|
|
Region: getAttrJsonValue(attrs, "addresses", "region"),
|
|
CountryCode: getAttrJsonValue(attrs, "addresses", "country"),
|
|
|
|
CreatedTime: util.GetCurrentTime(),
|
|
UpdatedTime: util.GetCurrentTime(),
|
|
}
|
|
|
|
if user.Owner == "" {
|
|
err = fmt.Errorf("organization in %s is required", UserExtensionKey)
|
|
}
|
|
return
|
|
}
|