Files
kubesphere/pkg/apiserver/authentication/identityprovider/aliyunidaas/idaas.go
2025-04-30 15:53:51 +08:00

144 lines
3.7 KiB
Go

/*
* Copyright 2024 the KubeSphere Authors.
* Please refer to the LICENSE file in the root directory of the project.
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
*/
package aliyunidaas
import (
"encoding/json"
"errors"
"io"
"net/http"
"github.com/mitchellh/mapstructure"
"golang.org/x/oauth2"
"kubesphere.io/kubesphere/pkg/apiserver/authentication/identityprovider"
"kubesphere.io/kubesphere/pkg/server/options"
)
func init() {
identityprovider.RegisterOAuthProviderFactory(&idaasProviderFactory{})
}
type aliyunIDaaS struct {
// ClientID is the application's ID.
ClientID string `json:"clientID" yaml:"clientID"`
// ClientSecret is the application's secret.
ClientSecret string `json:"clientSecret" yaml:"clientSecret"`
// Endpoint contains the resource server's token endpoint
// URLs. These are constants specific to each server and are
// often available via site-specific packages, such as
// google.Endpoint or github.Endpoint.
Endpoint endpoint `json:"endpoint" yaml:"endpoint"`
// RedirectURL is the URL to redirect users going through
// the OAuth flow, after the resource owner's URLs.
RedirectURL string `json:"redirectURL" yaml:"redirectURL"`
// Scope specifies optional requested permissions.
Scopes []string `json:"scopes" yaml:"scopes"`
Config *oauth2.Config `json:"-" yaml:"-"`
}
// endpoint represents an OAuth 2.0 provider's authorization and token
// endpoint URLs.
type endpoint struct {
AuthURL string `json:"authURL" yaml:"authURL"`
TokenURL string `json:"tokenURL" yaml:"tokenURL"`
UserInfoURL string `json:"userInfoURL" yaml:"userInfoURL"`
}
type idaasIdentity struct {
Sub string `json:"sub"`
OuID string `json:"ou_id"`
Nickname string `json:"nickname"`
PhoneNumber string `json:"phone_number"`
OuName string `json:"ou_name"`
Email string `json:"email"`
Username string `json:"username"`
}
type userInfoResp struct {
Success bool `json:"success"`
Message string `json:"message"`
Code string `json:"code"`
IDaaSIdentity idaasIdentity `json:"data"`
}
type idaasProviderFactory struct {
}
func (f *idaasProviderFactory) Type() string {
return "AliyunIDaaSProvider"
}
func (f *idaasProviderFactory) Create(opts options.DynamicOptions) (identityprovider.OAuthProvider, error) {
var idaas aliyunIDaaS
if err := mapstructure.Decode(opts, &idaas); err != nil {
return nil, err
}
idaas.Config = &oauth2.Config{
ClientID: idaas.ClientID,
ClientSecret: idaas.ClientSecret,
Endpoint: oauth2.Endpoint{
AuthURL: idaas.Endpoint.AuthURL,
TokenURL: idaas.Endpoint.TokenURL,
AuthStyle: oauth2.AuthStyleAutoDetect,
},
RedirectURL: idaas.RedirectURL,
Scopes: idaas.Scopes,
}
return &idaas, nil
}
func (a idaasIdentity) GetUserID() string {
return a.Sub
}
func (a idaasIdentity) GetUsername() string {
return a.Username
}
func (a idaasIdentity) GetEmail() string {
return a.Email
}
func (a *aliyunIDaaS) IdentityExchangeCallback(req *http.Request) (identityprovider.Identity, error) {
// OAuth2 callback, see also https://tools.ietf.org/html/rfc6749#section-4.1.2
code := req.URL.Query().Get("code")
ctx := req.Context()
token, err := a.Config.Exchange(ctx, code)
if err != nil {
return nil, err
}
resp, err := oauth2.NewClient(ctx, oauth2.StaticTokenSource(token)).Get(a.Endpoint.UserInfoURL)
if err != nil {
return nil, err
}
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var UserInfoResp userInfoResp
err = json.Unmarshal(data, &UserInfoResp)
if err != nil {
return nil, err
}
if !UserInfoResp.Success {
return nil, errors.New(UserInfoResp.Message)
}
return UserInfoResp.IDaaSIdentity, nil
}