feature: support idp driver attribtue_names (#25335)

Co-authored-by: Qiu Jian <qiujian@yunionyun.com>
This commit is contained in:
Jian Qiu
2026-08-15 00:16:19 +08:00
committed by GitHub
parent bf4ef9b0f5
commit d50f3d8e7f
27 changed files with 217 additions and 8 deletions

View File

@@ -98,6 +98,12 @@ func (self *SCASDriverClass) ValidateConfig(ctx context.Context, userCred mcclie
return tconf, nil
}
func (self *SCASDriverClass) AttributeNames(template string) (map[string]string, error) {
return map[string]string{
"cas:user": "User name in CAS",
}, nil
}
func init() {
driver.RegisterDriverClass(&SCASDriverClass{})
}

View File

@@ -30,6 +30,7 @@ type IIdentityBackendClass interface {
IsSso() bool
GetDefaultIconUri(tmpName string) string
ForceSyncUser() bool
AttributeNames(template string) (map[string]string, error)
}
type IIdentityBackend interface {

View File

@@ -86,6 +86,10 @@ func (self *SLDAPDriverClass) ValidateConfig(ctx context.Context, userCred mccli
return tconf, nil
}
func (self *SLDAPDriverClass) AttributeNames(template string) (map[string]string, error) {
return nil, nil
}
func init() {
driver.RegisterDriverClass(&SLDAPDriverClass{})
}

View File

@@ -73,5 +73,7 @@ func (drv *SAlipayOAuth2Driver) Authenticate(ctx context.Context, code string) (
attrs[k] = []string{v}
}
attrs["user_name"] = []string{fmt.Sprintf("alipay%s", userInfo["user_id"])}
attrs["user_id"] = []string{userInfo["user_id"]}
attrs["nick_name"] = []string{userInfo["nick_name"]}
return attrs, nil
}

View File

@@ -41,6 +41,14 @@ func (drv SAlipayDriverFactory) ValidateConfig(conf api.SOAuth2IdpConfigOptions)
return nil
}
func (drv SAlipayDriverFactory) AttributeNames() map[string]string {
return map[string]string{
"user_name": "User name in Alipay",
"user_id": "User ID in Alipay",
"nick_name": "Nick name in Alipay",
}
}
func init() {
oauth2.Register(&SAlipayDriverFactory{})
}

View File

@@ -45,6 +45,20 @@ func (drv SBingoIAMDriverFactory) ValidateConfig(conf api.SOAuth2IdpConfigOption
return nil
}
func (drv SBingoIAMDriverFactory) AttributeNames() map[string]string {
return map[string]string{
"tenant_name": "Tenant name in BingoIAM",
"tenant_id": "Tenant ID in BingoIAM",
"name": "User name in BingoIAM",
"name_en": "English user name in BingoIAM",
"user_id": "User ID in BingoIAM",
"display_name": "Display name in BingoIAM",
"email": "Email in BingoIAM",
"mobile": "Mobile in BingoIAM",
"org_id": "Org ID in BingoIAM",
}
}
func init() {
oauth2.Register(&SBingoIAMDriverFactory{})
}

View File

@@ -111,6 +111,14 @@ func (self *SOAuth2DriverClass) ValidateConfig(ctx context.Context, userCred mcc
return tconf, nil
}
func (self *SOAuth2DriverClass) AttributeNames(template string) (map[string]string, error) {
factory := findDriverFactory(template)
if factory == nil {
return nil, errors.Wrapf(httperrors.ErrNotSupported, "template %s not supported", template)
}
return factory.AttributeNames(), nil
}
func init() {
driver.RegisterDriverClass(&SOAuth2DriverClass{})
}

View File

@@ -120,5 +120,7 @@ func (drv *SDingtalkOAuth2Driver) Authenticate(ctx context.Context, code string)
ret := make(map[string][]string)
ret["name"] = []string{data.Nick}
ret["user_id"] = []string{data.Unionid}
ret["open_id"] = []string{data.Openid}
ret["union_id"] = []string{data.Unionid}
return ret, nil
}

View File

@@ -41,6 +41,15 @@ func (drv SDingtalkDriverFactory) ValidateConfig(conf api.SOAuth2IdpConfigOption
return nil
}
func (drv SDingtalkDriverFactory) AttributeNames() map[string]string {
return map[string]string{
"name": "User name in Dingtalk",
"user_id": "UnionID in Dingtalk",
"open_id": "OpenID in Dingtalk",
"union_id": "UnionID in Dingtalk",
}
}
func init() {
oauth2.Register(&SDingtalkDriverFactory{})
}

View File

@@ -31,7 +31,7 @@ func (drv SFeishuDriverFactory) TemplateName() string {
func (drv SFeishuDriverFactory) IdpAttributeOptions() api.SIdpAttributeOptions {
return api.SIdpAttributeOptions{
UserNameAttribute: "name_en",
UserNameAttribute: "name",
UserIdAttribute: "user_id",
UserDisplaynameAttribtue: "name",
UserEmailAttribute: "email",
@@ -43,6 +43,18 @@ func (drv SFeishuDriverFactory) ValidateConfig(conf api.SOAuth2IdpConfigOptions)
return nil
}
func (drv SFeishuDriverFactory) AttributeNames() map[string]string {
return map[string]string{
"en_name": "User name in Feishu",
"user_id": "User ID in Feishu",
"name": "User name in Feishu",
"email": "Email in Feishu",
"mobile": "Mobile in Feishu",
"union_id": "Union ID in Feishu",
"open_id": "Open ID in Feishu",
}
}
func init() {
oauth2.Register(&SFeishuDriverFactory{})
}

View File

@@ -76,6 +76,7 @@ type sAccessTokenData struct {
Name string `json:"name"`
EnName string `json:"en_name"`
OpenID string `json:"open_id"`
UnionID string `json:"union_id"`
TenantKey string `json:"tenant_key"`
RefreshExpiresIn int64 `json:"refresh_expires_in"`
RefreshToken string `json:"refresh_token"`
@@ -177,8 +178,10 @@ func (drv *SFeishuOAuth2Driver) Authenticate(ctx context.Context, code string) (
ret := make(map[string][]string)
ret["name"] = []string{userInfo.Name}
ret["user_id"] = []string{userInfo.UserID}
ret["name_en"] = []string{accessData.EnName}
ret["en_name"] = []string{accessData.EnName}
ret["email"] = []string{userInfo.Email}
ret["mobile"] = []string{userInfo.Mobile}
ret["open_id"] = []string{accessData.OpenID}
ret["union_id"] = []string{accessData.UnionID}
return ret, nil
}

View File

@@ -80,8 +80,9 @@ func (self *SOAuth2Driver) Authenticate(ctx context.Context, ident mcclient.SAut
if factory == nil {
return nil, errors.Wrapf(httperrors.ErrNotSupported, "template %s not supported", self.Template)
}
options := self.oauth2Config.SIdpAttributeOptions
options.Update(factory.IdpAttributeOptions())
options := factory.IdpAttributeOptions()
options.Update(self.oauth2Config.SIdpAttributeOptions)
// options.Update(factory.IdpAttributeOptions())
driver := factory.NewDriver(self.oauth2Config.AppId, self.oauth2Config.Secret)
ctx = context.WithValue(ctx, "config", self.SBaseIdentityDriver.Config)
attrs, err := driver.Authenticate(ctx, ident.OAuth2.Code)

View File

@@ -56,6 +56,16 @@ func (drv SQywxDriverFactory) ValidateConfig(conf api.SOAuth2IdpConfigOptions) e
return nil
}
func (drv SQywxDriverFactory) AttributeNames() map[string]string {
return map[string]string{
"name": "User name in Wecom",
"user_id": "User ID in Wecom",
"displayname": "Display name in Wecom",
"email": "Email in Wecom",
"mobile": "Mobile in Wecom",
}
}
func init() {
oauth2.Register(&SQywxDriverFactory{})
}

View File

@@ -25,6 +25,7 @@ type IOAuth2DriverFactory interface {
TemplateName() string
IdpAttributeOptions() api.SIdpAttributeOptions
ValidateConfig(conf api.SOAuth2IdpConfigOptions) error
AttributeNames() map[string]string
}
type IOAuth2Driver interface {

View File

@@ -41,6 +41,14 @@ func (drv SWechatDriverFactory) ValidateConfig(conf api.SOAuth2IdpConfigOptions)
return nil
}
func (drv SWechatDriverFactory) AttributeNames() map[string]string {
return map[string]string{
"name": "User name",
"user_id": "OpenID of User in Wechat",
"union_id": "UnionID of User in Wechat",
}
}
func init() {
oauth2.Register(&SWechatDriverFactory{})
}

View File

@@ -150,5 +150,6 @@ func (drv *SWechatOAuth2Driver) Authenticate(ctx context.Context, code string) (
ret := make(map[string][]string)
ret["name"] = []string{userInfo.Nickname}
ret["user_id"] = []string{userInfo.Openid}
ret["union_id"] = []string{userInfo.Unionid}
return ret, nil
}

View File

@@ -123,6 +123,20 @@ func (self *SOIDCDriverClass) ValidateConfig(ctx context.Context, userCred mccli
return tconf, nil
}
func (self *SOIDCDriverClass) AttributeNames(template string) (map[string]string, error) {
switch template {
case api.IdpTemplateDex:
return DexOIDCTemplate.AttributeNames, nil
case api.IdpTemplateGithub:
return GithubOIDCTemplate.AttributeNames, nil
case api.IdpTemplateAzureOAuth2:
return AzureADTemplate.AttributeNames, nil
case api.IdpTemplateGoogle:
return GoogleOIDCTemplate.AttributeNames, nil
}
return nil, nil
}
func init() {
driver.RegisterDriverClass(&SOIDCDriverClass{})
}

View File

@@ -31,6 +31,11 @@ var (
UserEmailAttribute: "email",
UserDisplaynameAttribtue: "name",
},
AttributeNames: map[string]string{
"name": "User name in Dex",
"sub": "User ID in Dex",
"email": "User email in Dex",
},
}
// https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/
// map[avatar_url:https://avatars1.githubusercontent.com/u/1121362?v=4 bio: blog:https://yunion.io collaborators:0 company:Yunion.io created_at:2011-10-12T04:18:27Z disk_usage:925302 email: events_url:https://api.github.com/users/swordqiu/events{/privacy} followers:13 followers_url:https://api.github.com/users/swordqiu/followers following:1 following_url:https://api.github.com/users/swordqiu/following{/other_user} gists_url:https://api.github.com/users/swordqiu/gists{/gist_id} gravatar_id: hireable: html_url:https://github.com/swordqiu
@@ -51,6 +56,12 @@ var (
UserEmailAttribute: "email",
UserDisplaynameAttribtue: "name",
},
AttributeNames: map[string]string{
"name": "User name in Github",
"id": "User ID in Github",
"email": "User email in Github",
"login": "User login name in Github",
},
}
// {
@@ -71,6 +82,11 @@ var (
UserEmailAttribute: "email",
UserDisplaynameAttribtue: "name",
},
AttributeNames: map[string]string{
"name": "User name in Google",
"sub": "User ID in Google",
"email": "User email in Google",
},
}
AzureADTemplate = api.SOIDCIdpConfigOptions{
@@ -86,5 +102,10 @@ var (
UserEmailAttribute: "email",
UserDisplaynameAttribtue: "name",
},
AttributeNames: map[string]string{
"name": "User name in Azure OAuth2",
"sub": "User ID in Azure OAuth2",
"email": "User email in Azure OAuth2",
},
}
)

View File

@@ -135,6 +135,16 @@ func (self *SSAMLDriverClass) ValidateConfig(ctx context.Context, userCred mccli
return tconf, nil
}
func (self *SSAMLDriverClass) AttributeNames(template string) (map[string]string, error) {
switch template {
case api.IdpTemplateSAMLTest:
return SAMLTestTemplate.AttributeNames, nil
case api.IdpTemplateAzureADSAML:
return AzureADTemplate.AttributeNames, nil
}
return nil, nil
}
func init() {
driver.RegisterDriverClass(&SSAMLDriverClass{})
}

View File

@@ -27,6 +27,12 @@ var (
UserEmailAttribute: "urn:oid:0.9.2342.19200300.100.1.3",
UserMobileAttribute: "urn:oid:2.5.4.20",
},
AttributeNames: map[string]string{
"urn:oid:0.9.2342.19200300.100.1.1": "User name",
"urn:oid:2.16.840.1.113730.3.1.241": "User display name",
"urn:oid:0.9.2342.19200300.100.1.3": "User email",
"urn:oid:2.5.4.20": "User mobile",
},
}
AzureADTemplate = api.SSAMLIdpConfigOptions{
@@ -37,5 +43,11 @@ var (
UserEmailAttribute: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
UserMobileAttribute: "",
},
AttributeNames: map[string]string{
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "User name",
"http://schemas.microsoft.com/identity/claims/objectidentifier": "User ID",
"http://schemas.microsoft.com/identity/claims/displayname": "User display name",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": "User email",
},
}
)

View File

@@ -56,6 +56,10 @@ func (self *SSQLDriverClass) ValidateConfig(ctx context.Context, userCred mcclie
return conf, nil
}
func (self *SSQLDriverClass) AttributeNames(template string) (map[string]string, error) {
return nil, nil
}
func init() {
driver.RegisterDriverClass(&SSQLDriverClass{})
}

View File

@@ -252,7 +252,11 @@ func (manager *SAssignmentManager) fetchProjectRoleUserIdsQuery(projId, roleId s
q1 = q1.Equals("role_id", roleId)
}
assigns := AssignmentManager.Query().SubQuery()
assignsQ := AssignmentManager.Query()
if len(roleId) > 0 {
assignsQ = assignsQ.Equals("role_id", roleId)
}
assigns := assignsQ.SubQuery()
usergroups := UsergroupManager.Query().SubQuery()
q2 := usergroups.Query(usergroups.Field("user_id", "actor_id"))
@@ -262,9 +266,6 @@ func (manager *SAssignmentManager) fetchProjectRoleUserIdsQuery(projId, roleId s
q2 = q2.Filter(sqlchemy.Equals(assigns.Field("type"), api.AssignmentGroupProject))
q2 = q2.Filter(sqlchemy.Equals(assigns.Field("target_id"), projId))
q2 = q2.Filter(sqlchemy.IsFalse(assigns.Field("inherited")))
if len(roleId) > 0 {
q2 = q2.Equals("role_id", roleId)
}
union := sqlchemy.Union(q1, q2)
return union.Query().Distinct()

View File

@@ -347,6 +347,38 @@ func (manager *SIdentityProviderManager) getDriveInstanceCount(drvName string) (
return manager.Query().Equals("driver", drvName).CountWithError()
}
func (manager *SIdentityProviderManager) GetPropertyAttributeNames(ctx context.Context, userCred mcclient.TokenCredential, input api.IdentityProviderPropertyAttributeNamesInput) (jsonutils.JSONObject, error) {
var drvName string
template := input.Template
if len(template) > 0 {
if _, ok := api.IdpTemplateDriver[template]; !ok {
return nil, httperrors.NewInputParameterError("invalid template")
}
drvName = api.IdpTemplateDriver[template]
input.Driver = drvName
} else {
drvName = input.Driver
if len(drvName) == 0 {
return nil, httperrors.NewInputParameterError("missing driver")
}
}
drvCls := driver.GetDriverClass(drvName)
if drvCls == nil {
return nil, httperrors.NewInputParameterError("driver %s not supported", drvName)
}
attrs, err := drvCls.AttributeNames(input.Template)
if err != nil {
return nil, errors.Wrap(err, "AttributeNames")
}
if len(attrs) == 0 {
return jsonutils.NewDict(), nil
}
return jsonutils.Marshal(attrs), nil
}
func (manager *SIdentityProviderManager) ValidateCreateData(
ctx context.Context,
userCred mcclient.TokenCredential,