mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-08 09:01:00 +08:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
8698f4111a | |||
fdccb8b22b | |||
19e7d0b0bd | |||
f6a502f7ff | |||
b34e16b145 | |||
11b56c340f |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -79,7 +79,7 @@ jobs:
|
||||
e2e:
|
||||
name: e2e-test
|
||||
runs-on: ubuntu-latest
|
||||
needs: [ frontend, backend, linter ]
|
||||
needs: [ go-tests ]
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
|
@ -174,13 +174,36 @@ func (c *ApiController) GetApplicationLogin() {
|
||||
}
|
||||
|
||||
func setHttpClient(idProvider idp.IdProvider, providerType string) {
|
||||
if providerType == "GitHub" || providerType == "Google" || providerType == "Facebook" || providerType == "LinkedIn" || providerType == "Steam" || providerType == "Line" {
|
||||
if isProxyProviderType(providerType) {
|
||||
idProvider.SetHttpClient(proxy.ProxyHttpClient)
|
||||
} else {
|
||||
idProvider.SetHttpClient(proxy.DefaultHttpClient)
|
||||
}
|
||||
}
|
||||
|
||||
func isProxyProviderType(providerType string) bool {
|
||||
providerTypes := []string{
|
||||
"GitHub",
|
||||
"Google",
|
||||
"Facebook",
|
||||
"LinkedIn",
|
||||
"Steam",
|
||||
"Line",
|
||||
"Amazon",
|
||||
"Instagram",
|
||||
"TikTok",
|
||||
"Twitter",
|
||||
"Uber",
|
||||
"Yahoo",
|
||||
}
|
||||
for _, v := range providerTypes {
|
||||
if strings.EqualFold(v, providerType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Login ...
|
||||
// @Title Login
|
||||
// @Tag Login API
|
||||
|
@ -105,14 +105,34 @@ func handleSearch(w ldapserver.ResponseWriter, m *ldapserver.Message) {
|
||||
}
|
||||
for i := 0; i < len(users); i++ {
|
||||
user := users[i]
|
||||
dn := fmt.Sprintf("cn=%s,%s", user.DisplayName, string(r.BaseObject()))
|
||||
dn := fmt.Sprintf("cn=%s,%s", user.Name, string(r.BaseObject()))
|
||||
e := ldapserver.NewSearchResultEntry(dn)
|
||||
e.AddAttribute("cn", message.AttributeValue(user.Name))
|
||||
e.AddAttribute("uid", message.AttributeValue(user.Name))
|
||||
e.AddAttribute("email", message.AttributeValue(user.Email))
|
||||
e.AddAttribute("mobile", message.AttributeValue(user.Phone))
|
||||
e.AddAttribute("userPassword", message.AttributeValue(getUserPasswordWithType(user)))
|
||||
// e.AddAttribute("postalAddress", message.AttributeValue(user.Address[0]))
|
||||
w.Write(e)
|
||||
}
|
||||
w.Write(res)
|
||||
}
|
||||
|
||||
// get user password with hash type prefix
|
||||
// TODO not handle salt yet
|
||||
// @return {md5}5f4dcc3b5aa765d61d8327deb882cf99
|
||||
func getUserPasswordWithType(user *object.User) string {
|
||||
org := object.GetOrganizationByUser(user)
|
||||
if org.PasswordType == "" || org.PasswordType == "plain" {
|
||||
return user.Password
|
||||
}
|
||||
prefix := org.PasswordType
|
||||
if prefix == "salt" {
|
||||
prefix = "sha256"
|
||||
} else if prefix == "md5-salt" {
|
||||
prefix = "md5"
|
||||
} else if prefix == "pbkdf2-salt" {
|
||||
prefix = "pbkdf2"
|
||||
}
|
||||
return fmt.Sprintf("{%s}%s", prefix, user.Password)
|
||||
}
|
||||
|
183
idp/goth.go
183
idp/goth.go
@ -25,30 +25,59 @@ import (
|
||||
"github.com/markbates/goth"
|
||||
"github.com/markbates/goth/providers/amazon"
|
||||
"github.com/markbates/goth/providers/apple"
|
||||
"github.com/markbates/goth/providers/auth0"
|
||||
"github.com/markbates/goth/providers/azureadv2"
|
||||
"github.com/markbates/goth/providers/battlenet"
|
||||
"github.com/markbates/goth/providers/bitbucket"
|
||||
"github.com/markbates/goth/providers/box"
|
||||
"github.com/markbates/goth/providers/cloudfoundry"
|
||||
"github.com/markbates/goth/providers/dailymotion"
|
||||
"github.com/markbates/goth/providers/deezer"
|
||||
"github.com/markbates/goth/providers/digitalocean"
|
||||
"github.com/markbates/goth/providers/discord"
|
||||
"github.com/markbates/goth/providers/dropbox"
|
||||
"github.com/markbates/goth/providers/eveonline"
|
||||
"github.com/markbates/goth/providers/facebook"
|
||||
"github.com/markbates/goth/providers/fitbit"
|
||||
"github.com/markbates/goth/providers/gitea"
|
||||
"github.com/markbates/goth/providers/github"
|
||||
"github.com/markbates/goth/providers/gitlab"
|
||||
"github.com/markbates/goth/providers/google"
|
||||
"github.com/markbates/goth/providers/heroku"
|
||||
"github.com/markbates/goth/providers/influxcloud"
|
||||
"github.com/markbates/goth/providers/instagram"
|
||||
"github.com/markbates/goth/providers/intercom"
|
||||
"github.com/markbates/goth/providers/kakao"
|
||||
"github.com/markbates/goth/providers/lastfm"
|
||||
"github.com/markbates/goth/providers/line"
|
||||
"github.com/markbates/goth/providers/linkedin"
|
||||
"github.com/markbates/goth/providers/mailru"
|
||||
"github.com/markbates/goth/providers/meetup"
|
||||
"github.com/markbates/goth/providers/microsoftonline"
|
||||
"github.com/markbates/goth/providers/naver"
|
||||
"github.com/markbates/goth/providers/nextcloud"
|
||||
"github.com/markbates/goth/providers/onedrive"
|
||||
"github.com/markbates/goth/providers/oura"
|
||||
"github.com/markbates/goth/providers/patreon"
|
||||
"github.com/markbates/goth/providers/paypal"
|
||||
"github.com/markbates/goth/providers/salesforce"
|
||||
"github.com/markbates/goth/providers/shopify"
|
||||
"github.com/markbates/goth/providers/slack"
|
||||
"github.com/markbates/goth/providers/soundcloud"
|
||||
"github.com/markbates/goth/providers/spotify"
|
||||
"github.com/markbates/goth/providers/steam"
|
||||
"github.com/markbates/goth/providers/strava"
|
||||
"github.com/markbates/goth/providers/stripe"
|
||||
"github.com/markbates/goth/providers/tiktok"
|
||||
"github.com/markbates/goth/providers/tumblr"
|
||||
"github.com/markbates/goth/providers/twitter"
|
||||
"github.com/markbates/goth/providers/twitch"
|
||||
"github.com/markbates/goth/providers/twitterv2"
|
||||
"github.com/markbates/goth/providers/typetalk"
|
||||
"github.com/markbates/goth/providers/uber"
|
||||
"github.com/markbates/goth/providers/wepay"
|
||||
"github.com/markbates/goth/providers/xero"
|
||||
"github.com/markbates/goth/providers/yahoo"
|
||||
"github.com/markbates/goth/providers/yammer"
|
||||
"github.com/markbates/goth/providers/yandex"
|
||||
"github.com/markbates/goth/providers/zoom"
|
||||
"golang.org/x/oauth2"
|
||||
@ -77,11 +106,41 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
|
||||
Provider: azureadv2.New(clientId, clientSecret, redirectUrl, azureadv2.ProviderOptions{Tenant: "common"}),
|
||||
Session: &azureadv2.Session{},
|
||||
}
|
||||
case "Auth0":
|
||||
idp = GothIdProvider{
|
||||
Provider: auth0.New(clientId, clientSecret, redirectUrl, "casdoor.auth0.com"),
|
||||
Session: &auth0.Session{},
|
||||
}
|
||||
case "BattleNet":
|
||||
idp = GothIdProvider{
|
||||
Provider: battlenet.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &battlenet.Session{},
|
||||
}
|
||||
case "Bitbucket":
|
||||
idp = GothIdProvider{
|
||||
Provider: bitbucket.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &bitbucket.Session{},
|
||||
}
|
||||
case "Box":
|
||||
idp = GothIdProvider{
|
||||
Provider: box.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &box.Session{},
|
||||
}
|
||||
case "CloudFoundry":
|
||||
idp = GothIdProvider{
|
||||
Provider: cloudfoundry.New("", clientId, clientSecret, redirectUrl),
|
||||
Session: &cloudfoundry.Session{},
|
||||
}
|
||||
case "Dailymotion":
|
||||
idp = GothIdProvider{
|
||||
Provider: dailymotion.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &dailymotion.Session{},
|
||||
}
|
||||
case "Deezer":
|
||||
idp = GothIdProvider{
|
||||
Provider: deezer.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &deezer.Session{},
|
||||
}
|
||||
case "DigitalOcean":
|
||||
idp = GothIdProvider{
|
||||
Provider: digitalocean.New(clientId, clientSecret, redirectUrl),
|
||||
@ -97,6 +156,16 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
|
||||
Provider: dropbox.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &dropbox.Session{},
|
||||
}
|
||||
case "EveOnline":
|
||||
idp = GothIdProvider{
|
||||
Provider: eveonline.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &eveonline.Session{},
|
||||
}
|
||||
case "Fitbit":
|
||||
idp = GothIdProvider{
|
||||
Provider: fitbit.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &fitbit.Session{},
|
||||
}
|
||||
case "Facebook":
|
||||
idp = GothIdProvider{
|
||||
Provider: facebook.New(clientId, clientSecret, redirectUrl),
|
||||
@ -127,16 +196,31 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
|
||||
Provider: heroku.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &heroku.Session{},
|
||||
}
|
||||
case "InfluxCloud":
|
||||
idp = GothIdProvider{
|
||||
Provider: influxcloud.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &influxcloud.Session{},
|
||||
}
|
||||
case "Instagram":
|
||||
idp = GothIdProvider{
|
||||
Provider: instagram.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &instagram.Session{},
|
||||
}
|
||||
case "Intercom":
|
||||
idp = GothIdProvider{
|
||||
Provider: intercom.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &intercom.Session{},
|
||||
}
|
||||
case "Kakao":
|
||||
idp = GothIdProvider{
|
||||
Provider: kakao.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &kakao.Session{},
|
||||
}
|
||||
case "Lastfm":
|
||||
idp = GothIdProvider{
|
||||
Provider: lastfm.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &lastfm.Session{},
|
||||
}
|
||||
case "Linkedin":
|
||||
idp = GothIdProvider{
|
||||
Provider: linkedin.New(clientId, clientSecret, redirectUrl),
|
||||
@ -147,11 +231,46 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
|
||||
Provider: line.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &line.Session{},
|
||||
}
|
||||
case "Mailru":
|
||||
idp = GothIdProvider{
|
||||
Provider: mailru.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &mailru.Session{},
|
||||
}
|
||||
case "Meetup":
|
||||
idp = GothIdProvider{
|
||||
Provider: meetup.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &meetup.Session{},
|
||||
}
|
||||
case "MicrosoftOnline":
|
||||
idp = GothIdProvider{
|
||||
Provider: microsoftonline.New(clientId, clientSecret, redirectUrl),
|
||||
Session: µsoftonline.Session{},
|
||||
}
|
||||
case "Naver":
|
||||
idp = GothIdProvider{
|
||||
Provider: naver.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &naver.Session{},
|
||||
}
|
||||
case "Nextcloud":
|
||||
idp = GothIdProvider{
|
||||
Provider: nextcloud.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &nextcloud.Session{},
|
||||
}
|
||||
case "OneDrive":
|
||||
idp = GothIdProvider{
|
||||
Provider: onedrive.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &onedrive.Session{},
|
||||
}
|
||||
case "Oura":
|
||||
idp = GothIdProvider{
|
||||
Provider: oura.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &oura.Session{},
|
||||
}
|
||||
case "Patreon":
|
||||
idp = GothIdProvider{
|
||||
Provider: patreon.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &patreon.Session{},
|
||||
}
|
||||
case "Paypal":
|
||||
idp = GothIdProvider{
|
||||
Provider: paypal.New(clientId, clientSecret, redirectUrl),
|
||||
@ -172,26 +291,81 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
|
||||
Provider: slack.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &slack.Session{},
|
||||
}
|
||||
case "Soundcloud":
|
||||
idp = GothIdProvider{
|
||||
Provider: soundcloud.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &soundcloud.Session{},
|
||||
}
|
||||
case "Spotify":
|
||||
idp = GothIdProvider{
|
||||
Provider: spotify.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &spotify.Session{},
|
||||
}
|
||||
case "Steam":
|
||||
idp = GothIdProvider{
|
||||
Provider: steam.New(clientSecret, redirectUrl),
|
||||
Session: &steam.Session{},
|
||||
}
|
||||
case "Strava":
|
||||
idp = GothIdProvider{
|
||||
Provider: strava.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &strava.Session{},
|
||||
}
|
||||
case "Stripe":
|
||||
idp = GothIdProvider{
|
||||
Provider: stripe.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &stripe.Session{},
|
||||
}
|
||||
case "TikTok":
|
||||
idp = GothIdProvider{
|
||||
Provider: tiktok.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &tiktok.Session{},
|
||||
}
|
||||
case "Tumblr":
|
||||
idp = GothIdProvider{
|
||||
Provider: tumblr.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &tumblr.Session{},
|
||||
}
|
||||
case "Twitch":
|
||||
idp = GothIdProvider{
|
||||
Provider: twitch.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &twitch.Session{},
|
||||
}
|
||||
case "Twitter":
|
||||
idp = GothIdProvider{
|
||||
Provider: twitter.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &twitter.Session{},
|
||||
Provider: twitterv2.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &twitterv2.Session{},
|
||||
}
|
||||
case "Typetalk":
|
||||
idp = GothIdProvider{
|
||||
Provider: typetalk.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &typetalk.Session{},
|
||||
}
|
||||
case "Uber":
|
||||
idp = GothIdProvider{
|
||||
Provider: uber.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &uber.Session{},
|
||||
}
|
||||
case "Wepay":
|
||||
idp = GothIdProvider{
|
||||
Provider: wepay.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &wepay.Session{},
|
||||
}
|
||||
case "Xero":
|
||||
idp = GothIdProvider{
|
||||
Provider: xero.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &xero.Session{},
|
||||
}
|
||||
case "Yahoo":
|
||||
idp = GothIdProvider{
|
||||
Provider: yahoo.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &yahoo.Session{},
|
||||
}
|
||||
case "Yammer":
|
||||
idp = GothIdProvider{
|
||||
Provider: yammer.New(clientId, clientSecret, redirectUrl),
|
||||
Session: &yammer.Session{},
|
||||
}
|
||||
case "Yandex":
|
||||
idp = GothIdProvider{
|
||||
Provider: yandex.New(clientId, clientSecret, redirectUrl),
|
||||
@ -232,6 +406,9 @@ func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
// to call the function to obtain accessToken
|
||||
value = url.Values{}
|
||||
value.Add("code", code)
|
||||
if idp.Provider.Name() == "twitterv2" || idp.Provider.Name() == "fitbit" {
|
||||
value.Add("oauth_verifier", "casdoor-verifier")
|
||||
}
|
||||
}
|
||||
accessToken, err := idp.Session.Authorize(idp.Provider, value)
|
||||
if err != nil {
|
||||
|
@ -98,7 +98,61 @@ func GetIdProvider(typ string, subType string, clientId string, clientSecret str
|
||||
return nil
|
||||
}
|
||||
|
||||
var gothList = []string{"Apple", "AzureAD", "Slack", "Steam", "Line"}
|
||||
var gothList = []string{
|
||||
"Apple",
|
||||
"AzureAD",
|
||||
"Slack",
|
||||
"Steam",
|
||||
"Line",
|
||||
"Amazon",
|
||||
"Auth0",
|
||||
"BattleNet",
|
||||
"Bitbucket",
|
||||
"Box",
|
||||
"CloudFoundry",
|
||||
"Dailymotion",
|
||||
"Deezer",
|
||||
"DigitalOcean",
|
||||
"Discord",
|
||||
"Dropbox",
|
||||
"EveOnline",
|
||||
"Fitbit",
|
||||
"Gitea",
|
||||
"Heroku",
|
||||
"InfluxCloud",
|
||||
"Instagram",
|
||||
"Intercom",
|
||||
"Kakao",
|
||||
"Lastfm",
|
||||
"Mailru",
|
||||
"Meetup",
|
||||
"MicrosoftOnline",
|
||||
"Naver",
|
||||
"Nextcloud",
|
||||
"OneDrive",
|
||||
"Oura",
|
||||
"Patreon",
|
||||
"Paypal",
|
||||
"SalesForce",
|
||||
"Shopify",
|
||||
"Soundcloud",
|
||||
"Spotify",
|
||||
"Strava",
|
||||
"Stripe",
|
||||
"TikTok",
|
||||
"Tumblr",
|
||||
"Twitch",
|
||||
"Twitter",
|
||||
"Typetalk",
|
||||
"Uber",
|
||||
"VK",
|
||||
"Wepay",
|
||||
"Xero",
|
||||
"Yahoo",
|
||||
"Yammer",
|
||||
"Yandex",
|
||||
"Zoom",
|
||||
}
|
||||
|
||||
func isGothSupport(provider string) bool {
|
||||
for _, value := range gothList {
|
||||
|
@ -174,6 +174,7 @@ func getUserWithoutThirdIdp(user *User) *UserWithoutThirdIdp {
|
||||
LastSigninWrongTime: user.LastSigninWrongTime,
|
||||
SigninWrongTimes: user.SigninWrongTimes,
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
@ -200,12 +201,32 @@ func getClaimsWithoutThirdIdp(claims Claims) ClaimsWithoutThirdIdp {
|
||||
return res
|
||||
}
|
||||
|
||||
func refineUser(user *User) *User {
|
||||
user.Password = ""
|
||||
|
||||
if user.Address == nil {
|
||||
user.Address = []string{}
|
||||
}
|
||||
if user.Properties == nil {
|
||||
user.Properties = map[string]string{}
|
||||
}
|
||||
if user.Roles == nil {
|
||||
user.Roles = []*Role{}
|
||||
}
|
||||
if user.Permissions == nil {
|
||||
user.Permissions = []*Permission{}
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
func generateJwtToken(application *Application, user *User, nonce string, scope string, host string) (string, string, string, error) {
|
||||
nowTime := time.Now()
|
||||
expireTime := nowTime.Add(time.Duration(application.ExpireInHours) * time.Hour)
|
||||
refreshExpireTime := nowTime.Add(time.Duration(application.RefreshExpireInHours) * time.Hour)
|
||||
|
||||
user.Password = ""
|
||||
user = refineUser(user)
|
||||
|
||||
_, originBackend := getOriginFromHost(host)
|
||||
|
||||
name := util.GenerateId()
|
||||
|
100
object/user.go
100
object/user.go
@ -78,32 +78,80 @@ type User struct {
|
||||
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
|
||||
LastSigninIp string `xorm:"varchar(100)" json:"lastSigninIp"`
|
||||
|
||||
GitHub string `xorm:"github varchar(100)" json:"github"`
|
||||
Google string `xorm:"varchar(100)" json:"google"`
|
||||
QQ string `xorm:"qq varchar(100)" json:"qq"`
|
||||
WeChat string `xorm:"wechat varchar(100)" json:"wechat"`
|
||||
Facebook string `xorm:"facebook varchar(100)" json:"facebook"`
|
||||
DingTalk string `xorm:"dingtalk varchar(100)" json:"dingtalk"`
|
||||
Weibo string `xorm:"weibo varchar(100)" json:"weibo"`
|
||||
Gitee string `xorm:"gitee varchar(100)" json:"gitee"`
|
||||
LinkedIn string `xorm:"linkedin varchar(100)" json:"linkedin"`
|
||||
Wecom string `xorm:"wecom varchar(100)" json:"wecom"`
|
||||
Lark string `xorm:"lark varchar(100)" json:"lark"`
|
||||
Gitlab string `xorm:"gitlab varchar(100)" json:"gitlab"`
|
||||
Adfs string `xorm:"adfs varchar(100)" json:"adfs"`
|
||||
Baidu string `xorm:"baidu varchar(100)" json:"baidu"`
|
||||
Alipay string `xorm:"alipay varchar(100)" json:"alipay"`
|
||||
Casdoor string `xorm:"casdoor varchar(100)" json:"casdoor"`
|
||||
Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"`
|
||||
Apple string `xorm:"apple varchar(100)" json:"apple"`
|
||||
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
|
||||
Slack string `xorm:"slack varchar(100)" json:"slack"`
|
||||
Steam string `xorm:"steam varchar(100)" json:"steam"`
|
||||
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
|
||||
Okta string `xorm:"okta varchar(100)" json:"okta"`
|
||||
Douyin string `xorm:"douyin varchar(100)" json:"douyin"`
|
||||
Line string `xorm:"line varchar(100)" json:"line"`
|
||||
Custom string `xorm:"custom varchar(100)" json:"custom"`
|
||||
GitHub string `xorm:"github varchar(100)" json:"github"`
|
||||
Google string `xorm:"varchar(100)" json:"google"`
|
||||
QQ string `xorm:"qq varchar(100)" json:"qq"`
|
||||
WeChat string `xorm:"wechat varchar(100)" json:"wechat"`
|
||||
Facebook string `xorm:"facebook varchar(100)" json:"facebook"`
|
||||
DingTalk string `xorm:"dingtalk varchar(100)" json:"dingtalk"`
|
||||
Weibo string `xorm:"weibo varchar(100)" json:"weibo"`
|
||||
Gitee string `xorm:"gitee varchar(100)" json:"gitee"`
|
||||
LinkedIn string `xorm:"linkedin varchar(100)" json:"linkedin"`
|
||||
Wecom string `xorm:"wecom varchar(100)" json:"wecom"`
|
||||
Lark string `xorm:"lark varchar(100)" json:"lark"`
|
||||
Gitlab string `xorm:"gitlab varchar(100)" json:"gitlab"`
|
||||
Adfs string `xorm:"adfs varchar(100)" json:"adfs"`
|
||||
Baidu string `xorm:"baidu varchar(100)" json:"baidu"`
|
||||
Alipay string `xorm:"alipay varchar(100)" json:"alipay"`
|
||||
Casdoor string `xorm:"casdoor varchar(100)" json:"casdoor"`
|
||||
Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"`
|
||||
Apple string `xorm:"apple varchar(100)" json:"apple"`
|
||||
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
|
||||
Slack string `xorm:"slack varchar(100)" json:"slack"`
|
||||
Steam string `xorm:"steam varchar(100)" json:"steam"`
|
||||
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
|
||||
Okta string `xorm:"okta varchar(100)" json:"okta"`
|
||||
Douyin string `xorm:"douyin varchar(100)" json:"douyin"`
|
||||
Line string `xorm:"line varchar(100)" json:"line"`
|
||||
Amazon string `xorm:"amazon varchar(100)" json:"amazon"`
|
||||
Auth0 string `xorm:"auth0 varchar(100)" json:"auth0"`
|
||||
BattleNet string `xorm:"battlenet varchar(100)" json:"battlenet"`
|
||||
Bitbucket string `xorm:"bitbucket varchar(100)" json:"bitbucket"`
|
||||
Box string `xorm:"box varchar(100)" json:"box"`
|
||||
CloudFoundry string `xorm:"cloudfoundry varchar(100)" json:"cloudfoundry"`
|
||||
Dailymotion string `xorm:"dailymotion varchar(100)" json:"dailymotion"`
|
||||
Deezer string `xorm:"deezer varchar(100)" json:"deezer"`
|
||||
DigitalOcean string `xorm:"digitalocean varchar(100)" json:"digitalocean"`
|
||||
Discord string `xorm:"discord varchar(100)" json:"discord"`
|
||||
Dropbox string `xorm:"dropbox varchar(100)" json:"dropbox"`
|
||||
EveOnline string `xorm:"eveonline varchar(100)" json:"eveonline"`
|
||||
Fitbit string `xorm:"fitbit varchar(100)" json:"fitbit"`
|
||||
Gitea string `xorm:"gitea varchar(100)" json:"gitea"`
|
||||
Heroku string `xorm:"heroku varchar(100)" json:"heroku"`
|
||||
InfluxCloud string `xorm:"influxcloud varchar(100)" json:"influxcloud"`
|
||||
Instagram string `xorm:"instagram varchar(100)" json:"instagram"`
|
||||
Intercom string `xorm:"intercom varchar(100)" json:"intercom"`
|
||||
Kakao string `xorm:"kakao varchar(100)" json:"kakao"`
|
||||
Lastfm string `xorm:"lastfm varchar(100)" json:"lastfm"`
|
||||
Mailru string `xorm:"mailru varchar(100)" json:"mailru"`
|
||||
Meetup string `xorm:"meetup varchar(100)" json:"meetup"`
|
||||
MicrosoftOnline string `xorm:"microsoftonline varchar(100)" json:"microsoftonline"`
|
||||
Naver string `xorm:"naver varchar(100)" json:"naver"`
|
||||
Nextcloud string `xorm:"nextcloud varchar(100)" json:"nextcloud"`
|
||||
OneDrive string `xorm:"onedrive varchar(100)" json:"onedrive"`
|
||||
Oura string `xorm:"oura varchar(100)" json:"oura"`
|
||||
Patreon string `xorm:"patreon varchar(100)" json:"patreon"`
|
||||
Paypal string `xorm:"paypal varchar(100)" json:"paypal"`
|
||||
SalesForce string `xorm:"salesforce varchar(100)" json:"salesforce"`
|
||||
Shopify string `xorm:"shopify varchar(100)" json:"shopify"`
|
||||
Soundcloud string `xorm:"soundcloud varchar(100)" json:"soundcloud"`
|
||||
Spotify string `xorm:"spotify varchar(100)" json:"spotify"`
|
||||
Strava string `xorm:"strava varchar(100)" json:"strava"`
|
||||
Stripe string `xorm:"stripe varchar(100)" json:"stripe"`
|
||||
TikTok string `xorm:"tiktok varchar(100)" json:"tiktok"`
|
||||
Tumblr string `xorm:"tumblr varchar(100)" json:"tumblr"`
|
||||
Twitch string `xorm:"twitch varchar(100)" json:"twitch"`
|
||||
Twitter string `xorm:"twitter varchar(100)" json:"twitter"`
|
||||
Typetalk string `xorm:"typetalk varchar(100)" json:"typetalk"`
|
||||
Uber string `xorm:"uber varchar(100)" json:"uber"`
|
||||
VK string `xorm:"vk varchar(100)" json:"vk"`
|
||||
Wepay string `xorm:"wepay varchar(100)" json:"wepay"`
|
||||
Xero string `xorm:"xero varchar(100)" json:"xero"`
|
||||
Yahoo string `xorm:"yahoo varchar(100)" json:"yahoo"`
|
||||
Yammer string `xorm:"yammer varchar(100)" json:"yammer"`
|
||||
Yandex string `xorm:"yandex varchar(100)" json:"yandex"`
|
||||
Zoom string `xorm:"zoom varchar(100)" json:"zoom"`
|
||||
Custom string `xorm:"custom varchar(100)" json:"custom"`
|
||||
|
||||
WebauthnCredentials []webauthn.Credential `xorm:"webauthnCredentials blob" json:"webauthnCredentials"`
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
module.exports = {
|
||||
const { defineConfig } = require("cypress");
|
||||
|
||||
module.exports = defineConfig({
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
// implement node event listeners here
|
||||
},
|
||||
"retries": {
|
||||
"runMode": 2,
|
||||
"openMode": 0
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
|
16
web/cypress/e2e/adapter.cy.js
Normal file
16
web/cypress/e2e/adapter.cy.js
Normal file
@ -0,0 +1,16 @@
|
||||
describe('Test adapter', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
const selector = {
|
||||
add: ".ant-table-title > div > .ant-btn"
|
||||
};
|
||||
it("test adapter", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/adapters");
|
||||
cy.url().should("eq", "http://localhost:7001/adapters");
|
||||
cy.get(selector.add).click();
|
||||
cy.url().should("include","http://localhost:7001/adapters/built-in/")
|
||||
});
|
||||
})
|
13
web/cypress/e2e/application.cy.js
Normal file
13
web/cypress/e2e/application.cy.js
Normal file
@ -0,0 +1,13 @@
|
||||
describe('Test aplication', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test aplication", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/applications");
|
||||
cy.url().should("eq", "http://localhost:7001/applications");
|
||||
cy.visit("http://localhost:7001/applications/built-in/app-built-in");
|
||||
cy.url().should("eq", "http://localhost:7001/applications/built-in/app-built-in");
|
||||
});
|
||||
})
|
13
web/cypress/e2e/certs.cy.js
Normal file
13
web/cypress/e2e/certs.cy.js
Normal file
@ -0,0 +1,13 @@
|
||||
describe('Test certs', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test certs", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/certs");
|
||||
cy.url().should("eq", "http://localhost:7001/certs");
|
||||
cy.visit("http://localhost:7001/certs/cert-built-in");
|
||||
cy.url().should("eq", "http://localhost:7001/certs/cert-built-in");
|
||||
});
|
||||
})
|
13
web/cypress/e2e/models.cy.js
Normal file
13
web/cypress/e2e/models.cy.js
Normal file
@ -0,0 +1,13 @@
|
||||
describe('Test models', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test org", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/models");
|
||||
cy.url().should("eq", "http://localhost:7001/models");
|
||||
cy.visit("http://localhost:7001/models/built-in/model-built-in");
|
||||
cy.url().should("eq", "http://localhost:7001/models/built-in/model-built-in");
|
||||
});
|
||||
})
|
15
web/cypress/e2e/orgnazition.cy.js
Normal file
15
web/cypress/e2e/orgnazition.cy.js
Normal file
@ -0,0 +1,15 @@
|
||||
describe('Test Orgnazition', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test org", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/organizations");
|
||||
cy.url().should("eq", "http://localhost:7001/organizations");
|
||||
cy.visit("http://localhost:7001/organizations/built-in");
|
||||
cy.url().should("eq", "http://localhost:7001/organizations/built-in");
|
||||
cy.visit("http://localhost:7001/organizations/built-in/users");
|
||||
cy.url().should("eq", "http://localhost:7001/organizations/built-in/users");
|
||||
});
|
||||
})
|
16
web/cypress/e2e/payments.cy.js
Normal file
16
web/cypress/e2e/payments.cy.js
Normal file
@ -0,0 +1,16 @@
|
||||
describe('Test payments', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
const selector = {
|
||||
add: ".ant-table-title > div > .ant-btn"
|
||||
};
|
||||
it("test payments", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/payments");
|
||||
cy.url().should("eq", "http://localhost:7001/payments");
|
||||
cy.get(selector.add).click();
|
||||
cy.url().should("include","http://localhost:7001/payments/")
|
||||
});
|
||||
})
|
13
web/cypress/e2e/permissions.cy.js
Normal file
13
web/cypress/e2e/permissions.cy.js
Normal file
@ -0,0 +1,13 @@
|
||||
describe('Test permissions', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test permissions", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/permissions");
|
||||
cy.url().should("eq", "http://localhost:7001/permissions");
|
||||
cy.visit("http://localhost:7001/permissions/built-in/permission-built-in");
|
||||
cy.url().should("eq", "http://localhost:7001/permissions/built-in/permission-built-in");
|
||||
});
|
||||
})
|
16
web/cypress/e2e/products.cy.js
Normal file
16
web/cypress/e2e/products.cy.js
Normal file
@ -0,0 +1,16 @@
|
||||
describe('Test products', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
const selector = {
|
||||
add: ".ant-table-title > div > .ant-btn > span"
|
||||
};
|
||||
it("test products", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/products");
|
||||
cy.url().should("eq", "http://localhost:7001/products");
|
||||
cy.get(selector.add).click();
|
||||
cy.url().should("include","http://localhost:7001/products/")
|
||||
});
|
||||
})
|
13
web/cypress/e2e/providers.cy.js
Normal file
13
web/cypress/e2e/providers.cy.js
Normal file
@ -0,0 +1,13 @@
|
||||
describe('Test providers', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test providers", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/providers");
|
||||
cy.url().should("eq", "http://localhost:7001/providers");
|
||||
cy.visit("http://localhost:7001/providers/admin/provider_captcha_default");
|
||||
cy.url().should("eq", "http://localhost:7001/providers/admin/provider_captcha_default");
|
||||
});
|
||||
})
|
11
web/cypress/e2e/records.cy.js
Normal file
11
web/cypress/e2e/records.cy.js
Normal file
@ -0,0 +1,11 @@
|
||||
describe('Test records', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test records", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/records");
|
||||
cy.url().should("eq", "http://localhost:7001/records");
|
||||
});
|
||||
})
|
11
web/cypress/e2e/resource.cy.js
Normal file
11
web/cypress/e2e/resource.cy.js
Normal file
@ -0,0 +1,11 @@
|
||||
describe('Test resource', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test resource", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/resources");
|
||||
cy.url().should("eq", "http://localhost:7001/resources");
|
||||
});
|
||||
})
|
11
web/cypress/e2e/role.cy.js
Normal file
11
web/cypress/e2e/role.cy.js
Normal file
@ -0,0 +1,11 @@
|
||||
describe('Test roles', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test role", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/roles");
|
||||
cy.url().should("eq", "http://localhost:7001/roles");
|
||||
});
|
||||
})
|
11
web/cypress/e2e/sessions.cy.js
Normal file
11
web/cypress/e2e/sessions.cy.js
Normal file
@ -0,0 +1,11 @@
|
||||
describe('Test sessions', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test sessions", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/sessions");
|
||||
cy.url().should("eq", "http://localhost:7001/sessions");
|
||||
});
|
||||
})
|
16
web/cypress/e2e/syncers.cy.js
Normal file
16
web/cypress/e2e/syncers.cy.js
Normal file
@ -0,0 +1,16 @@
|
||||
describe('Test syncers', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
const selector = {
|
||||
add: ".ant-table-title > div > .ant-btn"
|
||||
};
|
||||
it("test syncers", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/syncers");
|
||||
cy.url().should("eq", "http://localhost:7001/syncers");
|
||||
cy.get(selector.add).click();
|
||||
cy.url().should("include","http://localhost:7001/syncers/")
|
||||
});
|
||||
})
|
11
web/cypress/e2e/sysinfo.cy.js
Normal file
11
web/cypress/e2e/sysinfo.cy.js
Normal file
@ -0,0 +1,11 @@
|
||||
describe('Test sysinfo', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test sysinfo", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/sysinfo");
|
||||
cy.url().should("eq", "http://localhost:7001/sysinfo");
|
||||
});
|
||||
})
|
16
web/cypress/e2e/tokens.cy.js
Normal file
16
web/cypress/e2e/tokens.cy.js
Normal file
@ -0,0 +1,16 @@
|
||||
describe('Test tokens', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
const selector = {
|
||||
add: ".ant-table-title > div > .ant-btn"
|
||||
};
|
||||
it("test records", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/tokens");
|
||||
cy.url().should("eq", "http://localhost:7001/tokens");
|
||||
cy.get(selector.add).click();
|
||||
cy.url().should("include","http://localhost:7001/tokens/")
|
||||
});
|
||||
})
|
13
web/cypress/e2e/user.cy.js
Normal file
13
web/cypress/e2e/user.cy.js
Normal file
@ -0,0 +1,13 @@
|
||||
describe('Test User', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
it("test user", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/users");
|
||||
cy.url().should("eq", "http://localhost:7001/users");
|
||||
cy.visit("http://localhost:7001/users/built-in/admin");
|
||||
cy.url().should("eq", "http://localhost:7001/users/built-in/admin");
|
||||
});
|
||||
})
|
16
web/cypress/e2e/webhooks.cy.js
Normal file
16
web/cypress/e2e/webhooks.cy.js
Normal file
@ -0,0 +1,16 @@
|
||||
describe('Test webhooks', () => {
|
||||
beforeEach(()=>{
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.login();
|
||||
})
|
||||
const selector = {
|
||||
add: ".ant-table-title > div > .ant-btn"
|
||||
};
|
||||
it("test webhooks", () => {
|
||||
cy.visit("http://localhost:7001");
|
||||
cy.visit("http://localhost:7001/webhooks");
|
||||
cy.url().should("eq", "http://localhost:7001/webhooks");
|
||||
cy.get(selector.add).click();
|
||||
cy.url().should("include","http://localhost:7001/webhooks/")
|
||||
});
|
||||
})
|
@ -22,4 +22,21 @@
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
|
||||
Cypress.Commands.add('login', ()=>{
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: "http://localhost:7001/api/login",
|
||||
body: {
|
||||
"application": "app-built-in",
|
||||
"organization": "built-in",
|
||||
"username": "admin",
|
||||
"password": "123",
|
||||
"autoSignin": true,
|
||||
"type": "login",
|
||||
"phonePrefix": "86",
|
||||
},
|
||||
}).then((Response) => {
|
||||
expect(Response).property("body").property("status").to.equal("ok");
|
||||
});
|
||||
})
|
||||
|
@ -69,6 +69,7 @@
|
||||
"@babel/eslint-parser": "^7.18.9",
|
||||
"@babel/preset-react": "^7.18.6",
|
||||
"cross-env": "^7.0.3",
|
||||
"cypress": "^12.5.1",
|
||||
"eslint": "8.22.0",
|
||||
"eslint-plugin-react": "^7.31.1",
|
||||
"husky": "^4.3.8",
|
||||
|
@ -109,7 +109,10 @@ class AdapterEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.adapter.organization} onChange={(value => {this.updateAdapterField("organization", value);})}>
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.adapter.organization} onChange={(value => {
|
||||
this.getModels(value);
|
||||
this.updateAdapterField("organization", value);
|
||||
})}>
|
||||
{
|
||||
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ class App extends Component {
|
||||
menuVisible: false,
|
||||
themeAlgorithm: ["default"],
|
||||
themeData: Setting.ThemeDefault,
|
||||
logo: this.getLogo(Setting.getAlgorithmNames(Setting.ThemeDefault)),
|
||||
};
|
||||
|
||||
Setting.initServerUrl();
|
||||
@ -330,12 +331,12 @@ class App extends Component {
|
||||
{
|
||||
this.renderAvatar()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
{Setting.isMobile() ? null : Setting.getShortName(this.state.account.displayName)} <DownOutlined />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</Dropdown>
|
||||
);
|
||||
@ -484,8 +485,8 @@ class App extends Component {
|
||||
|
||||
isStartPages() {
|
||||
return window.location.pathname.startsWith("/login") ||
|
||||
window.location.pathname.startsWith("/signup") ||
|
||||
window.location.pathname === "/";
|
||||
window.location.pathname.startsWith("/signup") ||
|
||||
window.location.pathname === "/";
|
||||
}
|
||||
|
||||
renderRouter() {
|
||||
@ -559,7 +560,7 @@ class App extends Component {
|
||||
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: this.state.themeAlgorithm.includes("dark") ? "black" : "white"}}>
|
||||
{Setting.isMobile() ? null : (
|
||||
<Link to={"/"}>
|
||||
<div className="logo" style={{background: `url(${this.getLogo(Setting.getAlgorithmNames(this.state.themeData))})`}} />
|
||||
<div className="logo" style={{background: `url(${this.state.logo})`}} />
|
||||
</Link>
|
||||
)}
|
||||
{Setting.isMobile() ?
|
||||
@ -611,7 +612,7 @@ class App extends Component {
|
||||
textAlign: "center",
|
||||
}
|
||||
}>
|
||||
Powered by <a target="_blank" href="https://casdoor.org" rel="noreferrer"><img style={{paddingBottom: "3px"}} height={"20px"} alt={"Casdoor"} src={this.getLogo(Setting.getAlgorithmNames(this.state.themeData))} /></a>
|
||||
Powered by <a target="_blank" href="https://casdoor.org" rel="noreferrer"><img style={{paddingBottom: "3px"}} height={"20px"} alt={"Casdoor"} src={this.state.logo} /></a>
|
||||
</Footer>
|
||||
</React.Fragment>
|
||||
);
|
||||
@ -623,11 +624,11 @@ class App extends Component {
|
||||
|
||||
isEntryPages() {
|
||||
return window.location.pathname.startsWith("/signup") ||
|
||||
window.location.pathname.startsWith("/login") ||
|
||||
window.location.pathname.startsWith("/forget") ||
|
||||
window.location.pathname.startsWith("/prompt") ||
|
||||
window.location.pathname.startsWith("/cas") ||
|
||||
window.location.pathname.startsWith("/auto-signup");
|
||||
window.location.pathname.startsWith("/login") ||
|
||||
window.location.pathname.startsWith("/forget") ||
|
||||
window.location.pathname.startsWith("/prompt") ||
|
||||
window.location.pathname.startsWith("/cas") ||
|
||||
window.location.pathname.startsWith("/auto-signup");
|
||||
}
|
||||
|
||||
renderPage() {
|
||||
@ -643,12 +644,7 @@ class App extends Component {
|
||||
onUpdateAccount={(account) => {
|
||||
this.onUpdateAccount(account);
|
||||
}}
|
||||
updataThemeData={(nextThemeData) => {
|
||||
this.setState({
|
||||
themeData: nextThemeData,
|
||||
});
|
||||
localStorage.setItem("themeAlgorithm", Setting.getAlgorithmNames(nextThemeData).toString());
|
||||
}}
|
||||
updataThemeData={this.setTheme}
|
||||
/> :
|
||||
<Switch>
|
||||
<Route exact path="/callback" component={AuthCallback} />
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Col, List, Popconfirm, Result, Row, Table, Tooltip} from "antd";
|
||||
import {Button, Col, List, Popconfirm, Row, Table, Tooltip} from "antd";
|
||||
import {EditOutlined} from "@ant-design/icons";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
@ -25,19 +25,12 @@ import BaseListPage from "./BaseListPage";
|
||||
class ApplicationListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
organizationName: props.account.owner,
|
||||
data: [],
|
||||
pagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
loading: false,
|
||||
searchText: "",
|
||||
searchedColumn: "",
|
||||
isAuthorized: true,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
organizationName: this.props.account.owner,
|
||||
});
|
||||
}
|
||||
|
||||
newApplication() {
|
||||
@ -259,17 +252,6 @@ class ApplicationListPage extends BaseListPage {
|
||||
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
|
||||
};
|
||||
|
||||
if (!this.state.isAuthorized) {
|
||||
return (
|
||||
<Result
|
||||
status="403"
|
||||
title="403 Unauthorized"
|
||||
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
|
||||
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table scroll={{x: "max-content"}} columns={columns} dataSource={applications} rowKey="name" size="middle" bordered pagination={paginationProps}
|
||||
|
@ -25,11 +25,25 @@ class ManagedAccountTable extends React.Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
managedAccounts: this.props.table !== null ? this.props.table.map((item, index) => {
|
||||
item.key = index;
|
||||
return item;
|
||||
}) : [],
|
||||
};
|
||||
}
|
||||
|
||||
count = this.props.table?.length ?? 0;
|
||||
|
||||
updateTable(table) {
|
||||
this.props.onUpdateTable(table);
|
||||
this.setState({
|
||||
managedAccounts: table,
|
||||
});
|
||||
|
||||
this.props.onUpdateTable([...table].map((item) => {
|
||||
const newItem = Setting.deepCopy(item);
|
||||
delete newItem.key;
|
||||
return newItem;
|
||||
}));
|
||||
}
|
||||
|
||||
updateField(table, index, key, value) {
|
||||
@ -38,10 +52,12 @@ class ManagedAccountTable extends React.Component {
|
||||
}
|
||||
|
||||
addRow(table) {
|
||||
const row = {application: "", username: "", password: ""};
|
||||
const row = {key: this.count, application: "", username: "", password: ""};
|
||||
if (table === undefined || table === null) {
|
||||
table = [];
|
||||
}
|
||||
|
||||
this.count += 1;
|
||||
table = Setting.addRow(table, row);
|
||||
this.updateTable(table);
|
||||
}
|
||||
@ -131,7 +147,7 @@ class ManagedAccountTable extends React.Component {
|
||||
];
|
||||
|
||||
return (
|
||||
<Table scroll={{x: "max-content"}} rowKey="name" columns={columns} dataSource={table} size="middle" bordered pagination={false}
|
||||
<Table scroll={{x: "max-content"}} rowKey="key" columns={columns} dataSource={table} size="middle" bordered pagination={false}
|
||||
title={() => (
|
||||
<div>
|
||||
{this.props.title}
|
||||
@ -148,7 +164,7 @@ class ManagedAccountTable extends React.Component {
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col span={24}>
|
||||
{
|
||||
this.renderTable(this.props.table)
|
||||
this.renderTable(this.state.managedAccounts)
|
||||
}
|
||||
</Col>
|
||||
</Row>
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Result, Switch, Table} from "antd";
|
||||
import {Button, Popconfirm, Switch, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
@ -246,17 +246,6 @@ class OrganizationListPage extends BaseListPage {
|
||||
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
|
||||
};
|
||||
|
||||
if (!this.state.isAuthorized) {
|
||||
return (
|
||||
<Result
|
||||
status="403"
|
||||
title="403 Unauthorized"
|
||||
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
|
||||
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table scroll={{x: "max-content"}} columns={columns} dataSource={organizations} rowKey="name" size="middle" bordered pagination={paginationProps}
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Result, Table} from "antd";
|
||||
import {Button, Popconfirm, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ProviderBackend from "./backend/ProviderBackend";
|
||||
@ -25,20 +25,14 @@ import BaseListPage from "./BaseListPage";
|
||||
class ProviderListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
owner: Setting.isAdminUser(props.account) ? "admin" : props.account.owner,
|
||||
data: [],
|
||||
pagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
loading: false,
|
||||
searchText: "",
|
||||
searchedColumn: "",
|
||||
isAuthorized: true,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
owner: Setting.isAdminUser(this.props.account) ? "admin" : this.props.account.owner,
|
||||
});
|
||||
}
|
||||
|
||||
newProvider() {
|
||||
const randomName = Setting.getRandomName();
|
||||
return {
|
||||
@ -228,17 +222,6 @@ class ProviderListPage extends BaseListPage {
|
||||
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
|
||||
};
|
||||
|
||||
if (!this.state.isAuthorized) {
|
||||
return (
|
||||
<Result
|
||||
status="403"
|
||||
title="403 Unauthorized"
|
||||
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
|
||||
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table scroll={{x: "max-content"}} columns={columns} dataSource={providers} rowKey="name" size="middle" bordered pagination={paginationProps}
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Popconfirm, Result, Table, Upload} from "antd";
|
||||
import {Button, Popconfirm, Table, Upload} from "antd";
|
||||
import {UploadOutlined} from "@ant-design/icons";
|
||||
import copy from "copy-to-clipboard";
|
||||
import * as Setting from "./Setting";
|
||||
@ -25,20 +25,13 @@ import BaseListPage from "./BaseListPage";
|
||||
class ResourceListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
data: [],
|
||||
pagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
loading: false,
|
||||
searchText: "",
|
||||
searchedColumn: "",
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
fileList: [],
|
||||
uploading: false,
|
||||
isAuthorized: true,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
deleteResource(i) {
|
||||
@ -273,17 +266,6 @@ class ResourceListPage extends BaseListPage {
|
||||
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
|
||||
};
|
||||
|
||||
if (!this.state.isAuthorized) {
|
||||
return (
|
||||
<Result
|
||||
status="403"
|
||||
title="403 Unauthorized"
|
||||
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
|
||||
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table scroll={{x: "max-content"}} columns={columns} dataSource={resources} rowKey="name" size="middle" bordered pagination={paginationProps}
|
||||
|
@ -779,6 +779,54 @@ export function getProviderTypeOptions(category) {
|
||||
{id: "Okta", name: "Okta"},
|
||||
{id: "Douyin", name: "Douyin"},
|
||||
{id: "Line", name: "Line"},
|
||||
{id: "Amazon", name: "Amazon"},
|
||||
{id: "Auth0", name: "Auth0"},
|
||||
{id: "BattleNet", name: "Battle.net"},
|
||||
{id: "Bitbucket", name: "Bitbucket"},
|
||||
{id: "Box", name: "Box"},
|
||||
{id: "CloudFoundry", name: "Cloud Foundry"},
|
||||
{id: "Dailymotion", name: "Dailymotion"},
|
||||
{id: "Deezer", name: "Deezer"},
|
||||
{id: "DigitalOcean", name: "DigitalOcean"},
|
||||
{id: "Discord", name: "Discord"},
|
||||
{id: "Dropbox", name: "Dropbox"},
|
||||
{id: "EveOnline", name: "Eve Online"},
|
||||
{id: "Fitbit", name: "Fitbit"},
|
||||
{id: "Gitea", name: "Gitea"},
|
||||
{id: "Heroku", name: "Heroku"},
|
||||
{id: "InfluxCloud", name: "InfluxCloud"},
|
||||
{id: "Instagram", name: "Instagram"},
|
||||
{id: "Intercom", name: "Intercom"},
|
||||
{id: "Kakao", name: "Kakao"},
|
||||
{id: "Lastfm", name: "Lastfm"},
|
||||
{id: "Mailru", name: "Mailru"},
|
||||
{id: "Meetup", name: "Meetup"},
|
||||
{id: "MicrosoftOnline", name: "MicrosoftOnline"},
|
||||
{id: "Naver", name: "Naver"},
|
||||
{id: "Nextcloud", name: "Nextcloud"},
|
||||
{id: "OneDrive", name: "OneDrive"},
|
||||
{id: "Oura", name: "Oura"},
|
||||
{id: "Patreon", name: "Patreon"},
|
||||
{id: "Paypal", name: "Paypal"},
|
||||
{id: "SalesForce", name: "SalesForce"},
|
||||
{id: "Shopify", name: "Shopify"},
|
||||
{id: "Soundcloud", name: "Soundcloud"},
|
||||
{id: "Spotify", name: "Spotify"},
|
||||
{id: "Strava", name: "Strava"},
|
||||
{id: "Stripe", name: "Stripe"},
|
||||
{id: "TikTok", name: "TikTok"},
|
||||
{id: "Tumblr", name: "Tumblr"},
|
||||
{id: "Twitch", name: "Twitch"},
|
||||
{id: "Twitter", name: "Twitter"},
|
||||
{id: "Typetalk", name: "Typetalk"},
|
||||
{id: "Uber", name: "Uber"},
|
||||
{id: "VK", name: "VK"},
|
||||
{id: "Wepay", name: "Wepay"},
|
||||
{id: "Xero", name: "Xero"},
|
||||
{id: "Yahoo", name: "Yahoo"},
|
||||
{id: "Yammer", name: "Yammer"},
|
||||
{id: "Yandex", name: "Yandex"},
|
||||
{id: "Zoom", name: "Zoom"},
|
||||
{id: "Custom", name: "Custom"},
|
||||
]
|
||||
);
|
||||
|
@ -182,7 +182,10 @@ class UserEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} disabled={disabled} value={this.state.user.owner} onChange={(value => {this.updateUserField("owner", value);})}>
|
||||
<Select virtual={false} style={{width: "100%"}} disabled={disabled} value={this.state.user.owner} onChange={(value => {
|
||||
this.getApplicationsByOrganization(value);
|
||||
this.updateUserField("owner", value);
|
||||
})}>
|
||||
{
|
||||
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
|
||||
}
|
||||
@ -571,7 +574,7 @@ class UserEditPage extends React.Component {
|
||||
<Col span={22} >
|
||||
<ManagedAccountTable
|
||||
title={i18next.t("user:Managed accounts")}
|
||||
table={this.state.user.managedAccounts ?? []}
|
||||
table={this.state.user.managedAccounts}
|
||||
onUpdateTable={(table) => {this.updateUserField("managedAccounts", table);}}
|
||||
applications={this.state.applications}
|
||||
/>
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Result, Switch, Table, Upload} from "antd";
|
||||
import {Button, Popconfirm, Switch, Table, Upload} from "antd";
|
||||
import {UploadOutlined} from "@ant-design/icons";
|
||||
import moment from "moment";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
@ -26,20 +26,13 @@ import BaseListPage from "./BaseListPage";
|
||||
class UserListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
organizationName: props.match.params.organizationName,
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
organizationName: this.props.match.params.organizationName,
|
||||
organization: null,
|
||||
data: [],
|
||||
pagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
loading: false,
|
||||
searchText: "",
|
||||
searchedColumn: "",
|
||||
isAuthorized: true,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
newUser() {
|
||||
@ -371,17 +364,6 @@ class UserListPage extends BaseListPage {
|
||||
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
|
||||
};
|
||||
|
||||
if (!this.state.isAuthorized) {
|
||||
return (
|
||||
<Result
|
||||
status="403"
|
||||
title="403 Unauthorized"
|
||||
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
|
||||
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table scroll={{x: "max-content"}} columns={columns} dataSource={users} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
|
||||
|
@ -1,32 +0,0 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
import {createButton} from "react-social-login-buttons";
|
||||
import {StaticBaseUrl} from "../Setting";
|
||||
|
||||
function Icon({width = 24, height = 24, color}) {
|
||||
return <img src={`${StaticBaseUrl}/buttons/line.svg`} alt="Sign in with Line" style={{width: 24, height: 24}} />;
|
||||
}
|
||||
|
||||
const config = {
|
||||
text: "Sign in with Line",
|
||||
icon: Icon,
|
||||
iconFormat: name => `fa fa-${name}`,
|
||||
style: {background: "#ffffff", color: "#000000"},
|
||||
activeStyle: {background: "#ededee"},
|
||||
};
|
||||
|
||||
const LineLoginButton = createButton(config);
|
||||
|
||||
export default LineLoginButton;
|
34
web/src/auth/LoginButton.js
Normal file
34
web/src/auth/LoginButton.js
Normal file
@ -0,0 +1,34 @@
|
||||
// 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.
|
||||
|
||||
import i18next from "i18next";
|
||||
import {createButton} from "react-social-login-buttons";
|
||||
import {StaticBaseUrl} from "../Setting";
|
||||
|
||||
function LoginButton({type, align = "center", style = {background: "#ffffff", color: "#000000"}, activeStyle = {background: "#ededee"}}) {
|
||||
function Icon({width = 24, height = 24, color}) {
|
||||
return <img src={`${StaticBaseUrl}/buttons/${type.toLowerCase()}.svg`} alt={`Sign in with ${type}`} style={{width: width, height: height}} />;
|
||||
}
|
||||
const config = {
|
||||
text: `Sign in with ${type}`,
|
||||
icon: Icon,
|
||||
style: style,
|
||||
activeStyle: activeStyle,
|
||||
};
|
||||
const Button = createButton(config);
|
||||
const text = i18next.t("login:Sign in with {type}").replace("{type}", type);
|
||||
return <Button text={text} align={align} />;
|
||||
}
|
||||
|
||||
export default LoginButton;
|
@ -125,6 +125,198 @@ const authInfo = {
|
||||
scope: "profile%20openid%20email",
|
||||
endpoint: "https://access.line.me/oauth2/v2.1/authorize",
|
||||
},
|
||||
Amazon: {
|
||||
scope: "profile",
|
||||
endpoint: "https://www.amazon.com/ap/oa",
|
||||
},
|
||||
Auth0: {
|
||||
scope: "openid%20profile%20email",
|
||||
endpoint: "http://auth0.com/authorize",
|
||||
},
|
||||
BattleNet: {
|
||||
scope: "openid",
|
||||
endpoint: "https://oauth.battlenet.com.cn/authorize",
|
||||
},
|
||||
Bitbucket: {
|
||||
scope: "account",
|
||||
endpoint: "https://bitbucket.org/site/oauth2/authorize",
|
||||
},
|
||||
Box: {
|
||||
scope: "root_readwrite",
|
||||
endpoint: "https://account.box.com/api/oauth2/authorize",
|
||||
},
|
||||
CloudFoundry: {
|
||||
scope: "cloud_controller.read",
|
||||
endpoint: "https://login.cloudfoundry.org/oauth/authorize",
|
||||
},
|
||||
Dailymotion: {
|
||||
scope: "userinfo",
|
||||
endpoint: "https://api.dailymotion.com/oauth/authorize",
|
||||
},
|
||||
Deezer: {
|
||||
scope: "basic_access",
|
||||
endpoint: "https://connect.deezer.com/oauth/auth.php",
|
||||
},
|
||||
DigitalOcean: {
|
||||
scope: "read",
|
||||
endpoint: "https://cloud.digitalocean.com/v1/oauth/authorize",
|
||||
},
|
||||
Discord: {
|
||||
scope: "identify%20email",
|
||||
endpoint: "https://discord.com/api/oauth2/authorize",
|
||||
},
|
||||
Dropbox: {
|
||||
scope: "account_info.read",
|
||||
endpoint: "https://www.dropbox.com/oauth2/authorize",
|
||||
},
|
||||
EveOnline: {
|
||||
scope: "publicData",
|
||||
endpoint: "https://login.eveonline.com/oauth/authorize",
|
||||
},
|
||||
Fitbit: {
|
||||
scope: "activity%20heartrate%20location%20nutrition%20profile%20settings%20sleep%20social%20weight",
|
||||
endpoint: "https://www.fitbit.com/oauth2/authorize",
|
||||
},
|
||||
Gitea: {
|
||||
scope: "user:email",
|
||||
endpoint: "https://gitea.com/login/oauth/authorize",
|
||||
},
|
||||
Heroku: {
|
||||
scope: "global",
|
||||
endpoint: "https://id.heroku.com/oauth/authorize",
|
||||
},
|
||||
InfluxCloud: {
|
||||
scope: "read:org",
|
||||
endpoint: "https://cloud2.influxdata.com/oauth/authorize",
|
||||
},
|
||||
Instagram: {
|
||||
scope: "user_profile",
|
||||
endpoint: "https://api.instagram.com/oauth/authorize",
|
||||
},
|
||||
Intercom: {
|
||||
scope: "user.read",
|
||||
endpoint: "https://app.intercom.com/oauth",
|
||||
},
|
||||
Kakao: {
|
||||
scope: "account_email",
|
||||
endpoint: "https://kauth.kakao.com/oauth/authorize",
|
||||
},
|
||||
Lastfm: {
|
||||
scope: "user_read",
|
||||
endpoint: "https://www.last.fm/api/auth",
|
||||
},
|
||||
Mailru: {
|
||||
scope: "userinfo",
|
||||
endpoint: "https://oauth.mail.ru/login",
|
||||
},
|
||||
Meetup: {
|
||||
scope: "basic",
|
||||
endpoint: "https://secure.meetup.com/oauth2/authorize",
|
||||
},
|
||||
MicrosoftOnline: {
|
||||
scope: "openid%20profile%20email",
|
||||
endpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
|
||||
},
|
||||
Naver: {
|
||||
scope: "profile",
|
||||
endpoint: "https://nid.naver.com/oauth2.0/authorize",
|
||||
},
|
||||
Nextcloud: {
|
||||
scope: "openid%20profile%20email",
|
||||
endpoint: "https://cloud.example.org/apps/oauth2/authorize",
|
||||
},
|
||||
OneDrive: {
|
||||
scope: "offline_access%20onedrive.readonly",
|
||||
endpoint: "https://login.live.com/oauth20_authorize.srf",
|
||||
},
|
||||
Oura: {
|
||||
scope: "personal",
|
||||
endpoint: "https://cloud.ouraring.com/oauth/authorize",
|
||||
},
|
||||
Patreon: {
|
||||
scope: "identity",
|
||||
endpoint: "https://www.patreon.com/oauth2/authorize",
|
||||
},
|
||||
Paypal: {
|
||||
scope: "openid%20profile%20email",
|
||||
endpoint: "https://www.sandbox.paypal.com/connect",
|
||||
},
|
||||
SalesForce: {
|
||||
scope: "openid%20profile%20email",
|
||||
endpoint: "https://login.salesforce.com/services/oauth2/authorize",
|
||||
},
|
||||
Shopify: {
|
||||
scope: "read_products",
|
||||
endpoint: "https://myshopify.com/admin/oauth/authorize",
|
||||
},
|
||||
Soundcloud: {
|
||||
scope: "non-expiring",
|
||||
endpoint: "https://api.soundcloud.com/connect",
|
||||
},
|
||||
Spotify: {
|
||||
scope: "user-read-email",
|
||||
endpoint: "https://accounts.spotify.com/authorize",
|
||||
},
|
||||
Strava: {
|
||||
scope: "read",
|
||||
endpoint: "https://www.strava.com/oauth/authorize",
|
||||
},
|
||||
Stripe: {
|
||||
scope: "read_only",
|
||||
endpoint: "https://connect.stripe.com/oauth/authorize",
|
||||
},
|
||||
TikTok: {
|
||||
scope: "user.info.basic",
|
||||
endpoint: "https://www.tiktok.com/auth/authorize/",
|
||||
},
|
||||
Tumblr: {
|
||||
scope: "email",
|
||||
endpoint: "https://www.tumblr.com/oauth2/authorize",
|
||||
},
|
||||
Twitch: {
|
||||
scope: "user_read",
|
||||
endpoint: "https://id.twitch.tv/oauth2/authorize",
|
||||
},
|
||||
Twitter: {
|
||||
scope: "users.read",
|
||||
endpoint: "https://twitter.com/i/oauth2/authorize",
|
||||
},
|
||||
Typetalk: {
|
||||
scope: "my",
|
||||
endpoint: "https://typetalk.com/oauth2/authorize",
|
||||
},
|
||||
Uber: {
|
||||
scope: "profile",
|
||||
endpoint: "https://login.uber.com/oauth/v2/authorize",
|
||||
},
|
||||
VK: {
|
||||
scope: "email",
|
||||
endpoint: "https://oauth.vk.com/authorize",
|
||||
},
|
||||
Wepay: {
|
||||
scope: "manage_accounts%20view_user",
|
||||
endpoint: "https://www.wepay.com/v2/oauth2/authorize",
|
||||
},
|
||||
Xero: {
|
||||
scope: "openid%20profile%20email",
|
||||
endpoint: "https://login.xero.com/identity/connect/authorize",
|
||||
},
|
||||
Yahoo: {
|
||||
scope: "openid%20profile%20email",
|
||||
endpoint: "https://api.login.yahoo.com/oauth2/request_auth",
|
||||
},
|
||||
Yammer: {
|
||||
scope: "user",
|
||||
endpoint: "https://www.yammer.com/oauth2/authorize",
|
||||
},
|
||||
Yandex: {
|
||||
scope: "login:email",
|
||||
endpoint: "https://oauth.yandex.com/authorize",
|
||||
},
|
||||
Zoom: {
|
||||
scope: "user:read",
|
||||
endpoint: "https://zoom.us/oauth/authorize",
|
||||
},
|
||||
};
|
||||
|
||||
export function getProviderUrl(provider) {
|
||||
@ -184,12 +376,19 @@ export function getAuthUrl(application, provider, method) {
|
||||
|
||||
const isShortState = provider.type === "WeChat" && navigator.userAgent.includes("MicroMessenger");
|
||||
const state = Util.getStateFromQueryParams(application.name, provider.name, method, isShortState);
|
||||
const codeChallenge = "P3S-a7dr8bgM4bF6vOyiKkKETDl16rcAzao9F8UIL1Y"; // SHA256(Base64-URL-encode("casdoor-verifier"))
|
||||
|
||||
if (provider.type === "Google") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
|
||||
} else if (provider.type === "GitHub") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
|
||||
} else if (provider.type === "QQ") {
|
||||
if (provider.type === "Google" || provider.type === "GitHub" || provider.type === "QQ" || provider.type === "Facebook" || provider.type === "DingTalk"
|
||||
|| provider.type === "Weibo" || provider.type === "Gitee" || provider.type === "LinkedIn" || provider.type === "GitLab" || provider.type === "AzureAD"
|
||||
|| provider.type === "Slack" || provider.type === "Line" || provider.type === "Amazon" || provider.type === "Auth0" || provider.type === "BattleNet"
|
||||
|| provider.type === "Bitbucket" || provider.type === "Box" || provider.type === "CloudFoundry" || provider.type === "Dailymotion"
|
||||
|| provider.type === "DigitalOcean" || provider.type === "Discord" || provider.type === "Dropbox" || provider.type === "EveOnline" || provider.type === "Gitea"
|
||||
|| provider.type === "Heroku" || provider.type === "InfluxCloud" || provider.type === "Instagram" || provider.type === "Intercom" || provider.type === "Kakao"
|
||||
|| provider.type === "MailRu" || provider.type === "Meetup" || provider.type === "MicrosoftOnline" || provider.type === "Naver" || provider.type === "Nextcloud"
|
||||
|| provider.type === "OneDrive" || provider.type === "Oura" || provider.type === "Patreon" || provider.type === "PayPal" || provider.type === "SalesForce"
|
||||
|| provider.type === "SoundCloud" || provider.type === "Spotify" || provider.type === "Strava" || provider.type === "Stripe" || provider.type === "Tumblr"
|
||||
|| provider.type === "Twitch" || provider.type === "Typetalk" || provider.type === "Uber" || provider.type === "VK" || provider.type === "Wepay"
|
||||
|| provider.type === "Xero" || provider.type === "Yahoo" || provider.type === "Yammer" || provider.type === "Yandex" || provider.type === "Zoom") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
|
||||
} else if (provider.type === "WeChat") {
|
||||
if (navigator.userAgent.includes("MicroMessenger")) {
|
||||
@ -197,16 +396,6 @@ export function getAuthUrl(application, provider, method) {
|
||||
} else {
|
||||
return `${endpoint}?appid=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}#wechat_redirect`;
|
||||
}
|
||||
} else if (provider.type === "Facebook") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
|
||||
} else if (provider.type === "DingTalk") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}&prompt=consent`;
|
||||
} else if (provider.type === "Weibo") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
|
||||
} else if (provider.type === "Gitee") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
|
||||
} else if (provider.type === "LinkedIn") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
|
||||
} else if (provider.type === "WeCom") {
|
||||
if (provider.subType === "Internal") {
|
||||
if (provider.method === "Silent") {
|
||||
@ -232,8 +421,6 @@ export function getAuthUrl(application, provider, method) {
|
||||
}
|
||||
} else if (provider.type === "Lark") {
|
||||
return `${endpoint}?app_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}`;
|
||||
} else if (provider.type === "GitLab") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}`;
|
||||
} else if (provider.type === "Adfs") {
|
||||
return `${provider.domain}/adfs/oauth2/authorize?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&nonce=casdoor&scope=openid`;
|
||||
} else if (provider.type === "Baidu") {
|
||||
@ -246,21 +433,23 @@ export function getAuthUrl(application, provider, method) {
|
||||
return `${endpoint}?appid=${provider.clientId}&redirect_uri=${redirectUri}?state=${state}`;
|
||||
} else if (provider.type === "Apple") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}&response_mode=form_post`;
|
||||
} else if (provider.type === "AzureAD") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}`;
|
||||
} else if (provider.type === "Slack") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}`;
|
||||
} else if (provider.type === "Steam") {
|
||||
return `${endpoint}?openid.claimed_id=http://specs.openid.net/auth/2.0/identifier_select&openid.identity=http://specs.openid.net/auth/2.0/identifier_select&openid.mode=checkid_setup&openid.ns=http://specs.openid.net/auth/2.0&openid.realm=${window.location.origin}&openid.return_to=${redirectUri}?state=${state}`;
|
||||
} else if (provider.type === "Okta") {
|
||||
return `${provider.domain}/v1/authorize?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}`;
|
||||
} else if (provider.type === "Douyin") {
|
||||
} else if (provider.type === "Douyin" || provider.type === "TikTok") {
|
||||
return `${endpoint}?client_key=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}`;
|
||||
} else if (provider.type === "Custom") {
|
||||
return `${provider.customAuthUrl}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${provider.customScope}&response_type=code&state=${state}`;
|
||||
} else if (provider.type === "Bilibili") {
|
||||
return `${endpoint}#/?client_id=${provider.clientId}&return_url=${redirectUri}&state=${state}&response_type=code`;
|
||||
} else if (provider.type === "Line") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}`;
|
||||
} else if (provider.type === "Deezer") {
|
||||
return `${endpoint}?app_id=${provider.clientId}&redirect_uri=${redirectUri}&perms=${scope}`;
|
||||
} else if (provider.type === "Lastfm") {
|
||||
return `${endpoint}?api_key=${provider.clientId}&cb=${redirectUri}`;
|
||||
} else if (provider.type === "Shopify") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&state=${state}&grant_options[]=per-user`;
|
||||
} else if (provider.type === "Twitter" || provider.type === "Fitbit") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}&code_challenge=${codeChallenge}&code_challenge_method=S256`;
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ import SteamLoginButton from "./SteamLoginButton";
|
||||
import BilibiliLoginButton from "./BilibiliLoginButton";
|
||||
import OktaLoginButton from "./OktaLoginButton";
|
||||
import DouyinLoginButton from "./DouyinLoginButton";
|
||||
import LineLoginButton from "./LineLoginButton";
|
||||
import LoginButton from "./LoginButton";
|
||||
import * as AuthBackend from "./AuthBackend";
|
||||
import {getEvent} from "./Util";
|
||||
import {Modal} from "antd";
|
||||
@ -94,11 +94,9 @@ function getSigninButton(type) {
|
||||
return <OktaLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Douyin") {
|
||||
return <DouyinLoginButton text={text} align={"center"} />;
|
||||
} else if (type === "Line") {
|
||||
return <LineLoginButton text={text} align={"center"} />;
|
||||
} else {
|
||||
return <LoginButton key={type} type={type} />;
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
function getSamlUrl(provider, location) {
|
||||
|
@ -28,9 +28,18 @@ class PolicyTable extends React.Component {
|
||||
editingIndex: "",
|
||||
oldPolicy: "",
|
||||
add: false,
|
||||
page: 1,
|
||||
};
|
||||
}
|
||||
|
||||
count = 0;
|
||||
pageSize = 10;
|
||||
|
||||
getIndex(index) {
|
||||
// Need to be used in all place when modify table. Parameter is the row index in table, need to calculate the index in dataSource.
|
||||
return index + (this.state.page - 1) * 10;
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
if (this.props.mode === "edit") {
|
||||
this.synPolicies();
|
||||
@ -46,8 +55,8 @@ class PolicyTable extends React.Component {
|
||||
};
|
||||
|
||||
cancel = (table, index) => {
|
||||
Object.keys(table[index]).forEach((key) => {
|
||||
table[index][key] = this.state.oldPolicy[key];
|
||||
Object.keys(table[this.getIndex(index)]).forEach((key) => {
|
||||
table[this.getIndex(index)][key] = this.state.oldPolicy[key];
|
||||
});
|
||||
this.updateTable(table);
|
||||
this.setState({editingIndex: "", oldPolicy: ""});
|
||||
@ -62,23 +71,28 @@ class PolicyTable extends React.Component {
|
||||
}
|
||||
|
||||
updateField(table, index, key, value) {
|
||||
table[index][key] = value;
|
||||
table[this.getIndex(index)][key] = value;
|
||||
this.updateTable(table);
|
||||
}
|
||||
|
||||
addRow(table) {
|
||||
const row = {Ptype: "p"};
|
||||
const row = {key: this.count, Ptype: "p"};
|
||||
if (table === undefined) {
|
||||
table = [];
|
||||
}
|
||||
table = Setting.addRow(table, row, "top");
|
||||
|
||||
this.count = this.count + 1;
|
||||
this.updateTable(table);
|
||||
this.edit(row, 0);
|
||||
this.setState({add: true});
|
||||
this.setState({
|
||||
page: 1,
|
||||
add: true,
|
||||
});
|
||||
}
|
||||
|
||||
deleteRow(table, i) {
|
||||
table = Setting.deleteRow(table, i);
|
||||
deleteRow(table, index) {
|
||||
table = Setting.deleteRow(table, this.getIndex(index));
|
||||
this.updateTable(table);
|
||||
}
|
||||
|
||||
@ -91,8 +105,14 @@ class PolicyTable extends React.Component {
|
||||
AdapterBackend.syncPolicies(this.props.owner, this.props.name)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({policyLists: res.data});
|
||||
Setting.showMessage("success", i18next.t("adapter:Sync policies successfully"));
|
||||
|
||||
const policyList = res.data;
|
||||
policyList.map((policy, index) => {
|
||||
policy.key = index;
|
||||
});
|
||||
this.count = policyList.length;
|
||||
this.setState({policyLists: policyList});
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("adapter:Failed to sync policies")}: ${res.msg}`);
|
||||
}
|
||||
@ -129,12 +149,12 @@ class PolicyTable extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
deletePolicy(table, i) {
|
||||
AdapterBackend.RemovePolicy(this.props.owner, this.props.name, table[i]).then(res => {
|
||||
deletePolicy(table, index) {
|
||||
AdapterBackend.RemovePolicy(this.props.owner, this.props.name, table[this.getIndex(index)]).then(res => {
|
||||
if (res.status === "ok") {
|
||||
table = Setting.deleteRow(table, i);
|
||||
this.updateTable(table);
|
||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||
|
||||
this.deleteRow(table, index);
|
||||
} else {
|
||||
Setting.showMessage("error", i18next.t("general:Failed to delete"));
|
||||
}
|
||||
@ -279,9 +299,14 @@ class PolicyTable extends React.Component {
|
||||
return (
|
||||
<Table
|
||||
pagination={{
|
||||
defaultPageSize: 10,
|
||||
defaultPageSize: this.pageSize,
|
||||
onChange: (page) => this.setState({
|
||||
page: page,
|
||||
}),
|
||||
disabled: this.state.editingIndex !== "",
|
||||
current: this.state.page,
|
||||
}}
|
||||
columns={columns} dataSource={table} rowKey="index" size="middle" bordered
|
||||
columns={columns} dataSource={table} rowKey="key" size="middle" bordered
|
||||
loading={this.state.loading}
|
||||
title={() => (
|
||||
<div>
|
||||
|
@ -23,18 +23,19 @@ class PropertyTable extends React.Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
properties: [],
|
||||
count: this.props.properties !== null ? Object.entries(this.props.properties).length : 0,
|
||||
};
|
||||
|
||||
// transfer the Object to object[]
|
||||
if (this.props.properties !== null) {
|
||||
Object.entries(this.props.properties).map((item, index) => {
|
||||
this.state.properties.push({key: index, name: item[0], value: item[1]});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
page = 1;
|
||||
pageSize = 10;
|
||||
count = this.props.properties !== null ? Object.entries(this.props.properties).length : 0;
|
||||
|
||||
updateTable(table) {
|
||||
this.setState({properties: table});
|
||||
@ -46,12 +47,12 @@ class PropertyTable extends React.Component {
|
||||
}
|
||||
|
||||
addRow(table) {
|
||||
const row = {key: this.state.count, name: "", value: ""};
|
||||
const row = {key: this.count, name: "", value: ""};
|
||||
if (table === undefined) {
|
||||
table = [];
|
||||
}
|
||||
table = Setting.addRow(table, row);
|
||||
this.setState({count: this.state.count + 1});
|
||||
this.count = this.count + 1;
|
||||
this.updateTable(table);
|
||||
}
|
||||
|
||||
@ -61,8 +62,8 @@ class PropertyTable extends React.Component {
|
||||
}
|
||||
|
||||
getIndex(index) {
|
||||
// Parameter is the row index in table, need to calculate the index in dataSource. 10 is the pageSize.
|
||||
return index + (this.page - 1) * 10;
|
||||
// Need to be used in all place when modify table. Parameter is the row index in table, need to calculate the index in dataSource.
|
||||
return index + (this.page - 1) * this.pageSize;
|
||||
}
|
||||
|
||||
updateField(table, index, key, value) {
|
||||
@ -114,7 +115,10 @@ class PropertyTable extends React.Component {
|
||||
<Button style={{marginRight: "5px"}} type="primary" size="small" onClick={() => this.addRow(table)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
pagination={{onChange: page => {this.page = page;}}}
|
||||
pagination={{
|
||||
defaultPageSize: this.pageSize,
|
||||
onChange: page => {this.page = page;},
|
||||
}}
|
||||
columns={columns} dataSource={table} rowKey="key" size="middle" bordered
|
||||
/>
|
||||
);
|
||||
|
572
web/yarn.lock
572
web/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user