mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-23 02:35:49 +08:00
feat: support custom user mapping (#2029)
* feat: support custom user mapping * fix: parse id to string * Update data.json * Update data.json --------- Co-authored-by: hsluoyz <hsluoyz@qq.com>
This commit is contained in:
parent
ba97458edd
commit
3d4ca1adb1
@ -416,15 +416,8 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
} else if provider.Category == "OAuth" {
|
} else if provider.Category == "OAuth" {
|
||||||
// OAuth
|
// OAuth
|
||||||
|
idpInfo := object.FromProviderToIdpInfo(c.Ctx, provider)
|
||||||
clientId := provider.ClientId
|
idProvider := idp.GetIdProvider(idpInfo, authForm.RedirectUri)
|
||||||
clientSecret := provider.ClientSecret
|
|
||||||
if provider.Type == "WeChat" && strings.Contains(c.Ctx.Request.UserAgent(), "MicroMessenger") {
|
|
||||||
clientId = provider.ClientId2
|
|
||||||
clientSecret = provider.ClientSecret2
|
|
||||||
}
|
|
||||||
|
|
||||||
idProvider := idp.GetIdProvider(provider.Type, provider.SubType, clientId, clientSecret, provider.AppId, authForm.RedirectUri, provider.Domain, provider.CustomAuthUrl, provider.CustomTokenUrl, provider.CustomUserInfoUrl)
|
|
||||||
if idProvider == nil {
|
if idProvider == nil {
|
||||||
c.ResponseError(fmt.Sprintf(c.T("storage:The provider type: %s is not supported"), provider.Type))
|
c.ResponseError(fmt.Sprintf(c.T("storage:The provider type: %s is not supported"), provider.Type))
|
||||||
return
|
return
|
||||||
|
1
go.mod
1
go.mod
@ -39,6 +39,7 @@ require (
|
|||||||
github.com/lib/pq v1.10.2
|
github.com/lib/pq v1.10.2
|
||||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
||||||
github.com/markbates/goth v1.75.2
|
github.com/markbates/goth v1.75.2
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||||
github.com/nyaruka/phonenumbers v1.1.5
|
github.com/nyaruka/phonenumbers v1.1.5
|
||||||
github.com/pkoukk/tiktoken-go v0.1.1
|
github.com/pkoukk/tiktoken-go v0.1.1
|
||||||
|
@ -20,32 +20,37 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "net/url"
|
|
||||||
_ "time"
|
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CustomIdProvider struct {
|
type CustomIdProvider struct {
|
||||||
Client *http.Client
|
Client *http.Client
|
||||||
Config *oauth2.Config
|
Config *oauth2.Config
|
||||||
UserInfoUrl string
|
|
||||||
|
UserInfoURL string
|
||||||
|
TokenURL string
|
||||||
|
AuthURL string
|
||||||
|
UserMapping map[string]string
|
||||||
|
Scopes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCustomIdProvider(clientId string, clientSecret string, redirectUrl string, authUrl string, tokenUrl string, userInfoUrl string) *CustomIdProvider {
|
func NewCustomIdProvider(idpInfo *ProviderInfo, redirectUrl string) *CustomIdProvider {
|
||||||
idp := &CustomIdProvider{}
|
idp := &CustomIdProvider{}
|
||||||
idp.UserInfoUrl = userInfoUrl
|
|
||||||
|
|
||||||
config := &oauth2.Config{
|
idp.Config = &oauth2.Config{
|
||||||
ClientID: clientId,
|
ClientID: idpInfo.ClientId,
|
||||||
ClientSecret: clientSecret,
|
ClientSecret: idpInfo.ClientSecret,
|
||||||
RedirectURL: redirectUrl,
|
RedirectURL: redirectUrl,
|
||||||
Endpoint: oauth2.Endpoint{
|
Endpoint: oauth2.Endpoint{
|
||||||
AuthURL: authUrl,
|
AuthURL: idpInfo.AuthURL,
|
||||||
TokenURL: tokenUrl,
|
TokenURL: idpInfo.TokenURL,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
idp.Config = config
|
idp.UserInfoURL = idpInfo.UserInfoURL
|
||||||
|
idp.UserMapping = idpInfo.UserMapping
|
||||||
|
|
||||||
return idp
|
return idp
|
||||||
}
|
}
|
||||||
@ -60,22 +65,20 @@ func (idp *CustomIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CustomUserInfo struct {
|
type CustomUserInfo struct {
|
||||||
Id string `json:"sub"`
|
Id string `mapstructure:"id"`
|
||||||
Name string `json:"preferred_username,omitempty"`
|
Username string `mapstructure:"username"`
|
||||||
DisplayName string `json:"name"`
|
DisplayName string `mapstructure:"displayName"`
|
||||||
Email string `json:"email"`
|
Email string `mapstructure:"email"`
|
||||||
AvatarUrl string `json:"picture"`
|
AvatarUrl string `mapstructure:"avatarUrl"`
|
||||||
Status string `json:"status"`
|
|
||||||
Msg string `json:"msg"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||||
ctUserinfo := &CustomUserInfo{}
|
|
||||||
accessToken := token.AccessToken
|
accessToken := token.AccessToken
|
||||||
request, err := http.NewRequest("GET", idp.UserInfoUrl, nil)
|
request, err := http.NewRequest("GET", idp.UserInfoURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// add accessToken to request header
|
// add accessToken to request header
|
||||||
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
|
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
|
||||||
resp, err := idp.Client.Do(request)
|
resp, err := idp.Client.Do(request)
|
||||||
@ -89,21 +92,40 @@ func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(data, ctUserinfo)
|
var dataMap map[string]interface{}
|
||||||
|
err = json.Unmarshal(data, &dataMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctUserinfo.Status != "" {
|
// map user info
|
||||||
return nil, fmt.Errorf("err: %s", ctUserinfo.Msg)
|
for k, v := range idp.UserMapping {
|
||||||
|
_, ok := dataMap[v]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("cannot find %s in user from castom provider", v)
|
||||||
|
}
|
||||||
|
dataMap[k] = dataMap[v]
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to parse id to string
|
||||||
|
id, err := util.ParseIdToString(dataMap["id"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dataMap["id"] = id
|
||||||
|
|
||||||
|
customUserinfo := &CustomUserInfo{}
|
||||||
|
err = mapstructure.Decode(dataMap, customUserinfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
userInfo := &UserInfo{
|
userInfo := &UserInfo{
|
||||||
Id: ctUserinfo.Id,
|
Id: customUserinfo.Id,
|
||||||
Username: ctUserinfo.Name,
|
Username: customUserinfo.Username,
|
||||||
DisplayName: ctUserinfo.DisplayName,
|
DisplayName: customUserinfo.DisplayName,
|
||||||
Email: ctUserinfo.Email,
|
Email: customUserinfo.Email,
|
||||||
AvatarUrl: ctUserinfo.AvatarUrl,
|
AvatarUrl: customUserinfo.AvatarUrl,
|
||||||
}
|
}
|
||||||
return userInfo, nil
|
return userInfo, nil
|
||||||
}
|
}
|
||||||
|
123
idp/provider.go
123
idp/provider.go
@ -32,72 +32,89 @@ type UserInfo struct {
|
|||||||
AvatarUrl string
|
AvatarUrl string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ProviderInfo struct {
|
||||||
|
Type string
|
||||||
|
SubType string
|
||||||
|
ClientId string
|
||||||
|
ClientSecret string
|
||||||
|
AppId string
|
||||||
|
HostUrl string
|
||||||
|
RedirectUrl string
|
||||||
|
|
||||||
|
TokenURL string
|
||||||
|
AuthURL string
|
||||||
|
UserInfoURL string
|
||||||
|
UserMapping map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
type IdProvider interface {
|
type IdProvider interface {
|
||||||
SetHttpClient(client *http.Client)
|
SetHttpClient(client *http.Client)
|
||||||
GetToken(code string) (*oauth2.Token, error)
|
GetToken(code string) (*oauth2.Token, error)
|
||||||
GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetIdProvider(typ string, subType string, clientId string, clientSecret string, appId string, redirectUrl string, hostUrl string, authUrl string, tokenUrl string, userInfoUrl string) IdProvider {
|
func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) IdProvider {
|
||||||
if typ == "GitHub" {
|
switch idpInfo.Type {
|
||||||
return NewGithubIdProvider(clientId, clientSecret, redirectUrl)
|
case "GitHub":
|
||||||
} else if typ == "Google" {
|
return NewGithubIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
return NewGoogleIdProvider(clientId, clientSecret, redirectUrl)
|
case "Google":
|
||||||
} else if typ == "QQ" {
|
return NewGoogleIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
return NewQqIdProvider(clientId, clientSecret, redirectUrl)
|
case "QQ":
|
||||||
} else if typ == "WeChat" {
|
return NewQqIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
return NewWeChatIdProvider(clientId, clientSecret, redirectUrl)
|
case "WeChat":
|
||||||
} else if typ == "Facebook" {
|
return NewWeChatIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
return NewFacebookIdProvider(clientId, clientSecret, redirectUrl)
|
case "Facebook":
|
||||||
} else if typ == "DingTalk" {
|
return NewFacebookIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
return NewDingTalkIdProvider(clientId, clientSecret, redirectUrl)
|
case "DingTalk":
|
||||||
} else if typ == "Weibo" {
|
return NewDingTalkIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
return NewWeiBoIdProvider(clientId, clientSecret, redirectUrl)
|
case "Weibo":
|
||||||
} else if typ == "Gitee" {
|
return NewWeiBoIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
return NewGiteeIdProvider(clientId, clientSecret, redirectUrl)
|
case "Gitee":
|
||||||
} else if typ == "LinkedIn" {
|
return NewGiteeIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
return NewLinkedInIdProvider(clientId, clientSecret, redirectUrl)
|
case "LinkedIn":
|
||||||
} else if typ == "WeCom" {
|
return NewLinkedInIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
if subType == "Internal" {
|
case "WeCom":
|
||||||
return NewWeComInternalIdProvider(clientId, clientSecret, redirectUrl)
|
if idpInfo.SubType == "Internal" {
|
||||||
} else if subType == "Third-party" {
|
return NewWeComInternalIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
return NewWeComIdProvider(clientId, clientSecret, redirectUrl)
|
} else if idpInfo.SubType == "Third-party" {
|
||||||
|
return NewWeComIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else if typ == "Lark" {
|
case "Lark":
|
||||||
return NewLarkIdProvider(clientId, clientSecret, redirectUrl)
|
return NewLarkIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
} else if typ == "GitLab" {
|
case "GitLab":
|
||||||
return NewGitlabIdProvider(clientId, clientSecret, redirectUrl)
|
return NewGitlabIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
} else if typ == "Adfs" {
|
case "Adfs":
|
||||||
return NewAdfsIdProvider(clientId, clientSecret, redirectUrl, hostUrl)
|
return NewAdfsIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
|
||||||
} else if typ == "Baidu" {
|
case "Baidu":
|
||||||
return NewBaiduIdProvider(clientId, clientSecret, redirectUrl)
|
return NewBaiduIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
} else if typ == "Alipay" {
|
case "Alipay":
|
||||||
return NewAlipayIdProvider(clientId, clientSecret, redirectUrl)
|
return NewAlipayIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
} else if typ == "Custom" {
|
case "Custom":
|
||||||
return NewCustomIdProvider(clientId, clientSecret, redirectUrl, authUrl, tokenUrl, userInfoUrl)
|
return NewCustomIdProvider(idpInfo, redirectUrl)
|
||||||
} else if typ == "Infoflow" {
|
case "Infoflow":
|
||||||
if subType == "Internal" {
|
if idpInfo.SubType == "Internal" {
|
||||||
return NewInfoflowInternalIdProvider(clientId, clientSecret, appId, redirectUrl)
|
return NewInfoflowInternalIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, idpInfo.AppId, redirectUrl)
|
||||||
} else if subType == "Third-party" {
|
} else if idpInfo.SubType == "Third-party" {
|
||||||
return NewInfoflowIdProvider(clientId, clientSecret, appId, redirectUrl)
|
return NewInfoflowIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, idpInfo.AppId, redirectUrl)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else if typ == "Casdoor" {
|
case "Casdoor":
|
||||||
return NewCasdoorIdProvider(clientId, clientSecret, redirectUrl, hostUrl)
|
return NewCasdoorIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
|
||||||
} else if typ == "Okta" {
|
case "Okta":
|
||||||
return NewOktaIdProvider(clientId, clientSecret, redirectUrl, hostUrl)
|
return NewOktaIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
|
||||||
} else if typ == "Douyin" {
|
case "Douyin":
|
||||||
return NewDouyinIdProvider(clientId, clientSecret, redirectUrl)
|
return NewDouyinIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
} else if isGothSupport(typ) {
|
case "Bilibili":
|
||||||
return NewGothIdProvider(typ, clientId, clientSecret, redirectUrl, hostUrl)
|
return NewBilibiliIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
|
||||||
} else if typ == "Bilibili" {
|
default:
|
||||||
return NewBilibiliIdProvider(clientId, clientSecret, redirectUrl)
|
if isGothSupport(idpInfo.Type) {
|
||||||
|
return NewGothIdProvider(idpInfo.Type, idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var gothList = []string{
|
var gothList = []string{
|
||||||
|
@ -16,8 +16,11 @@ package object
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/beego/beego/context"
|
||||||
"github.com/casdoor/casdoor/i18n"
|
"github.com/casdoor/casdoor/i18n"
|
||||||
|
"github.com/casdoor/casdoor/idp"
|
||||||
"github.com/casdoor/casdoor/pp"
|
"github.com/casdoor/casdoor/pp"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
@ -28,21 +31,22 @@ type Provider struct {
|
|||||||
Name string `xorm:"varchar(100) notnull pk unique" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk unique" json:"name"`
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
|
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
Category string `xorm:"varchar(100)" json:"category"`
|
Category string `xorm:"varchar(100)" json:"category"`
|
||||||
Type string `xorm:"varchar(100)" json:"type"`
|
Type string `xorm:"varchar(100)" json:"type"`
|
||||||
SubType string `xorm:"varchar(100)" json:"subType"`
|
SubType string `xorm:"varchar(100)" json:"subType"`
|
||||||
Method string `xorm:"varchar(100)" json:"method"`
|
Method string `xorm:"varchar(100)" json:"method"`
|
||||||
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
||||||
ClientSecret string `xorm:"varchar(2000)" json:"clientSecret"`
|
ClientSecret string `xorm:"varchar(2000)" json:"clientSecret"`
|
||||||
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
|
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
|
||||||
ClientSecret2 string `xorm:"varchar(100)" json:"clientSecret2"`
|
ClientSecret2 string `xorm:"varchar(100)" json:"clientSecret2"`
|
||||||
Cert string `xorm:"varchar(100)" json:"cert"`
|
Cert string `xorm:"varchar(100)" json:"cert"`
|
||||||
CustomAuthUrl string `xorm:"varchar(200)" json:"customAuthUrl"`
|
CustomAuthUrl string `xorm:"varchar(200)" json:"customAuthUrl"`
|
||||||
CustomScope string `xorm:"varchar(200)" json:"customScope"`
|
CustomTokenUrl string `xorm:"varchar(200)" json:"customTokenUrl"`
|
||||||
CustomTokenUrl string `xorm:"varchar(200)" json:"customTokenUrl"`
|
CustomUserInfoUrl string `xorm:"varchar(200)" json:"customUserInfoUrl"`
|
||||||
CustomUserInfoUrl string `xorm:"varchar(200)" json:"customUserInfoUrl"`
|
CustomLogo string `xorm:"varchar(200)" json:"customLogo"`
|
||||||
CustomLogo string `xorm:"varchar(200)" json:"customLogo"`
|
Scopes string `xorm:"varchar(100)" json:"scopes"`
|
||||||
|
UserMapping map[string]string `xorm:"varchar(500)" json:"userMapping"`
|
||||||
|
|
||||||
Host string `xorm:"varchar(100)" json:"host"`
|
Host string `xorm:"varchar(100)" json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
@ -365,3 +369,27 @@ func providerChangeTrigger(oldName string, newName string) error {
|
|||||||
|
|
||||||
return session.Commit()
|
return session.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FromProviderToIdpInfo(ctx *context.Context, provider *Provider) *idp.ProviderInfo {
|
||||||
|
providerInfo := &idp.ProviderInfo{
|
||||||
|
Type: provider.Type,
|
||||||
|
SubType: provider.SubType,
|
||||||
|
ClientId: provider.ClientId,
|
||||||
|
ClientSecret: provider.ClientSecret,
|
||||||
|
AppId: provider.AppId,
|
||||||
|
HostUrl: provider.Host,
|
||||||
|
TokenURL: provider.CustomTokenUrl,
|
||||||
|
AuthURL: provider.CustomAuthUrl,
|
||||||
|
UserInfoURL: provider.CustomUserInfoUrl,
|
||||||
|
UserMapping: provider.UserMapping,
|
||||||
|
}
|
||||||
|
|
||||||
|
if provider.Type == "WeChat" {
|
||||||
|
if ctx != nil && strings.Contains(ctx.Request.UserAgent(), "MicroMessenger") {
|
||||||
|
providerInfo.ClientId = provider.ClientId2
|
||||||
|
providerInfo.ClientSecret = provider.ClientSecret2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return providerInfo
|
||||||
|
}
|
||||||
|
@ -6126,7 +6126,7 @@
|
|||||||
"customLogo": {
|
"customLogo": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"customScope": {
|
"scopes": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"customTokenUrl": {
|
"customTokenUrl": {
|
||||||
|
@ -4026,7 +4026,7 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
customLogo:
|
customLogo:
|
||||||
type: string
|
type: string
|
||||||
customScope:
|
scopes:
|
||||||
type: string
|
type: string
|
||||||
customTokenUrl:
|
customTokenUrl:
|
||||||
type: string
|
type: string
|
||||||
|
@ -289,3 +289,18 @@ func HasString(strs []string, str string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseIdToString(input interface{}) (string, error) {
|
||||||
|
switch v := input.(type) {
|
||||||
|
case string:
|
||||||
|
return v, nil
|
||||||
|
case int:
|
||||||
|
return strconv.Itoa(v), nil
|
||||||
|
case int64:
|
||||||
|
return strconv.FormatInt(v, 10), nil
|
||||||
|
case float64:
|
||||||
|
return strconv.FormatFloat(v, 'f', -1, 64), nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unsupported id type: %T", input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -246,3 +246,23 @@ func TestSnakeString(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseId(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
description string
|
||||||
|
input interface{}
|
||||||
|
expected interface{}
|
||||||
|
}{
|
||||||
|
{"Should be return 123456", "123456", "123456"},
|
||||||
|
{"Should be return 123456", 123456, "123456"},
|
||||||
|
{"Should be return 123456", int64(123456), "123456"},
|
||||||
|
{"Should be return 123456", float64(123456), "123456"},
|
||||||
|
}
|
||||||
|
for _, scenery := range scenarios {
|
||||||
|
t.Run(scenery.description, func(t *testing.T) {
|
||||||
|
actual, err := ParseIdToString(scenery.input)
|
||||||
|
assert.Nil(t, err, "The returned value not is expected")
|
||||||
|
assert.Equal(t, scenery.expected, actual, "The returned value not is expected")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -56,8 +56,10 @@ class ProviderEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
|
const provider = res.data;
|
||||||
|
provider.userMapping = provider.userMapping || {};
|
||||||
this.setState({
|
this.setState({
|
||||||
provider: res.data,
|
provider: provider,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", res.msg);
|
Setting.showMessage("error", res.msg);
|
||||||
@ -93,6 +95,40 @@ class ProviderEditPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateUserMappingField(key, value) {
|
||||||
|
const provider = this.state.provider;
|
||||||
|
provider.userMapping[key] = value;
|
||||||
|
this.setState({
|
||||||
|
provider: provider,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderUserMappingInput() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{Setting.getLabel(i18next.t("general:ID"), i18next.t("general:ID - Tooltip"))} :
|
||||||
|
<Input value={this.state.provider.userMapping.id} onChange={e => {
|
||||||
|
this.updateUserMappingField("id", e.target.value);
|
||||||
|
}} />
|
||||||
|
{Setting.getLabel(i18next.t("signup:Username"), i18next.t("signup:Username - Tooltip"))} :
|
||||||
|
<Input value={this.state.provider.userMapping.username} onChange={e => {
|
||||||
|
this.updateUserMappingField("username", e.target.value);
|
||||||
|
}} />
|
||||||
|
{Setting.getLabel(i18next.t("general:Display name"), i18next.t("general:Display name - Tooltip"))} :
|
||||||
|
<Input value={this.state.provider.userMapping.displayName} onChange={e => {
|
||||||
|
this.updateUserMappingField("displayName", e.target.value);
|
||||||
|
}} />
|
||||||
|
{Setting.getLabel(i18next.t("general:Email"), i18next.t("general:Email - Tooltip"))} :
|
||||||
|
<Input value={this.state.provider.userMapping.email} onChange={e => {
|
||||||
|
this.updateUserMappingField("email", e.target.value);
|
||||||
|
}} />
|
||||||
|
{Setting.getLabel(i18next.t("general:Avatar"), i18next.t("general:Avatar - Tooltip"))} :
|
||||||
|
<Input value={this.state.provider.userMapping.avatarUrl} onChange={e => {
|
||||||
|
this.updateUserMappingField("avatarUrl", e.target.value);
|
||||||
|
}} />
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
getClientIdLabel(provider) {
|
getClientIdLabel(provider) {
|
||||||
switch (provider.category) {
|
switch (provider.category) {
|
||||||
case "Email":
|
case "Email":
|
||||||
@ -350,7 +386,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
if (value === "Custom") {
|
if (value === "Custom") {
|
||||||
this.updateProviderField("customAuthUrl", "https://door.casdoor.com/login/oauth/authorize");
|
this.updateProviderField("customAuthUrl", "https://door.casdoor.com/login/oauth/authorize");
|
||||||
this.updateProviderField("customScope", "openid profile email");
|
this.updateProviderField("scopes", "openid profile email");
|
||||||
this.updateProviderField("customTokenUrl", "https://door.casdoor.com/api/login/oauth/access_token");
|
this.updateProviderField("customTokenUrl", "https://door.casdoor.com/api/login/oauth/access_token");
|
||||||
this.updateProviderField("customUserInfoUrl", "https://door.casdoor.com/api/userinfo");
|
this.updateProviderField("customUserInfoUrl", "https://door.casdoor.com/api/userinfo");
|
||||||
}
|
}
|
||||||
@ -416,16 +452,6 @@ class ProviderEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
|
||||||
{Setting.getLabel(i18next.t("provider:Scope"), i18next.t("provider:Scope - Tooltip"))}
|
|
||||||
</Col>
|
|
||||||
<Col span={22} >
|
|
||||||
<Input value={this.state.provider.customScope} onChange={e => {
|
|
||||||
this.updateProviderField("customScope", e.target.value);
|
|
||||||
}} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("provider:Token URL"), i18next.t("provider:Token URL - Tooltip"))}
|
{Setting.getLabel(i18next.t("provider:Token URL"), i18next.t("provider:Token URL - Tooltip"))}
|
||||||
@ -436,6 +462,16 @@ class ProviderEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("provider:Scope"), i18next.t("provider:Scope - Tooltip"))}
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.provider.scopes} onChange={e => {
|
||||||
|
this.updateProviderField("scopes", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("provider:UserInfo URL"), i18next.t("provider:UserInfo URL - Tooltip"))}
|
{Setting.getLabel(i18next.t("provider:UserInfo URL"), i18next.t("provider:UserInfo URL - Tooltip"))}
|
||||||
@ -446,6 +482,14 @@ class ProviderEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("provider:User mapping"), i18next.t("provider:User mapping - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
{this.renderUserMappingInput()}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("general:Favicon"), i18next.t("general:Favicon - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Favicon"), i18next.t("general:Favicon - Tooltip"))} :
|
||||||
|
@ -448,7 +448,7 @@ export function getAuthUrl(application, provider, method) {
|
|||||||
} else if (provider.type === "Douyin" || provider.type === "TikTok") {
|
} else if (provider.type === "Douyin" || provider.type === "TikTok") {
|
||||||
return `${endpoint}?client_key=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}`;
|
return `${endpoint}?client_key=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}`;
|
||||||
} else if (provider.type === "Custom") {
|
} else if (provider.type === "Custom") {
|
||||||
return `${provider.customAuthUrl}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${provider.customScope}&response_type=code&state=${state}`;
|
return `${provider.customAuthUrl}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${provider.scopes}&response_type=code&state=${state}`;
|
||||||
} else if (provider.type === "Bilibili") {
|
} else if (provider.type === "Bilibili") {
|
||||||
return `${endpoint}#/?client_id=${provider.clientId}&return_url=${redirectUri}&state=${state}&response_type=code`;
|
return `${endpoint}#/?client_id=${provider.clientId}&return_url=${redirectUri}&state=${state}&response_type=code`;
|
||||||
} else if (provider.type === "Deezer") {
|
} else if (provider.type === "Deezer") {
|
||||||
|
@ -570,8 +570,8 @@
|
|||||||
"pricing": {
|
"pricing": {
|
||||||
"Copy pricing page URL": "Preisseite URL kopieren",
|
"Copy pricing page URL": "Preisseite URL kopieren",
|
||||||
"Edit Pricing": "Edit Pricing",
|
"Edit Pricing": "Edit Pricing",
|
||||||
"Free": "Kostenlos",
|
|
||||||
"Failed to get plans": "Es konnten keine Pläne abgerufen werden",
|
"Failed to get plans": "Es konnten keine Pläne abgerufen werden",
|
||||||
|
"Free": "Kostenlos",
|
||||||
"Getting started": "Loslegen",
|
"Getting started": "Loslegen",
|
||||||
"New Pricing": "New Pricing",
|
"New Pricing": "New Pricing",
|
||||||
"Trial duration": "Testphase Dauer",
|
"Trial duration": "Testphase Dauer",
|
||||||
@ -739,6 +739,8 @@
|
|||||||
"Token URL - Tooltip": "Token-URL",
|
"Token URL - Tooltip": "Token-URL",
|
||||||
"Type": "Typ",
|
"Type": "Typ",
|
||||||
"Type - Tooltip": "Wählen Sie einen Typ aus",
|
"Type - Tooltip": "Wählen Sie einen Typ aus",
|
||||||
|
"User mapping": "User mapping",
|
||||||
|
"User mapping - Tooltip": "User mapping - Tooltip",
|
||||||
"UserInfo URL": "UserInfo-URL",
|
"UserInfo URL": "UserInfo-URL",
|
||||||
"UserInfo URL - Tooltip": "UserInfo-URL",
|
"UserInfo URL - Tooltip": "UserInfo-URL",
|
||||||
"admin (Shared)": "admin (Shared)"
|
"admin (Shared)": "admin (Shared)"
|
||||||
|
@ -739,6 +739,8 @@
|
|||||||
"Token URL - Tooltip": "Token URL",
|
"Token URL - Tooltip": "Token URL",
|
||||||
"Type": "Type",
|
"Type": "Type",
|
||||||
"Type - Tooltip": "Select a type",
|
"Type - Tooltip": "Select a type",
|
||||||
|
"User mapping": "User mapping",
|
||||||
|
"User mapping - Tooltip": "User mapping - Tooltip",
|
||||||
"UserInfo URL": "UserInfo URL",
|
"UserInfo URL": "UserInfo URL",
|
||||||
"UserInfo URL - Tooltip": "UserInfo URL",
|
"UserInfo URL - Tooltip": "UserInfo URL",
|
||||||
"admin (Shared)": "admin (Shared)"
|
"admin (Shared)": "admin (Shared)"
|
||||||
|
@ -739,6 +739,8 @@
|
|||||||
"Token URL - Tooltip": "URL de token",
|
"Token URL - Tooltip": "URL de token",
|
||||||
"Type": "Tipo",
|
"Type": "Tipo",
|
||||||
"Type - Tooltip": "Seleccionar un tipo",
|
"Type - Tooltip": "Seleccionar un tipo",
|
||||||
|
"User mapping": "User mapping",
|
||||||
|
"User mapping - Tooltip": "User mapping - Tooltip",
|
||||||
"UserInfo URL": "URL de información del usuario",
|
"UserInfo URL": "URL de información del usuario",
|
||||||
"UserInfo URL - Tooltip": "URL de información de usuario",
|
"UserInfo URL - Tooltip": "URL de información de usuario",
|
||||||
"admin (Shared)": "administrador (compartido)"
|
"admin (Shared)": "administrador (compartido)"
|
||||||
|
@ -739,6 +739,8 @@
|
|||||||
"Token URL - Tooltip": "URL de jeton",
|
"Token URL - Tooltip": "URL de jeton",
|
||||||
"Type": "Type",
|
"Type": "Type",
|
||||||
"Type - Tooltip": "Sélectionnez un type",
|
"Type - Tooltip": "Sélectionnez un type",
|
||||||
|
"User mapping": "User mapping",
|
||||||
|
"User mapping - Tooltip": "User mapping - Tooltip",
|
||||||
"UserInfo URL": "URL d'informations utilisateur",
|
"UserInfo URL": "URL d'informations utilisateur",
|
||||||
"UserInfo URL - Tooltip": "URL d'informations sur l'utilisateur",
|
"UserInfo URL - Tooltip": "URL d'informations sur l'utilisateur",
|
||||||
"admin (Shared)": "admin (Partagé)"
|
"admin (Shared)": "admin (Partagé)"
|
||||||
|
@ -739,6 +739,8 @@
|
|||||||
"Token URL - Tooltip": "Token URL: URL Token",
|
"Token URL - Tooltip": "Token URL: URL Token",
|
||||||
"Type": "Jenis",
|
"Type": "Jenis",
|
||||||
"Type - Tooltip": "Pilih tipe",
|
"Type - Tooltip": "Pilih tipe",
|
||||||
|
"User mapping": "User mapping",
|
||||||
|
"User mapping - Tooltip": "User mapping - Tooltip",
|
||||||
"UserInfo URL": "URL UserInfo",
|
"UserInfo URL": "URL UserInfo",
|
||||||
"UserInfo URL - Tooltip": "URL Informasi Pengguna",
|
"UserInfo URL - Tooltip": "URL Informasi Pengguna",
|
||||||
"admin (Shared)": "Admin (Berbagi)"
|
"admin (Shared)": "Admin (Berbagi)"
|
||||||
|
@ -739,6 +739,8 @@
|
|||||||
"Token URL - Tooltip": "トークンURL",
|
"Token URL - Tooltip": "トークンURL",
|
||||||
"Type": "タイプ",
|
"Type": "タイプ",
|
||||||
"Type - Tooltip": "タイプを選択してください",
|
"Type - Tooltip": "タイプを選択してください",
|
||||||
|
"User mapping": "User mapping",
|
||||||
|
"User mapping - Tooltip": "User mapping - Tooltip",
|
||||||
"UserInfo URL": "UserInfo URLを日本語に翻訳すると、「ユーザー情報のURL」となります",
|
"UserInfo URL": "UserInfo URLを日本語に翻訳すると、「ユーザー情報のURL」となります",
|
||||||
"UserInfo URL - Tooltip": "ユーザー情報URL",
|
"UserInfo URL - Tooltip": "ユーザー情報URL",
|
||||||
"admin (Shared)": "管理者(共有)"
|
"admin (Shared)": "管理者(共有)"
|
||||||
|
@ -739,6 +739,8 @@
|
|||||||
"Token URL - Tooltip": "토큰 URL",
|
"Token URL - Tooltip": "토큰 URL",
|
||||||
"Type": "타입",
|
"Type": "타입",
|
||||||
"Type - Tooltip": "유형을 선택하세요",
|
"Type - Tooltip": "유형을 선택하세요",
|
||||||
|
"User mapping": "User mapping",
|
||||||
|
"User mapping - Tooltip": "User mapping - Tooltip",
|
||||||
"UserInfo URL": "사용자 정보 URL",
|
"UserInfo URL": "사용자 정보 URL",
|
||||||
"UserInfo URL - Tooltip": "UserInfo URL: 사용자 정보 URL",
|
"UserInfo URL - Tooltip": "UserInfo URL: 사용자 정보 URL",
|
||||||
"admin (Shared)": "관리자 (공유)"
|
"admin (Shared)": "관리자 (공유)"
|
||||||
|
@ -739,6 +739,8 @@
|
|||||||
"Token URL - Tooltip": "URL do Token",
|
"Token URL - Tooltip": "URL do Token",
|
||||||
"Type": "Tipo",
|
"Type": "Tipo",
|
||||||
"Type - Tooltip": "Selecione um tipo",
|
"Type - Tooltip": "Selecione um tipo",
|
||||||
|
"User mapping": "User mapping",
|
||||||
|
"User mapping - Tooltip": "User mapping - Tooltip",
|
||||||
"UserInfo URL": "URL do UserInfo",
|
"UserInfo URL": "URL do UserInfo",
|
||||||
"UserInfo URL - Tooltip": "URL do UserInfo",
|
"UserInfo URL - Tooltip": "URL do UserInfo",
|
||||||
"admin (Shared)": "admin (Compartilhado)"
|
"admin (Shared)": "admin (Compartilhado)"
|
||||||
|
@ -739,6 +739,8 @@
|
|||||||
"Token URL - Tooltip": "Токен URL",
|
"Token URL - Tooltip": "Токен URL",
|
||||||
"Type": "Тип",
|
"Type": "Тип",
|
||||||
"Type - Tooltip": "Выберите тип",
|
"Type - Tooltip": "Выберите тип",
|
||||||
|
"User mapping": "User mapping",
|
||||||
|
"User mapping - Tooltip": "User mapping - Tooltip",
|
||||||
"UserInfo URL": "URL информации о пользователе",
|
"UserInfo URL": "URL информации о пользователе",
|
||||||
"UserInfo URL - Tooltip": "URL пользовательской информации (URL информации о пользователе)",
|
"UserInfo URL - Tooltip": "URL пользовательской информации (URL информации о пользователе)",
|
||||||
"admin (Shared)": "администратор (общий)"
|
"admin (Shared)": "администратор (общий)"
|
||||||
|
@ -739,6 +739,8 @@
|
|||||||
"Token URL - Tooltip": "Địa chỉ URL của Token",
|
"Token URL - Tooltip": "Địa chỉ URL của Token",
|
||||||
"Type": "Kiểu",
|
"Type": "Kiểu",
|
||||||
"Type - Tooltip": "Chọn loại",
|
"Type - Tooltip": "Chọn loại",
|
||||||
|
"User mapping": "User mapping",
|
||||||
|
"User mapping - Tooltip": "User mapping - Tooltip",
|
||||||
"UserInfo URL": "Đường dẫn UserInfo",
|
"UserInfo URL": "Đường dẫn UserInfo",
|
||||||
"UserInfo URL - Tooltip": "Địa chỉ URL của Thông tin người dùng",
|
"UserInfo URL - Tooltip": "Địa chỉ URL của Thông tin người dùng",
|
||||||
"admin (Shared)": "quản trị viên (Chung)"
|
"admin (Shared)": "quản trị viên (Chung)"
|
||||||
|
@ -570,8 +570,8 @@
|
|||||||
"pricing": {
|
"pricing": {
|
||||||
"Copy pricing page URL": "复制定价页面链接",
|
"Copy pricing page URL": "复制定价页面链接",
|
||||||
"Edit Pricing": "编辑定价",
|
"Edit Pricing": "编辑定价",
|
||||||
"Free": "免费",
|
|
||||||
"Failed to get plans": "未能获取计划",
|
"Failed to get plans": "未能获取计划",
|
||||||
|
"Free": "免费",
|
||||||
"Getting started": "开始使用",
|
"Getting started": "开始使用",
|
||||||
"New Pricing": "添加定价",
|
"New Pricing": "添加定价",
|
||||||
"Trial duration": "试用期时长",
|
"Trial duration": "试用期时长",
|
||||||
|
@ -66,7 +66,7 @@ class PricingPage extends React.Component {
|
|||||||
.then(results => {
|
.then(results => {
|
||||||
const hasError = results.some(result => result.status === "error");
|
const hasError = results.some(result => result.status === "error");
|
||||||
if (hasError) {
|
if (hasError) {
|
||||||
Setting.showMessage("error", `${i18next.t("Failed to get plans")}`);
|
Setting.showMessage("error", i18next.t("pricing:Failed to get plans"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -75,7 +75,7 @@ class PricingPage extends React.Component {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
Setting.showMessage("error", `Failed to get plans: ${error}`);
|
Setting.showMessage("error", i18next.t("pricing:Failed to get plans") + `: ${error}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@
|
|||||||
lru-cache "^5.1.1"
|
lru-cache "^5.1.1"
|
||||||
semver "^6.3.0"
|
semver "^6.3.0"
|
||||||
|
|
||||||
"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.5":
|
"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.22.5":
|
||||||
version "7.22.5"
|
version "7.22.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz#2192a1970ece4685fbff85b48da2c32fcb130b7c"
|
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz#2192a1970ece4685fbff85b48da2c32fcb130b7c"
|
||||||
integrity sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q==
|
integrity sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q==
|
||||||
@ -433,6 +433,16 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703"
|
||||||
integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==
|
integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==
|
||||||
|
|
||||||
|
"@babel/plugin-proposal-private-property-in-object@^7.21.11":
|
||||||
|
version "7.21.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz#69d597086b6760c4126525cfa154f34631ff272c"
|
||||||
|
integrity sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-annotate-as-pure" "^7.18.6"
|
||||||
|
"@babel/helper-create-class-features-plugin" "^7.21.0"
|
||||||
|
"@babel/helper-plugin-utils" "^7.20.2"
|
||||||
|
"@babel/plugin-syntax-private-property-in-object" "^7.14.5"
|
||||||
|
|
||||||
"@babel/plugin-proposal-unicode-property-regex@^7.4.4":
|
"@babel/plugin-proposal-unicode-property-regex@^7.4.4":
|
||||||
version "7.18.6"
|
version "7.18.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user