Compare commits

...

2 Commits

Author SHA1 Message Date
1adb172d6b feat: add more crypto algorithm for jwt signing (#3150)
* feat: add more algorithm support for JWT signing

* feat: add i18n support

* feat: add i18n support

* feat: optimize if statement

* fix: remove additional space line
2024-08-30 16:59:41 +08:00
c08f2b1f3f feat: support Casdoor storage provider (#3147)
* feat: support Casdoor storage provider

* fix: fix code format and nil pointer error

* feat: change cert if statement
2024-08-27 23:54:03 +08:00
37 changed files with 206 additions and 34 deletions

View File

@ -27,7 +27,18 @@ import (
)
func deployStaticFiles(provider *object.Provider) {
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
certificate := ""
if provider.Category == "Storage" && provider.Type == "Casdoor" {
cert, err := object.GetCert(util.GetId(provider.Owner, provider.Cert))
if err != nil {
panic(err)
}
if cert == nil {
panic(err)
}
certificate = cert.Certificate
}
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint, certificate, provider.Content)
if err != nil {
panic(err)
}

4
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/casdoor/go-sms-sender v0.24.0
github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/notify v0.45.0
github.com/casdoor/oss v1.7.0
github.com/casdoor/oss v1.8.0
github.com/casdoor/xorm-adapter/v3 v3.1.0
github.com/casvisor/casvisor-go-sdk v1.4.0
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
@ -30,7 +30,7 @@ require (
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/go-webauthn/webauthn v0.6.0
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/uuid v1.4.0
github.com/google/uuid v1.6.0
github.com/json-iterator/go v1.1.12
github.com/lestrrat-go/jwx v1.2.29
github.com/lib/pq v1.10.9

9
go.sum
View File

@ -1083,6 +1083,8 @@ github.com/casbin/casbin/v2 v2.28.3/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRt
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3jM=
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
github.com/casdoor/casdoor-go-sdk v0.50.0 h1:bUYbz/MzJuWfLKJbJM0+U0YpYewAur+THp5TKnufWZM=
github.com/casdoor/casdoor-go-sdk v0.50.0/go.mod h1:cMnkCQJgMYpgAlgEx8reSt1AVaDIQLcJ1zk5pzBaz+4=
github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXyD9XPs=
github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc=
github.com/casdoor/go-sms-sender v0.24.0 h1:LNLsce3EG/87I3JS6UiajF3LlQmdIiCgebEu0IE4wSM=
@ -1091,8 +1093,8 @@ github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk=
github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ=
github.com/casdoor/oss v1.7.0 h1:VCOuD+CcD0MAA99p6JTyUak14bVR6UsaeyuTaVg0Mrs=
github.com/casdoor/oss v1.7.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
github.com/casdoor/oss v1.8.0 h1:uuyKhDIp7ydOtV4lpqhAY23Ban2Ln8La8+QT36CwylM=
github.com/casdoor/oss v1.8.0/go.mod h1:uaqO7KBI2lnZcnB8rF7O6C2bN7llIbfC5Ql8ex1yR1U=
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
github.com/casvisor/casvisor-go-sdk v1.4.0 h1:hbZEGGJ1cwdHFAxeXrMoNw6yha6Oyg2F0qQhBNCN/dg=
@ -1460,8 +1462,9 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=

View File

@ -97,6 +97,7 @@ type Application struct {
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
TokenSigningMethod string `xorm:"varchar(100)" json:"tokenSigningMethod"`
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
ExpireInHours int `json:"expireInHours"`
RefreshExpireInHours int `json:"refreshExpireInHours"`

View File

@ -112,7 +112,7 @@ func GetOidcDiscovery(host string) OidcDiscovery {
ResponseModesSupported: []string{"query", "fragment", "login", "code", "link"},
GrantTypesSupported: []string{"password", "authorization_code"},
SubjectTypesSupported: []string{"public"},
IdTokenSigningAlgValuesSupported: []string{"RS256"},
IdTokenSigningAlgValuesSupported: []string{"RS256", "RS512", "ES256", "ES384", "ES512"},
ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"},
ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isForbidden", "signupApplication", "ldap"},
RequestParameterSupported: true,

View File

@ -117,7 +117,18 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface, error) {
endpoint := getProviderEndpoint(provider)
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
certificate := ""
if provider.Category == "Storage" && provider.Type == "Casdoor" {
cert, err := GetCert(util.GetId(provider.Owner, provider.Cert))
if err != nil {
return nil, err
}
if cert == nil {
return nil, fmt.Errorf("no cert for %s", provider.Cert)
}
certificate = cert.Certificate
}
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint, certificate, provider.Content)
if err != nil {
return nil, err
}
@ -145,11 +156,15 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
objectKeyRefined := refineObjectKey(provider, objectKey)
_, err = storageProvider.Put(objectKeyRefined, fileBuffer)
object, err := storageProvider.Put(objectKeyRefined, fileBuffer)
if err != nil {
return "", "", err
}
if provider.Type == "Casdoor" {
fileUrl = object.Path
}
return fileUrl, objectKey, nil
}

View File

@ -17,6 +17,7 @@ package object
import (
"fmt"
"reflect"
"strings"
"time"
"github.com/casdoor/casdoor/util"
@ -378,36 +379,52 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
application.TokenFormat = "JWT"
}
var jwtMethod jwt.SigningMethod
if application.TokenSigningMethod == "RS256" {
jwtMethod = jwt.SigningMethodRS256
} else if application.TokenSigningMethod == "RS512" {
jwtMethod = jwt.SigningMethodRS512
} else if application.TokenSigningMethod == "ES256" {
jwtMethod = jwt.SigningMethodES256
} else if application.TokenSigningMethod == "ES512" {
jwtMethod = jwt.SigningMethodES512
} else if application.TokenSigningMethod == "ES384" {
jwtMethod = jwt.SigningMethodES384
} else {
jwtMethod = jwt.SigningMethodRS256
}
// the JWT token length in "JWT-Empty" mode will be very short, as User object only has two properties: owner and name
if application.TokenFormat == "JWT" {
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
token = jwt.NewWithClaims(jwtMethod, claimsWithoutThirdIdp)
claimsWithoutThirdIdp.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsWithoutThirdIdp.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
refreshToken = jwt.NewWithClaims(jwtMethod, claimsWithoutThirdIdp)
} else if application.TokenFormat == "JWT-Empty" {
claimsShort := getShortClaims(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
token = jwt.NewWithClaims(jwtMethod, claimsShort)
claimsShort.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsShort.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
refreshToken = jwt.NewWithClaims(jwtMethod, claimsShort)
} else if application.TokenFormat == "JWT-Custom" {
claimsCustom := getClaimsCustom(claims, application.TokenFields)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsCustom)
token = jwt.NewWithClaims(jwtMethod, claimsCustom)
refreshClaims := getClaimsCustom(claims, application.TokenFields)
refreshClaims["exp"] = jwt.NewNumericDate(refreshExpireTime)
refreshClaims["TokenType"] = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, refreshClaims)
refreshToken = jwt.NewWithClaims(jwtMethod, refreshClaims)
} else if application.TokenFormat == "JWT-Standard" {
claimsStandard := getStandardClaims(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsStandard)
token = jwt.NewWithClaims(jwtMethod, claimsStandard)
claimsStandard.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsStandard.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsStandard)
refreshToken = jwt.NewWithClaims(jwtMethod, claimsStandard)
} else {
return "", "", "", fmt.Errorf("unknown application TokenFormat: %s", application.TokenFormat)
}
@ -425,34 +442,57 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
}
}
// RSA private key
key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
var (
tokenString string
refreshTokenString string
key interface{}
)
if strings.Contains(application.TokenSigningMethod, "RS") || application.TokenSigningMethod == "" {
// RSA private key
key, err = jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
} else if strings.Contains(application.TokenSigningMethod, "ES") {
// ES private key
key, err = jwt.ParseECPrivateKeyFromPEM([]byte(cert.PrivateKey))
} else if strings.Contains(application.TokenSigningMethod, "Ed") {
// Ed private key
key, err = jwt.ParseEdPrivateKeyFromPEM([]byte(cert.PrivateKey))
}
if err != nil {
return "", "", "", err
}
token.Header["kid"] = cert.Name
tokenString, err := token.SignedString(key)
tokenString, err = token.SignedString(key)
if err != nil {
return "", "", "", err
}
refreshTokenString, err := refreshToken.SignedString(key)
refreshTokenString, err = refreshToken.SignedString(key)
return tokenString, refreshTokenString, name, err
}
func ParseJwtToken(token string, cert *Cert) (*Claims, error) {
t, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
var (
certificate interface{}
err error
)
if cert.Certificate == "" {
return nil, fmt.Errorf("the certificate field should not be empty for the cert: %v", cert)
}
// RSA certificate
certificate, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
if _, ok := token.Method.(*jwt.SigningMethodRSA); ok {
// RSA certificate
certificate, err = jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
} else if _, ok := token.Method.(*jwt.SigningMethodECDSA); ok {
// ES certificate
certificate, err = jwt.ParseECPublicKeyFromPEM([]byte(cert.Certificate))
} else {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
if err != nil {
return nil, err
}

19
storage/casdoor.go Normal file
View File

@ -0,0 +1,19 @@
package storage
import (
"github.com/casdoor/oss"
"github.com/casdoor/oss/casdoor"
)
func NewCasdoorStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string, cert string, content string) oss.StorageInterface {
sp := casdoor.New(&casdoor.Config{
clientId,
clientSecret,
endpoint,
cert,
region,
content,
bucket,
})
return sp
}

View File

@ -16,7 +16,7 @@ package storage
import "github.com/casdoor/oss"
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string) (oss.StorageInterface, error) {
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string, cert string, content string) (oss.StorageInterface, error) {
switch providerType {
case "Local File System":
return NewLocalFileSystemStorageProvider(), nil
@ -36,6 +36,8 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
return NewGoogleCloudStorageProvider(clientSecret, bucket, endpoint), nil
case "Synology":
return NewSynologyNasStorageProvider(clientId, clientSecret, endpoint), nil
case "Casdoor":
return NewCasdoorStorageProvider(providerType, clientId, clientSecret, region, bucket, endpoint, cert, content), nil
}
return nil, nil

View File

@ -407,6 +407,16 @@ class ApplicationEditPage extends React.Component {
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Token signing method"), i18next.t("application:Token signing method - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenSigningMethod === "" ? "RS256" : this.state.application.tokenSigningMethod} onChange={(value => {this.updateApplicationField("tokenSigningMethod", value);})}
options={["RS256", "RS512", "ES256", "ES512", "ES384"].map((item) => Setting.getOption(item, item))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Token fields"), i18next.t("application:Token fields - Tooltip"))} :

View File

@ -843,7 +843,7 @@ class ProviderEditPage extends React.Component {
)
}
{
this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "AzureADB2C" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "AzureADB2C" && (this.state.provider.type !== "Casdoor" && this.state.category !== "Storage") && this.state.provider.type !== "Okta" ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
@ -870,7 +870,7 @@ class ProviderEditPage extends React.Component {
</Col>
</Row>
)}
{["Custom HTTP SMS", "Local File System", "MinIO", "Tencent Cloud COS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology"].includes(this.state.provider.type) ? null : (
{["Custom HTTP SMS", "Local File System", "MinIO", "Tencent Cloud COS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology", "Casdoor"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Endpoint (Intranet)"), i18next.t("provider:Region endpoint for Intranet"))} :
@ -885,7 +885,9 @@ class ProviderEditPage extends React.Component {
{["Custom HTTP SMS", "Local File System"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Bucket"), i18next.t("provider:Bucket - Tooltip"))} :
{["Casdoor"].includes(this.state.provider.type) ?
Setting.getLabel(i18next.t("general:Provider"), i18next.t("provider:Provider - Tooltip"))
: Setting.getLabel(i18next.t("provider:Bucket"), i18next.t("provider:Bucket - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.bucket} onChange={e => {
@ -906,7 +908,7 @@ class ProviderEditPage extends React.Component {
</Col>
</Row>
)}
{["Custom HTTP SMS", "Qiniu Cloud Kodo", "Synology"].includes(this.state.provider.type) ? null : (
{["Custom HTTP SMS", "Qiniu Cloud Kodo", "Synology", "Casdoor"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
@ -918,10 +920,24 @@ class ProviderEditPage extends React.Component {
</Col>
</Row>
)}
{["AWS S3", "Tencent Cloud COS", "Qiniu Cloud Kodo"].includes(this.state.provider.type) ? (
{["Casdoor"].includes(this.state.provider.type) ? (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Region ID"), i18next.t("provider:Region ID - Tooltip"))} :
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.content} onChange={e => {
this.updateProviderField("content", e.target.value);
}} />
</Col>
</Row>
) : null}
{["AWS S3", "Tencent Cloud COS", "Qiniu Cloud Kodo", "Casdoor"].includes(this.state.provider.type) ? (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{["Casdoor"].includes(this.state.provider.type) ?
Setting.getLabel(i18next.t("general:Application"), i18next.t("general:Application - Tooltip")) :
Setting.getLabel(i18next.t("provider:Region ID"), i18next.t("provider:Region ID - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.regionId} onChange={e => {
@ -1298,7 +1314,7 @@ class ProviderEditPage extends React.Component {
) : null
}
{
(this.state.provider.type === "Alipay" || this.state.provider.type === "WeChat Pay") ? (
(this.state.provider.type === "Alipay" || this.state.provider.type === "WeChat Pay" || this.state.provider.type === "Casdoor") ? (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Cert"), i18next.t("general:Cert - Tooltip"))} :

View File

@ -229,6 +229,10 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/social_synology.png`,
url: "https://www.synology.com/en-global/dsm/feature/file_sharing",
},
"Casdoor": {
logo: `${StaticBaseUrl}/img/casdoor.png`,
url: "https://casdoor.org/docs/provider/storage/overview",
},
},
SAML: {
"Aliyun IDaaS": {
@ -1062,6 +1066,7 @@ export function getProviderTypeOptions(category) {
{id: "Qiniu Cloud Kodo", name: "Qiniu Cloud Kodo"},
{id: "Google Cloud Storage", name: "Google Cloud Storage"},
{id: "Synology", name: "Synology"},
{id: "Casdoor", name: "Casdoor"},
]
);
} else if (category === "SAML") {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Uživatelská pole zahrnutá v tokenu",
"Token format": "Formát tokenu",
"Token format - Tooltip": "Formát přístupového tokenu",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Nečekali jste, že uvidíte tuto výzvu"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token-Format",
"Token format - Tooltip": "Das Format des Access-Tokens",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Sie sind unerwartet auf diese Aufforderungsseite gelangt"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "The user fields included in the token",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Formato del token",
"Token format - Tooltip": "El formato del token de acceso",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Es inesperado ver esta página de inicio"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Format de jeton",
"Token format - Tooltip": "Le format du jeton d'accès",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Il n'était pas prévu que vous voyez cette page de saisie"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Format token",
"Token format - Tooltip": "Format dari token akses",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Anda tidak mengharapkan untuk melihat halaman prompt ini"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "トークン形式",
"Token format - Tooltip": "アクセストークンのフォーマット",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "このプロンプトページを見ることは予期せぬことである"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "토큰 형식",
"Token format - Tooltip": "접근 토큰의 형식",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "당신은 이 프롬프트 페이지를 볼 것을 예상하지 못했습니다"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Formato do token",
"Token format - Tooltip": "O formato do token de acesso",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Você não deveria ver esta página de prompt"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Формат жетона",
"Token format - Tooltip": "Формат токена доступа",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Вы не ожидали увидеть эту страницу-подсказку"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Používateľské polia zahrnuté v tokene",
"Token format": "Formát tokenu",
"Token format - Tooltip": "Formát prístupového tokenu",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Neočekávali ste, že uvidíte túto výzvu"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Поля маркерів підказка",
"Token format": "Формат маркера",
"Token format - Tooltip": "Формат маркера доступу",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Ви неочікувано побачите цю сторінку запиту"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Định dạng mã thông báo",
"Token format - Tooltip": "Định dạng của mã thông báo truy cập",
"Token signing method": "Token signing method",
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
"You are unexpected to see this prompt page": "Bạn không mong đợi thấy trang này hiện lên"
},
"cert": {

View File

@ -121,6 +121,8 @@
"Token fields - Tooltip": "Token中所包含的用户字段",
"Token format": "Access Token格式",
"Token format - Tooltip": "Access Token格式",
"Token signing method": "Token签名算法",
"Token signing method - Tooltip": "JWT token的签名算法需要与证书算法相匹配",
"You are unexpected to see this prompt page": "错误:该提醒页面不应出现"
},
"cert": {