mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-10-29 17:58:36 +08:00
feat(bingocloud): add bingoiam for oauth2 (#17083)
Co-authored-by: 李锐 <lirui@bingosoft.net>
This commit is contained in:
@@ -61,6 +61,7 @@ func init() {
|
||||
"default",
|
||||
"k8s",
|
||||
"monitor",
|
||||
"bingocloud",
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -102,6 +102,7 @@ const (
|
||||
IdpTemplateDingtalk = "dingtalk_oauth2"
|
||||
IdpTemplateFeishu = "feishu_oauth2"
|
||||
IdpTemplateQywechat = "qywechat_oauth2"
|
||||
IdpTemplateBingoIAM = "bingoiam_oauth2"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -123,6 +124,7 @@ var (
|
||||
IdpTemplateDingtalk: IdentityDriverOAuth2,
|
||||
IdpTemplateWechat: IdentityDriverOAuth2,
|
||||
IdpTemplateQywechat: IdentityDriverOAuth2,
|
||||
IdpTemplateBingoIAM: IdentityDriverOAuth2,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// 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 bingoiam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"yunion.io/x/jsonutils"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/util/httputils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/keystone/driver/oauth2"
|
||||
"yunion.io/x/onecloud/pkg/keystone/models"
|
||||
)
|
||||
|
||||
type SBingoIAMOAuth2Driver struct {
|
||||
oauth2.SOAuth2BaseDriver
|
||||
|
||||
idp *models.SIdentityProvider
|
||||
accessToken string
|
||||
domains map[string]*models.SDomain
|
||||
projects map[string]*models.SProject
|
||||
}
|
||||
|
||||
func NewBingoIAMOAuth2Driver(appId string, secret string) oauth2.IOAuth2Driver {
|
||||
drv := &SBingoIAMOAuth2Driver{
|
||||
domains: map[string]*models.SDomain{},
|
||||
projects: map[string]*models.SProject{},
|
||||
SOAuth2BaseDriver: oauth2.SOAuth2BaseDriver{
|
||||
AppId: appId,
|
||||
Secret: secret,
|
||||
},
|
||||
}
|
||||
return drv
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) getIAMEndpoint(ctx context.Context) string {
|
||||
endpoint := ""
|
||||
if config, isOk := ctx.Value("config").(identity.TConfigs); isOk {
|
||||
endpoint = strings.Trim(config["oauth2"]["iam_endpoint"].String(), `"`)
|
||||
}
|
||||
return endpoint
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) getIAMApiEndpoint(ctx context.Context) string {
|
||||
endpoint := ""
|
||||
if config, isOk := ctx.Value("config").(identity.TConfigs); isOk {
|
||||
endpoint = strings.Trim(config["oauth2"]["iam_api_endpoint"].String(), `"`)
|
||||
}
|
||||
return endpoint
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) GetSsoRedirectUri(ctx context.Context, callbackUrl, state string) (string, error) {
|
||||
req := map[string]string{
|
||||
"client_id": drv.AppId,
|
||||
"redirect_uri": callbackUrl,
|
||||
"response_type": "code id_token",
|
||||
"scope": "openid",
|
||||
"state": state,
|
||||
}
|
||||
authUrl := drv.getIAMEndpoint(ctx)
|
||||
urlStr := fmt.Sprintf("%s/oauth2/authorize?%s", authUrl, jsonutils.Marshal(req).QueryString())
|
||||
return urlStr, nil
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) Authenticate(ctx context.Context, code string) (map[string][]string, error) {
|
||||
accessToken, err := drv.fetchAccessToken(ctx, code)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetch bingo iam accessToken")
|
||||
}
|
||||
userInfo, err := drv.fetchUserInfo(ctx, accessToken)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "fetch bingo iam userInfo")
|
||||
}
|
||||
attrs := make(map[string][]string)
|
||||
attrs["name"] = []string{fmt.Sprintf("%s", userInfo["preferred_username"])}
|
||||
attrs["display_name"] = []string{fmt.Sprintf("%s", userInfo["name"])}
|
||||
attrs["user_id"] = []string{fmt.Sprintf("%s", userInfo["sub"])}
|
||||
attrs["name_en"] = []string{fmt.Sprintf("%s", userInfo["preferred_username"])}
|
||||
attrs["email"] = []string{fmt.Sprintf("%s", userInfo["email"])}
|
||||
attrs["org_id"] = []string{fmt.Sprintf("%s", userInfo["org_id"])}
|
||||
attrs["mobile"] = []string{fmt.Sprintf("%s", userInfo["phone_number"])}
|
||||
attrs["tenant_id"] = []string{fmt.Sprintf("%s", userInfo["tenant_id"])}
|
||||
attrs["tenant_name"] = []string{fmt.Sprintf("%s", userInfo["tenant_name"])}
|
||||
return attrs, nil
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) Sync(ctx context.Context, idpId string) error {
|
||||
var err error
|
||||
drv.idp, err = models.IdentityProviderManager.FetchIdentityProviderById(idpId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//err = drv.syncTenants(ctx, drv.idp)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//err = drv.syncProjects(ctx, drv.idp)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
//err = drv.syncUsers(ctx, drv.idp)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) getAccessToken(ctx context.Context) (string, error) {
|
||||
authUrl := drv.getIAMEndpoint(ctx)
|
||||
authUrl = fmt.Sprintf("%s/oauth2/token?grant_type=client_credentials", authUrl)
|
||||
headers := http.Header{}
|
||||
headers.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%v:%v", drv.AppId, drv.Secret))))
|
||||
|
||||
httpclient := httputils.GetDefaultClient()
|
||||
_, resp, err := httputils.JSONRequest(httpclient, ctx, httputils.POST, authUrl, headers, nil, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var data map[string]interface{}
|
||||
err = resp.Unmarshal(&data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
drv.accessToken = data["access_token"].(string)
|
||||
|
||||
return drv.accessToken, nil
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) fetchAccessToken(ctx context.Context, code string) (string, error) {
|
||||
authUrl := drv.getIAMEndpoint(ctx)
|
||||
authUrl = fmt.Sprintf("%s/oauth2/token?grant_type=authorization_code&code=%s", authUrl, code)
|
||||
headers := http.Header{}
|
||||
headers.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%v:%v", drv.AppId, drv.Secret))))
|
||||
|
||||
httpclient := httputils.GetDefaultClient()
|
||||
_, resp, err := httputils.JSONRequest(httpclient, ctx, httputils.POST, authUrl, headers, nil, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var data map[string]interface{}
|
||||
err = resp.Unmarshal(&data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return data["access_token"].(string), nil
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) fetchUserInfo(ctx context.Context, accessToken string) (map[string]interface{}, error) {
|
||||
authUrl := drv.getIAMEndpoint(ctx)
|
||||
authUrl = fmt.Sprintf("%s/oauth2/userinfo?access_token=%s", authUrl, accessToken)
|
||||
headers := http.Header{}
|
||||
headers.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%v:%v", drv.AppId, drv.Secret))))
|
||||
|
||||
httpclient := httputils.GetDefaultClient()
|
||||
_, resp, err := httputils.JSONRequest(httpclient, ctx, httputils.GET, authUrl, headers, nil, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data map[string]interface{}
|
||||
err = resp.Unmarshal(&data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// 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 bingoiam // import "yunion.io/x/onecloud/pkg/keystone/driver/oauth2/bingo"
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2019 Yunion
|
||||
//
|
||||
// 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 bingoiam
|
||||
|
||||
import (
|
||||
api "yunion.io/x/onecloud/pkg/apis/identity"
|
||||
"yunion.io/x/onecloud/pkg/keystone/driver/oauth2"
|
||||
)
|
||||
|
||||
type SBingoIAMDriverFactory struct{}
|
||||
|
||||
func (drv SBingoIAMDriverFactory) NewDriver(appId string, secret string) oauth2.IOAuth2Driver {
|
||||
return NewBingoIAMOAuth2Driver(appId, secret)
|
||||
}
|
||||
|
||||
func (drv SBingoIAMDriverFactory) TemplateName() string {
|
||||
return api.IdpTemplateBingoIAM
|
||||
}
|
||||
|
||||
func (drv SBingoIAMDriverFactory) IdpAttributeOptions() api.SIdpAttributeOptions {
|
||||
return api.SIdpAttributeOptions{
|
||||
DomainNameAttribute: "tenant_name",
|
||||
DomainIdAttribute: "tenant_id",
|
||||
UserNameAttribute: "name",
|
||||
UserIdAttribute: "user_id",
|
||||
UserDisplaynameAttribtue: "display_name",
|
||||
ProjectAttribute: "",
|
||||
DefaultProjectId: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (drv SBingoIAMDriverFactory) ValidateConfig(conf api.SOAuth2IdpConfigOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
oauth2.Register(&SBingoIAMDriverFactory{})
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
package bingoiam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
json "yunion.io/x/jsonutils"
|
||||
"yunion.io/x/log"
|
||||
"yunion.io/x/pkg/errors"
|
||||
"yunion.io/x/pkg/tristate"
|
||||
"yunion.io/x/pkg/util/httputils"
|
||||
|
||||
"yunion.io/x/onecloud/pkg/keystone/models"
|
||||
)
|
||||
|
||||
type SBase struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
Email string `json:"email"`
|
||||
Mobile string `json:"mobile"`
|
||||
ParentId string `json:"parentId"`
|
||||
ExternalId string `json:"externalId"`
|
||||
Active bool `json:"active"`
|
||||
IsDeleted bool `json:"isDeleted"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
}
|
||||
|
||||
type STenant struct {
|
||||
SBase
|
||||
}
|
||||
|
||||
type SOrganization struct {
|
||||
SBase
|
||||
TenantId string `json:"tenantId"`
|
||||
}
|
||||
|
||||
type SUser struct {
|
||||
SBase
|
||||
OrgId string `json:"orgId"`
|
||||
UserName string `json:"userName"`
|
||||
TenantId string `json:"tenantId"`
|
||||
}
|
||||
|
||||
type SApp struct {
|
||||
SBase
|
||||
Name string `json:"title"`
|
||||
TenantId string `json:"tenantId"`
|
||||
AppType string `json:"appType"`
|
||||
ProjectId string `json:"projectId"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type SProject struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
TenantId string `json:"tenantId"`
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) syncTenants(ctx context.Context, idp *models.SIdentityProvider) error {
|
||||
var count, page = 0, 0
|
||||
for {
|
||||
page++
|
||||
total, tenants, err := drv.getTenants(ctx, page, 500)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, tenant := range tenants {
|
||||
domain, err := idp.SyncOrCreateDomain(ctx, tenant.Id, tenant.Name, fmt.Sprintf("Sync from %s", idp.Name), false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "idp.SyncOrCreateDomain")
|
||||
}
|
||||
drv.domains[tenant.Id] = domain
|
||||
}
|
||||
count += len(tenants)
|
||||
if count >= total {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) syncOrganizations(ctx context.Context, idp *models.SIdentityProvider) error {
|
||||
var loader func(filters string, orgs chan []*SOrganization)
|
||||
loader = func(filters string, orgs chan []*SOrganization) {
|
||||
var page, count = 0, 0
|
||||
var childOrgs []*SOrganization
|
||||
for {
|
||||
page++
|
||||
total, orgs, err := drv.getOrganizations(ctx, filters, page, 500)
|
||||
if err != nil {
|
||||
log.Errorf("get organizations %s fail %s", filters, err)
|
||||
return
|
||||
}
|
||||
childOrgs = append(childOrgs, orgs...)
|
||||
count += len(orgs)
|
||||
if count >= total {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
if len(childOrgs) > 0 {
|
||||
orgs <- childOrgs
|
||||
}
|
||||
for _, org := range childOrgs {
|
||||
loader("parentId eq "+org.Id, orgs)
|
||||
}
|
||||
}
|
||||
|
||||
var allOrgs = make(chan []*SOrganization)
|
||||
go func() {
|
||||
for orgs := range allOrgs {
|
||||
for _, org := range orgs {
|
||||
domain := drv.domains[org.TenantId]
|
||||
if domain == nil {
|
||||
continue
|
||||
}
|
||||
//TODO save to db
|
||||
}
|
||||
}
|
||||
}()
|
||||
loader("parentId is null", allOrgs)
|
||||
close(allOrgs)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) syncProjects(ctx context.Context, idp *models.SIdentityProvider) error {
|
||||
var count, page = 0, 0
|
||||
for {
|
||||
page++
|
||||
total, projects, err := drv.getProjects(ctx, page, 500)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, project := range projects {
|
||||
domain := drv.domains[project.TenantId]
|
||||
if domain == nil {
|
||||
continue
|
||||
}
|
||||
ret, err := models.ProjectManager.FetchProject("", project.Name, domain.Id, "")
|
||||
if err != nil {
|
||||
log.Errorf("fetch project %s fail %s", project.Name, err)
|
||||
if errors.Cause(err) == sql.ErrNoRows {
|
||||
ret, err = models.ProjectManager.NewProject(ctx, project.Name, fmt.Sprintf("Sync from %s", idp.Name), domain.Id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
drv.projects[project.Id] = ret
|
||||
}
|
||||
count += len(projects)
|
||||
if count >= total {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) syncUsers(ctx context.Context, idp *models.SIdentityProvider) error {
|
||||
var count, page = 0, 0
|
||||
for {
|
||||
page++
|
||||
total, users, err := drv.getUsers(ctx, page, 500)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, user := range users {
|
||||
domain := drv.domains[user.TenantId]
|
||||
if domain == nil {
|
||||
continue
|
||||
}
|
||||
_, err = idp.SyncOrCreateUser(ctx, user.Id, user.UserName, domain.Id, true, func(u *models.SUser) {
|
||||
u.Id = user.Id
|
||||
u.Name = user.UserName
|
||||
u.Displayname = user.Name
|
||||
u.Description = fmt.Sprintf("Sync from %s", idp.Name)
|
||||
u.Email = user.Email
|
||||
u.DomainId = domain.Id
|
||||
u.Mobile = user.Mobile
|
||||
u.Enabled = tristate.True
|
||||
u.SEnabledIdentityBaseResource.Enabled = tristate.True
|
||||
})
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "idp.SyncOrCreateDomain")
|
||||
}
|
||||
}
|
||||
count += len(users)
|
||||
if count >= total {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) getTenants(ctx context.Context, page, pageSize int) (int, []*STenant, error) {
|
||||
var accessToken = drv.accessToken
|
||||
|
||||
if accessToken == "" {
|
||||
accessToken, _ = drv.getAccessToken(ctx)
|
||||
}
|
||||
|
||||
urlStrStr := fmt.Sprintf("%v/tenant?total=true&page_size=%v&page=%v", drv.getIAMApiEndpoint(ctx), pageSize, page)
|
||||
headers := http.Header{}
|
||||
headers.Set("Authorization", "Bearer "+accessToken)
|
||||
|
||||
return doRequest[[]*STenant](ctx, urlStrStr, httputils.GET, headers, nil)
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) getOrganizations(ctx context.Context, filters string, page, pageSize int) (int, []*SOrganization, error) {
|
||||
var accessToken = drv.accessToken
|
||||
|
||||
if accessToken == "" {
|
||||
accessToken, _ = drv.getAccessToken(ctx)
|
||||
}
|
||||
|
||||
urlStr := fmt.Sprintf("%v/organization?total=true&page_size=%v&page=%v&filters=%v", drv.getIAMApiEndpoint(ctx), pageSize, page, url.QueryEscape(filters))
|
||||
headers := http.Header{}
|
||||
headers.Set("Authorization", "Bearer "+accessToken)
|
||||
|
||||
return doRequest[[]*SOrganization](ctx, urlStr, httputils.GET, headers, nil)
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) getUsers(ctx context.Context, page, pageSize int) (int, []*SUser, error) {
|
||||
var accessToken = drv.accessToken
|
||||
|
||||
if accessToken == "" {
|
||||
accessToken, _ = drv.getAccessToken(ctx)
|
||||
}
|
||||
|
||||
urlStr := fmt.Sprintf("%v/user?total=true&page_size=%v&page=%v", drv.getIAMApiEndpoint(ctx), pageSize, page)
|
||||
headers := http.Header{}
|
||||
headers.Set("Authorization", "Bearer "+accessToken)
|
||||
|
||||
return doRequest[[]*SUser](ctx, urlStr, httputils.GET, headers, nil)
|
||||
|
||||
}
|
||||
|
||||
func (drv *SBingoIAMOAuth2Driver) getProjects(ctx context.Context, page, pageSize int) (int, []*SProject, error) {
|
||||
var accessToken = drv.accessToken
|
||||
|
||||
if accessToken == "" {
|
||||
accessToken, _ = drv.getAccessToken(ctx)
|
||||
}
|
||||
|
||||
urlStr := fmt.Sprintf("%v/project?total=true&page_size=%v&page=%v", drv.getIAMApiEndpoint(ctx), pageSize, page)
|
||||
headers := http.Header{}
|
||||
headers.Set("Authorization", "Bearer "+accessToken)
|
||||
|
||||
return doRequest[[]*SProject](ctx, urlStr, httputils.GET, headers, nil)
|
||||
}
|
||||
|
||||
func doRequest[Result any](ctx context.Context, urlStr string, method httputils.THttpMethod, headers http.Header, body json.JSONObject) (total int, result Result, err error) {
|
||||
httpclient := httputils.GetDefaultClient()
|
||||
repsHeaders, resp, err := httputils.JSONRequest(httpclient, ctx, method, urlStr, headers, body, true)
|
||||
if err != nil {
|
||||
return 0, result, err
|
||||
}
|
||||
total, _ = strconv.Atoi(repsHeaders.Get("X-Total-Count"))
|
||||
err = resp.Unmarshal(&result)
|
||||
return
|
||||
}
|
||||
@@ -71,6 +71,7 @@ func (self *SOAuth2Driver) GetSsoRedirectUri(ctx context.Context, callbackUrl, s
|
||||
return "", errors.Wrapf(httperrors.ErrNotSupported, "template %s not supported", self.Template)
|
||||
}
|
||||
driver := factory.NewDriver(self.oauth2Config.AppId, self.oauth2Config.Secret)
|
||||
ctx = context.WithValue(ctx, "config", self.SBaseIdentityDriver.Config)
|
||||
return driver.GetSsoRedirectUri(ctx, callbackUrl, state)
|
||||
}
|
||||
|
||||
@@ -82,6 +83,7 @@ func (self *SOAuth2Driver) Authenticate(ctx context.Context, ident mcclient.SAut
|
||||
options := 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)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "driver %s Authenticate", self.Template)
|
||||
@@ -120,6 +122,14 @@ func (self *SOAuth2Driver) Authenticate(ctx context.Context, ident mcclient.SAut
|
||||
}
|
||||
|
||||
func (self *SOAuth2Driver) Sync(ctx context.Context) error {
|
||||
factory := findDriverFactory(self.Template)
|
||||
if factory == nil {
|
||||
return nil
|
||||
}
|
||||
if driver, isOk := factory.NewDriver(self.oauth2Config.AppId, self.oauth2Config.Secret).(IOAuth2Synchronizer); isOk {
|
||||
ctx = context.WithValue(ctx, "config", self.SBaseIdentityDriver.Config)
|
||||
return driver.Sync(ctx, self.IdpId)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ type IOAuth2Driver interface {
|
||||
GetSsoRedirectUri(ctx context.Context, callbackUrl, state string) (string, error)
|
||||
}
|
||||
|
||||
type IOAuth2Synchronizer interface {
|
||||
Sync(ctx context.Context, idpId string) error
|
||||
}
|
||||
|
||||
type SOAuth2BaseDriver struct {
|
||||
AppId string
|
||||
Secret string
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
_ "yunion.io/x/onecloud/pkg/keystone/driver/ldap"
|
||||
_ "yunion.io/x/onecloud/pkg/keystone/driver/oauth2"
|
||||
_ "yunion.io/x/onecloud/pkg/keystone/driver/oauth2/alipay"
|
||||
_ "yunion.io/x/onecloud/pkg/keystone/driver/oauth2/bingoiam"
|
||||
_ "yunion.io/x/onecloud/pkg/keystone/driver/oauth2/dingtalk"
|
||||
_ "yunion.io/x/onecloud/pkg/keystone/driver/oauth2/feishu"
|
||||
_ "yunion.io/x/onecloud/pkg/keystone/driver/oauth2/qywechat"
|
||||
|
||||
Reference in New Issue
Block a user