mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-09 01:13:41 +08:00
Compare commits
69 Commits
Author | SHA1 | Date | |
---|---|---|---|
43bebc03b9 | |||
c5f25cbc7d | |||
3feb6ce84d | |||
08d6b45fc5 | |||
56d0de64dc | |||
1813e8e8c7 | |||
e27c764a55 | |||
e5a2057382 | |||
8457ff7433 | |||
888a6f2feb | |||
b57b64fc36 | |||
0d239ba1cf | |||
8927e08217 | |||
0636069584 | |||
4d0f73c84e | |||
74a2478e10 | |||
acc6f3e887 | |||
185ab9750a | |||
48adc050d6 | |||
b0e318c9db | |||
f9a6efc00f | |||
bd4a6775dd | |||
e3a43d0062 | |||
0cf281cac0 | |||
7322f67ae0 | |||
b927c6d7b4 | |||
01212cd1f3 | |||
bf55f94d41 | |||
f14711d315 | |||
58e1c28f7c | |||
922b19c64b | |||
1d21c3fa90 | |||
6175fd6764 | |||
2ceb54f058 | |||
aaeaa7fefa | |||
d522247552 | |||
79dbdab6c9 | |||
fe40910e3b | |||
2d1736f13a | |||
12b4d1c7cd | |||
a45d2b87c1 | |||
8484465d09 | |||
dff65eee20 | |||
596016456c | |||
673261c258 | |||
3c5985a3c0 | |||
4f3d62520a | |||
96f8b3d937 | |||
7ab5a5ade1 | |||
5cbd0a96ca | |||
7ccd8c4d4f | |||
b0fa3fc484 | |||
af01c4226a | |||
7a3d85a29a | |||
fd5ccd8d41 | |||
a439c5195d | |||
ba2e997d54 | |||
0818de85d1 | |||
457c6098a4 | |||
60f979fbb5 | |||
ff53e44fa6 | |||
1832de47db | |||
535eb0c465 | |||
c190634cf3 | |||
f7559aa040 | |||
1e0b709c73 | |||
c0800b7fb3 | |||
6fcdad2100 | |||
69d26d5c21 |
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -114,12 +114,12 @@ jobs:
|
||||
wait-on-timeout: 210
|
||||
working-directory: ./web
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: cypress-screenshots
|
||||
path: ./web/cypress/screenshots
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: cypress-videos
|
||||
@ -147,7 +147,7 @@ jobs:
|
||||
- name: Release
|
||||
run: yarn global add semantic-release@17.4.4 && semantic-release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Fetch Current version
|
||||
id: get-current-tag
|
||||
|
@ -98,6 +98,7 @@ p, *, *, GET, /api/get-organization-names, *, *
|
||||
p, *, *, GET, /api/get-all-objects, *, *
|
||||
p, *, *, GET, /api/get-all-actions, *, *
|
||||
p, *, *, GET, /api/get-all-roles, *, *
|
||||
p, *, *, GET, /api/run-casbin-command, *, *
|
||||
p, *, *, GET, /api/get-invitation-info, *, *
|
||||
p, *, *, GET, /api/faceid-signin-begin, *, *
|
||||
`
|
||||
|
@ -25,7 +25,10 @@ enableErrorMask = false
|
||||
enableGzip = true
|
||||
inactiveTimeoutMinutes =
|
||||
ldapServerPort = 389
|
||||
ldapsCertId = ""
|
||||
ldapsServerPort = 636
|
||||
radiusServerPort = 1812
|
||||
radiusDefaultOrganization = "built-in"
|
||||
radiusSecret = "secret"
|
||||
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}
|
||||
logConfig = {"filename": "logs/casdoor.log", "maxdays":99999, "perm":"0770"}
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -617,6 +618,17 @@ func (c *ApiController) Login() {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to login in: %s"), err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if provider.EmailRegex != "" {
|
||||
reg, err := regexp.Compile(provider.EmailRegex)
|
||||
if err != nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to login in: %s"), err.Error()))
|
||||
return
|
||||
}
|
||||
if !reg.MatchString(userInfo.Email) {
|
||||
c.ResponseError(fmt.Sprintf(c.T("check:Email is invalid")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if authForm.Method == "signup" {
|
||||
@ -854,6 +866,7 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
if authForm.Passcode != "" {
|
||||
user.CountryCode = user.GetCountryCode(user.CountryCode)
|
||||
mfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferredMfaProps(false))
|
||||
if mfaUtil == nil {
|
||||
c.ResponseError("Invalid multi-factor authentication type")
|
||||
|
114
controllers/casbin_cli_api.go
Normal file
114
controllers/casbin_cli_api.go
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func processArgsToTempFiles(args []string) ([]string, []string, error) {
|
||||
tempFiles := []string{}
|
||||
newArgs := []string{}
|
||||
for i := 0; i < len(args); i++ {
|
||||
if (args[i] == "-m" || args[i] == "-p") && i+1 < len(args) {
|
||||
pattern := fmt.Sprintf("casbin_temp_%s_*.conf", args[i])
|
||||
tempFile, err := os.CreateTemp("", pattern)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to create temp file: %v", err)
|
||||
}
|
||||
|
||||
_, err = tempFile.WriteString(args[i+1])
|
||||
if err != nil {
|
||||
tempFile.Close()
|
||||
return nil, nil, fmt.Errorf("failed to write to temp file: %v", err)
|
||||
}
|
||||
|
||||
tempFile.Close()
|
||||
tempFiles = append(tempFiles, tempFile.Name())
|
||||
newArgs = append(newArgs, args[i], tempFile.Name())
|
||||
i++
|
||||
} else {
|
||||
newArgs = append(newArgs, args[i])
|
||||
}
|
||||
}
|
||||
return tempFiles, newArgs, nil
|
||||
}
|
||||
|
||||
// RunCasbinCommand
|
||||
// @Title RunCasbinCommand
|
||||
// @Tag Enforcer API
|
||||
// @Description Call Casbin CLI commands
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /run-casbin-command [get]
|
||||
func (c *ApiController) RunCasbinCommand() {
|
||||
language := c.Input().Get("language")
|
||||
argString := c.Input().Get("args")
|
||||
|
||||
if language == "" {
|
||||
language = "go"
|
||||
}
|
||||
// use "casbin-go-cli" by default, can be also "casbin-java-cli", "casbin-node-cli", etc.
|
||||
// the pre-built binary of "casbin-go-cli" can be found at: https://github.com/casbin/casbin-go-cli/releases
|
||||
binaryName := fmt.Sprintf("casbin-%s-cli", language)
|
||||
|
||||
_, err := exec.LookPath(binaryName)
|
||||
if err != nil {
|
||||
c.ResponseError(fmt.Sprintf("executable file: %s not found in PATH", binaryName))
|
||||
return
|
||||
}
|
||||
|
||||
// RBAC model & policy example:
|
||||
// https://door.casdoor.com/api/run-casbin-command?language=go&args=["enforce", "-m", "[request_definition]\nr = sub, obj, act\n\n[policy_definition]\np = sub, obj, act\n\n[role_definition]\ng = _, _\n\n[policy_effect]\ne = some(where (p.eft == allow))\n\n[matchers]\nm = g(r.sub, p.sub) %26%26 r.obj == p.obj %26%26 r.act == p.act", "-p", "p, alice, data1, read\np, bob, data2, write\np, data2_admin, data2, read\np, data2_admin, data2, write\ng, alice, data2_admin", "alice", "data1", "read"]
|
||||
// Casbin CLI usage:
|
||||
// https://github.com/jcasbin/casbin-java-cli?tab=readme-ov-file#get-started
|
||||
var args []string
|
||||
err = json.Unmarshal([]byte(argString), &args)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
tempFiles, processedArgs, err := processArgsToTempFiles(args)
|
||||
defer func() {
|
||||
for _, file := range tempFiles {
|
||||
os.Remove(file)
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
command := exec.Command(binaryName, processedArgs...)
|
||||
outputBytes, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
errorString := err.Error()
|
||||
if outputBytes != nil {
|
||||
output := string(outputBytes)
|
||||
errorString = fmt.Sprintf("%s, error: %s", output, err.Error())
|
||||
}
|
||||
|
||||
c.ResponseError(errorString)
|
||||
return
|
||||
}
|
||||
|
||||
output := string(outputBytes)
|
||||
output = strings.TrimSuffix(output, "\n")
|
||||
c.ResponseOk(output)
|
||||
}
|
@ -22,13 +22,6 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
MfaRecoveryCodesSession = "mfa_recovery_codes"
|
||||
MfaCountryCodeSession = "mfa_country_code"
|
||||
MfaDestSession = "mfa_dest"
|
||||
MfaTotpSecretSession = "mfa_totp_secret"
|
||||
)
|
||||
|
||||
// MfaSetupInitiate
|
||||
// @Title MfaSetupInitiate
|
||||
// @Tag MFA API
|
||||
@ -72,11 +65,6 @@ func (c *ApiController) MfaSetupInitiate() {
|
||||
}
|
||||
|
||||
recoveryCode := uuid.NewString()
|
||||
c.SetSession(MfaRecoveryCodesSession, recoveryCode)
|
||||
if mfaType == object.TotpType {
|
||||
c.SetSession(MfaTotpSecretSession, mfaProps.Secret)
|
||||
}
|
||||
|
||||
mfaProps.RecoveryCodes = []string{recoveryCode}
|
||||
|
||||
resp := mfaProps
|
||||
@ -94,6 +82,9 @@ func (c *ApiController) MfaSetupInitiate() {
|
||||
func (c *ApiController) MfaSetupVerify() {
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
passcode := c.Ctx.Request.Form.Get("passcode")
|
||||
secret := c.Ctx.Request.Form.Get("secret")
|
||||
dest := c.Ctx.Request.Form.Get("dest")
|
||||
countryCode := c.Ctx.Request.Form.Get("countryCode")
|
||||
|
||||
if mfaType == "" || passcode == "" {
|
||||
c.ResponseError("missing auth type or passcode")
|
||||
@ -104,32 +95,28 @@ func (c *ApiController) MfaSetupVerify() {
|
||||
MfaType: mfaType,
|
||||
}
|
||||
if mfaType == object.TotpType {
|
||||
secret := c.GetSession(MfaTotpSecretSession)
|
||||
if secret == nil {
|
||||
if secret == "" {
|
||||
c.ResponseError("totp secret is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = secret.(string)
|
||||
config.Secret = secret
|
||||
} else if mfaType == object.SmsType {
|
||||
dest := c.GetSession(MfaDestSession)
|
||||
if dest == nil {
|
||||
if dest == "" {
|
||||
c.ResponseError("destination is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = dest.(string)
|
||||
countryCode := c.GetSession(MfaCountryCodeSession)
|
||||
if countryCode == nil {
|
||||
config.Secret = dest
|
||||
if countryCode == "" {
|
||||
c.ResponseError("country code is missing")
|
||||
return
|
||||
}
|
||||
config.CountryCode = countryCode.(string)
|
||||
config.CountryCode = countryCode
|
||||
} else if mfaType == object.EmailType {
|
||||
dest := c.GetSession(MfaDestSession)
|
||||
if dest == nil {
|
||||
if dest == "" {
|
||||
c.ResponseError("destination is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = dest.(string)
|
||||
config.Secret = dest
|
||||
}
|
||||
|
||||
mfaUtil := object.GetMfaUtil(mfaType, config)
|
||||
@ -159,6 +146,10 @@ func (c *ApiController) MfaSetupEnable() {
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
secret := c.Ctx.Request.Form.Get("secret")
|
||||
dest := c.Ctx.Request.Form.Get("dest")
|
||||
countryCode := c.Ctx.Request.Form.Get("secret")
|
||||
recoveryCodes := c.Ctx.Request.Form.Get("recoveryCodes")
|
||||
|
||||
user, err := object.GetUser(util.GetId(owner, name))
|
||||
if err != nil {
|
||||
@ -176,43 +167,39 @@ func (c *ApiController) MfaSetupEnable() {
|
||||
}
|
||||
|
||||
if mfaType == object.TotpType {
|
||||
secret := c.GetSession(MfaTotpSecretSession)
|
||||
if secret == nil {
|
||||
if secret == "" {
|
||||
c.ResponseError("totp secret is missing")
|
||||
return
|
||||
}
|
||||
config.Secret = secret.(string)
|
||||
config.Secret = secret
|
||||
} else if mfaType == object.EmailType {
|
||||
if user.Email == "" {
|
||||
dest := c.GetSession(MfaDestSession)
|
||||
if dest == nil {
|
||||
if dest == "" {
|
||||
c.ResponseError("destination is missing")
|
||||
return
|
||||
}
|
||||
user.Email = dest.(string)
|
||||
user.Email = dest
|
||||
}
|
||||
} else if mfaType == object.SmsType {
|
||||
if user.Phone == "" {
|
||||
dest := c.GetSession(MfaDestSession)
|
||||
if dest == nil {
|
||||
if dest == "" {
|
||||
c.ResponseError("destination is missing")
|
||||
return
|
||||
}
|
||||
user.Phone = dest.(string)
|
||||
countryCode := c.GetSession(MfaCountryCodeSession)
|
||||
if countryCode == nil {
|
||||
user.Phone = dest
|
||||
if countryCode == "" {
|
||||
c.ResponseError("country code is missing")
|
||||
return
|
||||
}
|
||||
user.CountryCode = countryCode.(string)
|
||||
user.CountryCode = countryCode
|
||||
}
|
||||
}
|
||||
recoveryCodes := c.GetSession(MfaRecoveryCodesSession)
|
||||
if recoveryCodes == nil {
|
||||
|
||||
if recoveryCodes == "" {
|
||||
c.ResponseError("recovery codes is missing")
|
||||
return
|
||||
}
|
||||
config.RecoveryCodes = []string{recoveryCodes.(string)}
|
||||
config.RecoveryCodes = []string{recoveryCodes}
|
||||
|
||||
mfaUtil := object.GetMfaUtil(mfaType, config)
|
||||
if mfaUtil == nil {
|
||||
@ -226,14 +213,6 @@ func (c *ApiController) MfaSetupEnable() {
|
||||
return
|
||||
}
|
||||
|
||||
c.DelSession(MfaRecoveryCodesSession)
|
||||
if mfaType == object.TotpType {
|
||||
c.DelSession(MfaTotpSecretSession)
|
||||
} else {
|
||||
c.DelSession(MfaCountryCodeSession)
|
||||
c.DelSession(MfaDestSession)
|
||||
}
|
||||
|
||||
c.ResponseOk(http.StatusText(http.StatusOK))
|
||||
}
|
||||
|
||||
|
@ -322,17 +322,22 @@ func (c *ApiController) IntrospectToken() {
|
||||
}
|
||||
|
||||
tokenTypeHint := c.Input().Get("token_type_hint")
|
||||
token, err := object.GetTokenByTokenValue(tokenValue, tokenTypeHint)
|
||||
if err != nil {
|
||||
c.ResponseTokenError(err.Error())
|
||||
return
|
||||
}
|
||||
if token == nil {
|
||||
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||
c.ServeJSON()
|
||||
return
|
||||
var token *object.Token
|
||||
if tokenTypeHint != "" {
|
||||
token, err = object.GetTokenByTokenValue(tokenValue, tokenTypeHint)
|
||||
if err != nil {
|
||||
c.ResponseTokenError(err.Error())
|
||||
return
|
||||
}
|
||||
if token == nil {
|
||||
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var introspectionResponse object.IntrospectionResponse
|
||||
|
||||
if application.TokenFormat == "JWT-Standard" {
|
||||
jwtToken, err := object.ParseStandardJwtTokenByApplication(tokenValue, application)
|
||||
if err != nil || jwtToken.Valid() != nil {
|
||||
@ -344,12 +349,37 @@ func (c *ApiController) IntrospectToken() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = &object.IntrospectionResponse{
|
||||
introspectionResponse = object.IntrospectionResponse{
|
||||
Active: true,
|
||||
Scope: jwtToken.Scope,
|
||||
ClientId: clientId,
|
||||
Username: token.User,
|
||||
TokenType: token.TokenType,
|
||||
Username: jwtToken.Name,
|
||||
TokenType: jwtToken.TokenType,
|
||||
Exp: jwtToken.ExpiresAt.Unix(),
|
||||
Iat: jwtToken.IssuedAt.Unix(),
|
||||
Nbf: jwtToken.NotBefore.Unix(),
|
||||
Sub: jwtToken.Subject,
|
||||
Aud: jwtToken.Audience,
|
||||
Iss: jwtToken.Issuer,
|
||||
Jti: jwtToken.ID,
|
||||
}
|
||||
} else {
|
||||
jwtToken, err := object.ParseJwtTokenByApplication(tokenValue, application)
|
||||
if err != nil || jwtToken.Valid() != nil {
|
||||
// and token revoked case. but we not implement
|
||||
// TODO: 2022-03-03 add token revoked check, when we implemented the Token Revocation(rfc7009) Specs.
|
||||
// refs: https://tools.ietf.org/html/rfc7009
|
||||
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
introspectionResponse = object.IntrospectionResponse{
|
||||
Active: true,
|
||||
Scope: jwtToken.Scope,
|
||||
ClientId: clientId,
|
||||
Username: jwtToken.Name,
|
||||
TokenType: jwtToken.TokenType,
|
||||
Exp: jwtToken.ExpiresAt.Unix(),
|
||||
Iat: jwtToken.IssuedAt.Unix(),
|
||||
Nbf: jwtToken.NotBefore.Unix(),
|
||||
@ -358,33 +388,22 @@ func (c *ApiController) IntrospectToken() {
|
||||
Iss: jwtToken.Issuer,
|
||||
Jti: jwtToken.ID,
|
||||
}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
|
||||
jwtToken, err := object.ParseJwtTokenByApplication(tokenValue, application)
|
||||
if err != nil || jwtToken.Valid() != nil {
|
||||
// and token revoked case. but we not implement
|
||||
// TODO: 2022-03-03 add token revoked check, when we implemented the Token Revocation(rfc7009) Specs.
|
||||
// refs: https://tools.ietf.org/html/rfc7009
|
||||
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||
c.ServeJSON()
|
||||
return
|
||||
if tokenTypeHint == "" {
|
||||
token, err = object.GetTokenByTokenValue(tokenValue, introspectionResponse.TokenType)
|
||||
if err != nil {
|
||||
c.ResponseTokenError(err.Error())
|
||||
return
|
||||
}
|
||||
if token == nil {
|
||||
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||
c.ServeJSON()
|
||||
return
|
||||
}
|
||||
}
|
||||
introspectionResponse.TokenType = token.TokenType
|
||||
|
||||
c.Data["json"] = &object.IntrospectionResponse{
|
||||
Active: true,
|
||||
Scope: jwtToken.Scope,
|
||||
ClientId: clientId,
|
||||
Username: token.User,
|
||||
TokenType: token.TokenType,
|
||||
Exp: jwtToken.ExpiresAt.Unix(),
|
||||
Iat: jwtToken.IssuedAt.Unix(),
|
||||
Nbf: jwtToken.NotBefore.Unix(),
|
||||
Sub: jwtToken.Subject,
|
||||
Aud: jwtToken.Audience,
|
||||
Iss: jwtToken.Issuer,
|
||||
Jti: jwtToken.ID,
|
||||
}
|
||||
c.Data["json"] = introspectionResponse
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
@ -364,17 +364,13 @@ func (c *ApiController) AddUser() {
|
||||
return
|
||||
}
|
||||
|
||||
msg := object.CheckUsername(user.Name, c.GetAcceptLanguage())
|
||||
emptyUser := object.User{}
|
||||
msg := object.CheckUpdateUser(&emptyUser, &user, c.GetAcceptLanguage())
|
||||
if msg != "" {
|
||||
c.ResponseError(msg)
|
||||
return
|
||||
}
|
||||
|
||||
if err = object.CheckIpWhitelist(user.IpWhitelist, c.GetAcceptLanguage()); err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddUser(&user))
|
||||
c.ServeJSON()
|
||||
}
|
||||
@ -479,6 +475,16 @@ func (c *ApiController) SetPassword() {
|
||||
|
||||
userId := util.GetId(userOwner, userName)
|
||||
|
||||
user, err := object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if user == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
|
||||
return
|
||||
}
|
||||
|
||||
requestUserId := c.GetSessionUsername()
|
||||
if requestUserId == "" && code == "" {
|
||||
c.ResponseError(c.T("general:Please login first"), "Please login first")
|
||||
@ -522,7 +528,11 @@ func (c *ApiController) SetPassword() {
|
||||
}
|
||||
}
|
||||
} else if code == "" {
|
||||
err = object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||
if user.Ldap == "" {
|
||||
err = object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||
} else {
|
||||
err = object.CheckLdapUserPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||
}
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -565,8 +575,14 @@ func (c *ApiController) SetPassword() {
|
||||
targetUser.Password = newPassword
|
||||
targetUser.UpdateUserPassword(organization)
|
||||
targetUser.NeedUpdatePassword = false
|
||||
targetUser.LastChangePasswordTime = util.GetCurrentTime()
|
||||
|
||||
if user.Ldap == "" {
|
||||
_, err = object.UpdateUser(userId, targetUser, []string{"password", "need_update_password", "password_type", "last_change_password_time"}, false)
|
||||
} else {
|
||||
err = object.ResetLdapPassword(targetUser, newPassword, c.GetAcceptLanguage())
|
||||
}
|
||||
|
||||
_, err = object.UpdateUser(userId, targetUser, []string{"password", "need_update_password", "password_type"}, false)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
@ -246,8 +246,6 @@ func (c *ApiController) SendVerificationCode() {
|
||||
if user != nil && util.GetMaskedEmail(mfaProps.Secret) == vform.Dest {
|
||||
vform.Dest = mfaProps.Secret
|
||||
}
|
||||
} else if vform.Method == MfaSetupVerification {
|
||||
c.SetSession(MfaDestSession, vform.Dest)
|
||||
}
|
||||
|
||||
provider, err = application.GetEmailProvider(vform.Method)
|
||||
@ -282,11 +280,6 @@ func (c *ApiController) SendVerificationCode() {
|
||||
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
|
||||
}
|
||||
}
|
||||
|
||||
if vform.Method == MfaSetupVerification {
|
||||
c.SetSession(MfaCountryCodeSession, vform.CountryCode)
|
||||
c.SetSession(MfaDestSession, vform.Dest)
|
||||
}
|
||||
} else if vform.Method == MfaAuthVerification {
|
||||
mfaProps := user.GetPreferredMfaProps(false)
|
||||
if user != nil && util.GetMaskedPhone(mfaProps.Secret) == vform.Dest {
|
||||
@ -294,6 +287,7 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
|
||||
vform.CountryCode = mfaProps.CountryCode
|
||||
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
|
||||
}
|
||||
|
||||
provider, err = application.GetSmsProvider(vform.Method, vform.CountryCode)
|
||||
|
3
go.mod
3
go.mod
@ -9,7 +9,7 @@ require (
|
||||
github.com/beego/beego v1.12.12
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/casbin/casbin/v2 v2.77.2
|
||||
github.com/casdoor/go-sms-sender v0.24.0
|
||||
github.com/casdoor/go-sms-sender v0.25.0
|
||||
github.com/casdoor/gomail/v2 v2.0.1
|
||||
github.com/casdoor/ldapserver v1.2.0
|
||||
github.com/casdoor/notify v0.45.0
|
||||
@ -63,6 +63,7 @@ require (
|
||||
golang.org/x/crypto v0.21.0
|
||||
golang.org/x/net v0.21.0
|
||||
golang.org/x/oauth2 v0.17.0
|
||||
golang.org/x/text v0.14.0
|
||||
google.golang.org/api v0.150.0
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
|
4
go.sum
4
go.sum
@ -1087,8 +1087,8 @@ github.com/casdoor/casdoor-go-sdk v0.50.0 h1:bUYbz/MzJuWfLKJbJM0+U0YpYewAur+THp5
|
||||
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=
|
||||
github.com/casdoor/go-sms-sender v0.24.0/go.mod h1:bOm4H8/YfJmEHjBatEVQFOnAf0OOn1B0Wi5B7zDhws0=
|
||||
github.com/casdoor/go-sms-sender v0.25.0 h1:eF4cOCSbjVg7+0uLlJQnna/FQ0BWW+Fp/x4cXhzQu1Y=
|
||||
github.com/casdoor/go-sms-sender v0.25.0/go.mod h1:bOm4H8/YfJmEHjBatEVQFOnAf0OOn1B0Wi5B7zDhws0=
|
||||
github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w=
|
||||
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
|
||||
github.com/casdoor/ldapserver v1.2.0 h1:HdSYe+ULU6z9K+2BqgTrJKQRR4//ERAXB64ttOun6Ow=
|
||||
|
@ -1,167 +1,167 @@
|
||||
{
|
||||
"account": {
|
||||
"Failed to add user": "Failed to add user",
|
||||
"Get init score failed, error: %w": "Get init score failed, error: %w",
|
||||
"Please sign out first": "Please sign out first",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
|
||||
"Failed to add user": "عدم موفقیت در افزودن کاربر",
|
||||
"Get init score failed, error: %w": "عدم موفقیت در دریافت امتیاز اولیه، خطا: %w",
|
||||
"Please sign out first": "لطفاً ابتدا خارج شوید",
|
||||
"The application does not allow to sign up new account": "برنامه اجازه ثبتنام حساب جدید را نمیدهد"
|
||||
},
|
||||
"auth": {
|
||||
"Challenge method should be S256": "Challenge method should be S256",
|
||||
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
|
||||
"Failed to login in: %s": "Failed to login in: %s",
|
||||
"Invalid token": "Invalid token",
|
||||
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||
"The organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
|
||||
"Challenge method should be S256": "روش چالش باید S256 باشد",
|
||||
"Failed to create user, user information is invalid: %s": "عدم موفقیت در ایجاد کاربر، اطلاعات کاربر نامعتبر است: %s",
|
||||
"Failed to login in: %s": "عدم موفقیت در ورود: %s",
|
||||
"Invalid token": "توکن نامعتبر",
|
||||
"State expected: %s, but got: %s": "وضعیت مورد انتظار: %s، اما دریافت شد: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "حساب برای ارائهدهنده: %s و نام کاربری: %s (%s) وجود ندارد و مجاز به ثبتنام بهعنوان حساب جدید از طریق %%s نیست، لطفاً از روش دیگری برای ثبتنام استفاده کنید",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "حساب برای ارائهدهنده: %s و نام کاربری: %s (%s) وجود ندارد و مجاز به ثبتنام بهعنوان حساب جدید نیست، لطفاً با پشتیبانی IT خود تماس بگیرید",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "حساب برای ارائهدهنده: %s و نام کاربری: %s (%s) در حال حاضر به حساب دیگری مرتبط است: %s (%s)",
|
||||
"The application: %s does not exist": "برنامه: %s وجود ندارد",
|
||||
"The login method: login with LDAP is not enabled for the application": "روش ورود: ورود با LDAP برای برنامه فعال نیست",
|
||||
"The login method: login with SMS is not enabled for the application": "روش ورود: ورود با پیامک برای برنامه فعال نیست",
|
||||
"The login method: login with email is not enabled for the application": "روش ورود: ورود با ایمیل برای برنامه فعال نیست",
|
||||
"The login method: login with face is not enabled for the application": "روش ورود: ورود با چهره برای برنامه فعال نیست",
|
||||
"The login method: login with password is not enabled for the application": "روش ورود: ورود با رمز عبور برای برنامه فعال نیست",
|
||||
"The organization: %s does not exist": "سازمان: %s وجود ندارد",
|
||||
"The provider: %s is not enabled for the application": "ارائهدهنده: %s برای برنامه فعال نیست",
|
||||
"Unauthorized operation": "عملیات غیرمجاز",
|
||||
"Unknown authentication type (not password or provider), form = %s": "نوع احراز هویت ناشناخته (نه رمز عبور و نه ارائهدهنده)، فرم = %s",
|
||||
"User's tag: %s is not listed in the application's tags": "برچسب کاربر: %s در برچسبهای برنامه فهرست نشده است",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "کاربر پرداختی %s اشتراک فعال یا در انتظار ندارد و برنامه: %s قیمتگذاری پیشفرض ندارد"
|
||||
},
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
"Service %s and %s do not match": "سرویس %s و %s مطابقت ندارند"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
|
||||
"DisplayName cannot be blank": "DisplayName cannot be blank",
|
||||
"DisplayName is not valid real name": "DisplayName is not valid real name",
|
||||
"Email already exists": "Email already exists",
|
||||
"Email cannot be empty": "Email cannot be empty",
|
||||
"Email is invalid": "Email is invalid",
|
||||
"Empty username.": "Empty username.",
|
||||
"Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
|
||||
"Face data mismatch": "Face data mismatch",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"Invitation code cannot be blank": "Invitation code cannot be blank",
|
||||
"Invitation code exhausted": "Invitation code exhausted",
|
||||
"Invitation code is invalid": "Invitation code is invalid",
|
||||
"Invitation code suspended": "Invitation code suspended",
|
||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||
"LastName cannot be blank": "LastName cannot be blank",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
"Please register using the email corresponding to the invitation code": "Please register using the email corresponding to the invitation code",
|
||||
"Please register using the phone corresponding to the invitation code": "Please register using the phone corresponding to the invitation code",
|
||||
"Please register using the username corresponding to the invitation code": "Please register using the username corresponding to the invitation code",
|
||||
"Session outdated, please login again": "Session outdated, please login again",
|
||||
"The invitation code has already been used": "The invitation code has already been used",
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||
"password or code is incorrect": "password or code is incorrect",
|
||||
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
|
||||
"unsupported password type: %s": "unsupported password type: %s"
|
||||
"Affiliation cannot be blank": "وابستگی نمیتواند خالی باشد",
|
||||
"Default code does not match the code's matching rules": "کد پیشفرض با قوانین تطبیق کد مطابقت ندارد",
|
||||
"DisplayName cannot be blank": "نام نمایشی نمیتواند خالی باشد",
|
||||
"DisplayName is not valid real name": "نام نمایشی یک نام واقعی معتبر نیست",
|
||||
"Email already exists": "ایمیل قبلاً وجود دارد",
|
||||
"Email cannot be empty": "ایمیل نمیتواند خالی باشد",
|
||||
"Email is invalid": "ایمیل نامعتبر است",
|
||||
"Empty username.": "نام کاربری خالی است.",
|
||||
"Face data does not exist, cannot log in": "دادههای چهره وجود ندارد، نمیتوان وارد شد",
|
||||
"Face data mismatch": "عدم تطابق دادههای چهره",
|
||||
"FirstName cannot be blank": "نام نمیتواند خالی باشد",
|
||||
"Invitation code cannot be blank": "کد دعوت نمیتواند خالی باشد",
|
||||
"Invitation code exhausted": "کد دعوت استفاده شده است",
|
||||
"Invitation code is invalid": "کد دعوت نامعتبر است",
|
||||
"Invitation code suspended": "کد دعوت معلق است",
|
||||
"LDAP user name or password incorrect": "نام کاربری یا رمز عبور LDAP نادرست است",
|
||||
"LastName cannot be blank": "نام خانوادگی نمیتواند خالی باشد",
|
||||
"Multiple accounts with same uid, please check your ldap server": "چندین حساب با uid یکسان، لطفاً سرور LDAP خود را بررسی کنید",
|
||||
"Organization does not exist": "سازمان وجود ندارد",
|
||||
"Phone already exists": "تلفن قبلاً وجود دارد",
|
||||
"Phone cannot be empty": "تلفن نمیتواند خالی باشد",
|
||||
"Phone number is invalid": "شماره تلفن نامعتبر است",
|
||||
"Please register using the email corresponding to the invitation code": "لطفاً با استفاده از ایمیل مربوط به کد دعوت ثبتنام کنید",
|
||||
"Please register using the phone corresponding to the invitation code": "لطفاً با استفاده از تلفن مربوط به کد دعوت ثبتنام کنید",
|
||||
"Please register using the username corresponding to the invitation code": "لطفاً با استفاده از نام کاربری مربوط به کد دعوت ثبتنام کنید",
|
||||
"Session outdated, please login again": "جلسه منقضی شده است، لطفاً دوباره وارد شوید",
|
||||
"The invitation code has already been used": "کد دعوت قبلاً استفاده شده است",
|
||||
"The user is forbidden to sign in, please contact the administrator": "ورود کاربر ممنوع است، لطفاً با مدیر تماس بگیرید",
|
||||
"The user: %s doesn't exist in LDAP server": "کاربر: %s در سرور LDAP وجود ندارد",
|
||||
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "نام کاربری فقط میتواند حاوی کاراکترهای الفبایی عددی، زیرخط یا خط تیره باشد، نمیتواند خط تیره یا زیرخط متوالی داشته باشد، و نمیتواند با خط تیره یا زیرخط شروع یا پایان یابد.",
|
||||
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "مقدار \"%s\" برای فیلد حساب \"%s\" با عبارت منظم مورد حساب مطابقت ندارد",
|
||||
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "مقدار \"%s\" برای فیلد ثبتنام \"%s\" با عبارت منظم مورد ثبتنام برنامه \"%s\" مطابقت ندارد",
|
||||
"Username already exists": "نام کاربری قبلاً وجود دارد",
|
||||
"Username cannot be an email address": "نام کاربری نمیتواند یک آدرس ایمیل باشد",
|
||||
"Username cannot contain white spaces": "نام کاربری نمیتواند حاوی فاصله باشد",
|
||||
"Username cannot start with a digit": "نام کاربری نمیتواند با یک رقم شروع شود",
|
||||
"Username is too long (maximum is 39 characters).": "نام کاربری بیش از حد طولانی است (حداکثر ۳۹ کاراکتر).",
|
||||
"Username must have at least 2 characters": "نام کاربری باید حداقل ۲ کاراکتر داشته باشد",
|
||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "شما رمز عبور یا کد اشتباه را بیش از حد وارد کردهاید، لطفاً %d دقیقه صبر کنید و دوباره تلاش کنید",
|
||||
"Your region is not allow to signup by phone": "منطقه شما اجازه ثبتنام با تلفن را ندارد",
|
||||
"password or code is incorrect": "رمز عبور یا کد نادرست است",
|
||||
"password or code is incorrect, you have %d remaining chances": "رمز عبور یا کد نادرست است، شما %d فرصت باقیمانده دارید",
|
||||
"unsupported password type: %s": "نوع رمز عبور پشتیبانی نشده: %s"
|
||||
},
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode",
|
||||
"this operation requires administrator to perform": "this operation requires administrator to perform"
|
||||
"Missing parameter": "پارامتر گمشده",
|
||||
"Please login first": "لطفاً ابتدا وارد شوید",
|
||||
"The organization: %s should have one application at least": "سازمان: %s باید حداقل یک برنامه داشته باشد",
|
||||
"The user: %s doesn't exist": "کاربر: %s وجود ندارد",
|
||||
"don't support captchaProvider: ": "از captchaProvider پشتیبانی نمیشود: ",
|
||||
"this operation is not allowed in demo mode": "این عملیات در حالت دمو مجاز نیست",
|
||||
"this operation requires administrator to perform": "این عملیات نیاز به مدیر برای انجام دارد"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Ldap server exist"
|
||||
"Ldap server exist": "سرور LDAP وجود دارد"
|
||||
},
|
||||
"link": {
|
||||
"Please link first": "Please link first",
|
||||
"This application has no providers": "This application has no providers",
|
||||
"This application has no providers of type": "This application has no providers of type",
|
||||
"This provider can't be unlinked": "This provider can't be unlinked",
|
||||
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
|
||||
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
|
||||
"Please link first": "لطفاً ابتدا پیوند دهید",
|
||||
"This application has no providers": "این برنامه ارائهدهندهای ندارد",
|
||||
"This application has no providers of type": "این برنامه ارائهدهندهای از نوع ندارد",
|
||||
"This provider can't be unlinked": "این ارائهدهنده نمیتواند لغو پیوند شود",
|
||||
"You are not the global admin, you can't unlink other users": "شما مدیر جهانی نیستید، نمیتوانید کاربران دیگر را لغو پیوند کنید",
|
||||
"You can't unlink yourself, you are not a member of any application": "شما نمیتوانید خودتان را لغو پیوند کنید، شما عضو هیچ برنامهای نیستید"
|
||||
},
|
||||
"organization": {
|
||||
"Only admin can modify the %s.": "Only admin can modify the %s.",
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
"Only admin can modify the %s.": "فقط مدیر میتواند %s را تغییر دهد.",
|
||||
"The %s is immutable.": "%s غیرقابل تغییر است.",
|
||||
"Unknown modify rule %s.": "قانون تغییر ناشناخته %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
"The permission: \"%s\" doesn't exist": "مجوز: \"%s\" وجود ندارد"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
"Invalid application id": "شناسه برنامه نامعتبر",
|
||||
"the provider: %s does not exist": "ارائهدهنده: %s وجود ندارد"
|
||||
},
|
||||
"resource": {
|
||||
"User is nil for tag: avatar": "User is nil for tag: avatar",
|
||||
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
|
||||
"User is nil for tag: avatar": "کاربر برای برچسب: آواتار تهی است",
|
||||
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "نام کاربری یا مسیر کامل فایل خالی است: نام کاربری = %s، مسیر کامل فایل = %s"
|
||||
},
|
||||
"saml": {
|
||||
"Application %s not found": "Application %s not found"
|
||||
"Application %s not found": "برنامه %s یافت نشد"
|
||||
},
|
||||
"saml_sp": {
|
||||
"provider %s's category is not SAML": "provider %s's category is not SAML"
|
||||
"provider %s's category is not SAML": "دستهبندی ارائهدهنده %s SAML نیست"
|
||||
},
|
||||
"service": {
|
||||
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
|
||||
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
|
||||
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
|
||||
"Empty parameters for emailForm: %v": "پارامترهای خالی برای emailForm: %v",
|
||||
"Invalid Email receivers: %s": "گیرندگان ایمیل نامعتبر: %s",
|
||||
"Invalid phone receivers: %s": "گیرندگان تلفن نامعتبر: %s"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
|
||||
"The provider type: %s is not supported": "The provider type: %s is not supported"
|
||||
"The objectKey: %s is not allowed": "objectKey: %s مجاز نیست",
|
||||
"The provider type: %s is not supported": "نوع ارائهدهنده: %s پشتیبانی نمیشود"
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
|
||||
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
|
||||
"Invalid client_id": "Invalid client_id",
|
||||
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
|
||||
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
|
||||
"Grant_type: %s is not supported in this application": "grant_type: %s در این برنامه پشتیبانی نمیشود",
|
||||
"Invalid application or wrong clientSecret": "برنامه نامعتبر یا clientSecret نادرست",
|
||||
"Invalid client_id": "client_id نامعتبر",
|
||||
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "آدرس بازگشت: %s در لیست آدرسهای بازگشت مجاز وجود ندارد",
|
||||
"Token not found, invalid accessToken": "توکن یافت نشد، accessToken نامعتبر"
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Display name cannot be empty",
|
||||
"New password cannot contain blank space.": "New password cannot contain blank space."
|
||||
"Display name cannot be empty": "نام نمایشی نمیتواند خالی باشد",
|
||||
"New password cannot contain blank space.": "رمز عبور جدید نمیتواند حاوی فاصله خالی باشد."
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "Failed to import users"
|
||||
"Failed to import users": "عدم موفقیت در وارد کردن کاربران"
|
||||
},
|
||||
"util": {
|
||||
"No application is found for userId: %s": "No application is found for userId: %s",
|
||||
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
"No application is found for userId: %s": "هیچ برنامهای برای userId: %s یافت نشد",
|
||||
"No provider for category: %s is found for application: %s": "هیچ ارائهدهندهای برای دستهبندی: %s برای برنامه: %s یافت نشد",
|
||||
"The provider: %s is not found": "ارائهدهنده: %s یافت نشد"
|
||||
},
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Invalid captcha provider.",
|
||||
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
|
||||
"The verification code has not been sent yet!": "The verification code has not been sent yet!",
|
||||
"The verification code has not been sent yet, or has already been used!": "The verification code has not been sent yet, or has already been used!",
|
||||
"Turing test failed.": "Turing test failed.",
|
||||
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
|
||||
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
|
||||
"Unknown type": "Unknown type",
|
||||
"Wrong verification code!": "Wrong verification code!",
|
||||
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||
"Invalid captcha provider.": "ارائهدهنده کپچا نامعتبر.",
|
||||
"Phone number is invalid in your region %s": "شماره تلفن در منطقه شما نامعتبر است %s",
|
||||
"The verification code has not been sent yet!": "کد تأیید هنوز ارسال نشده است!",
|
||||
"The verification code has not been sent yet, or has already been used!": "کد تأیید هنوز ارسال نشده است، یا قبلاً استفاده شده است!",
|
||||
"Turing test failed.": "تست تورینگ ناموفق بود.",
|
||||
"Unable to get the email modify rule.": "عدم توانایی در دریافت قانون تغییر ایمیل.",
|
||||
"Unable to get the phone modify rule.": "عدم توانایی در دریافت قانون تغییر تلفن.",
|
||||
"Unknown type": "نوع ناشناخته",
|
||||
"Wrong verification code!": "کد تأیید اشتباه!",
|
||||
"You should verify your code in %d min!": "شما باید کد خود را در %d دقیقه تأیید کنید!",
|
||||
"please add a SMS provider to the \"Providers\" list for the application: %s": "لطفاً یک ارائهدهنده پیامک به لیست \"ارائهدهندگان\" برای برنامه: %s اضافه کنید",
|
||||
"please add an Email provider to the \"Providers\" list for the application: %s": "لطفاً یک ارائهدهنده ایمیل به لیست \"ارائهدهندگان\" برای برنامه: %s اضافه کنید",
|
||||
"the user does not exist, please sign up first": "کاربر وجود ندارد، لطفاً ابتدا ثبتنام کنید"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
|
||||
"Found no credentials for this user": "هیچ اعتباری برای این کاربر یافت نشد",
|
||||
"Please call WebAuthnSigninBegin first": "لطفاً ابتدا WebAuthnSigninBegin را فراخوانی کنید"
|
||||
}
|
||||
}
|
||||
|
@ -15,10 +15,10 @@
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Аккаунт для провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован как новый аккаунт. Пожалуйста, обратитесь в службу поддержки IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Аккаунт поставщика: %s и имя пользователя: %s (%s) уже связаны с другим аккаунтом: %s (%s)",
|
||||
"The application: %s does not exist": "Приложение: %s не существует",
|
||||
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
|
||||
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
|
||||
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
|
||||
"The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
|
||||
"The login method: login with LDAP is not enabled for the application": "Метод входа в систему: вход с помощью LDAP не включен для приложения",
|
||||
"The login method: login with SMS is not enabled for the application": "Метод входа: вход с помощью SMS не включен для приложения",
|
||||
"The login method: login with email is not enabled for the application": "Метод входа: вход с помощью электронной почты не включен для приложения",
|
||||
"The login method: login with face is not enabled for the application": "Метод входа: вход с помощью лица не включен для приложения",
|
||||
"The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения",
|
||||
"The organization: %s does not exist": "The organization: %s does not exist",
|
||||
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
|
||||
@ -53,16 +53,16 @@
|
||||
"Phone already exists": "Телефон уже существует",
|
||||
"Phone cannot be empty": "Телефон не может быть пустым",
|
||||
"Phone number is invalid": "Номер телефона является недействительным",
|
||||
"Please register using the email corresponding to the invitation code": "Please register using the email corresponding to the invitation code",
|
||||
"Please register using the phone corresponding to the invitation code": "Please register using the phone corresponding to the invitation code",
|
||||
"Please register using the username corresponding to the invitation code": "Please register using the username corresponding to the invitation code",
|
||||
"Please register using the email corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь, используя электронную почту, соответствующую коду приглашения",
|
||||
"Please register using the phone corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь по телефону, соответствующему коду приглашения",
|
||||
"Please register using the username corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь, используя имя пользователя, соответствующее коду приглашения",
|
||||
"Session outdated, please login again": "Сессия устарела, пожалуйста, войдите снова",
|
||||
"The invitation code has already been used": "The invitation code has already been used",
|
||||
"The user is forbidden to sign in, please contact the administrator": "Пользователю запрещен вход, пожалуйста, обратитесь к администратору",
|
||||
"The user: %s doesn't exist in LDAP server": "Пользователь %s не существует на LDAP сервере",
|
||||
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "Имя пользователя может состоять только из буквенно-цифровых символов, нижних подчеркиваний или дефисов, не может содержать последовательные дефисы или подчеркивания, а также не может начинаться или заканчиваться на дефис или подчеркивание.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Значение \\\"%s\\\" для поля аккаунта \\\"%s\\\" не соответствует регулярному значению",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Значение \\\"%s\\\" поля регистрации \\\"%s\\\" не соответствует регулярному выражению приложения \\\"%s\\\"",
|
||||
"Username already exists": "Имя пользователя уже существует",
|
||||
"Username cannot be an email address": "Имя пользователя не может быть адресом электронной почты",
|
||||
"Username cannot contain white spaces": "Имя пользователя не может содержать пробелы",
|
||||
@ -78,11 +78,11 @@
|
||||
"general": {
|
||||
"Missing parameter": "Отсутствующий параметр",
|
||||
"Please login first": "Пожалуйста, сначала войдите в систему",
|
||||
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
|
||||
"The organization: %s should have one application at least": "Организация: %s должна иметь хотя бы одно приложение",
|
||||
"The user: %s doesn't exist": "Пользователь %s не существует",
|
||||
"don't support captchaProvider: ": "неподдерживаемый captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "эта операция не разрешена в демо-режиме",
|
||||
"this operation requires administrator to perform": "this operation requires administrator to perform"
|
||||
"this operation requires administrator to perform": "для выполнения этой операции требуется администратор"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP-сервер существует"
|
||||
@ -101,11 +101,11 @@
|
||||
"Unknown modify rule %s.": "Неизвестное изменение правила %s."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
|
||||
"The permission: \\\"%s\\\" doesn't exist": "Разрешение: \\\"%s\\\" не существует"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Неверный идентификатор приложения",
|
||||
"the provider: %s does not exist": "провайдер: %s не существует"
|
||||
"the provider: %s does not exist": "Провайдер: %s не существует"
|
||||
},
|
||||
"resource": {
|
||||
"User is nil for tag: avatar": "Пользователь равен нулю для тега: аватар",
|
||||
@ -115,7 +115,7 @@
|
||||
"Application %s not found": "Приложение %s не найдено"
|
||||
},
|
||||
"saml_sp": {
|
||||
"provider %s's category is not SAML": "категория провайдера %s не является SAML"
|
||||
"provider %s's category is not SAML": "Категория провайдера %s не является SAML"
|
||||
},
|
||||
"service": {
|
||||
"Empty parameters for emailForm: %v": "Пустые параметры для emailForm: %v",
|
||||
@ -148,7 +148,7 @@
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Недействительный поставщик CAPTCHA.",
|
||||
"Phone number is invalid in your region %s": "Номер телефона недействителен в вашем регионе %s",
|
||||
"The verification code has not been sent yet!": "The verification code has not been sent yet!",
|
||||
"The verification code has not been sent yet!": "Код проверки еще не отправлен!",
|
||||
"The verification code has not been sent yet, or has already been used!": "The verification code has not been sent yet, or has already been used!",
|
||||
"Turing test failed.": "Тест Тьюринга не удался.",
|
||||
"Unable to get the email modify rule.": "Невозможно получить правило изменения электронной почты.",
|
||||
@ -156,8 +156,8 @@
|
||||
"Unknown type": "Неизвестный тип",
|
||||
"Wrong verification code!": "Неправильный код подтверждения!",
|
||||
"You should verify your code in %d min!": "Вы должны проверить свой код через %d минут!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "Пожалуйста, добавьте поставщика SMS в список \\\"Провайдеры\\\" для приложения: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "Пожалуйста, добавьте поставщика электронной почты в список \\\"Провайдеры\\\" для приложения: %s",
|
||||
"the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь"
|
||||
},
|
||||
"webauthn": {
|
||||
|
@ -188,10 +188,23 @@ type GitHubUserInfo struct {
|
||||
} `json:"plan"`
|
||||
}
|
||||
|
||||
type GitHubUserEmailInfo struct {
|
||||
Email string `json:"email"`
|
||||
Primary bool `json:"primary"`
|
||||
Verified bool `json:"verified"`
|
||||
Visibility string `json:"visibility"`
|
||||
}
|
||||
|
||||
type GitHubErrorInfo struct {
|
||||
Message string `json:"message"`
|
||||
DocumentationUrl string `json:"documentation_url"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func (idp *GithubIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
req, err := http.NewRequest("GET", "https://api.github.com/user", nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Authorization", "token "+token.AccessToken)
|
||||
resp, err := idp.Client.Do(req)
|
||||
@ -212,6 +225,42 @@ func (idp *GithubIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if githubUserInfo.Email == "" {
|
||||
reqEmail, err := http.NewRequest("GET", "https://api.github.com/user/emails", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reqEmail.Header.Add("Authorization", "token "+token.AccessToken)
|
||||
respEmail, err := idp.Client.Do(reqEmail)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer respEmail.Body.Close()
|
||||
emailBody, err := io.ReadAll(respEmail.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if respEmail.StatusCode != 200 {
|
||||
var errMessage GitHubErrorInfo
|
||||
err = json.Unmarshal(emailBody, &errMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("GithubIdProvider:GetUserInfo() error, status code = %d, error message = %v\n", respEmail.StatusCode, errMessage)
|
||||
} else {
|
||||
var userEmails []GitHubUserEmailInfo
|
||||
err = json.Unmarshal(emailBody, &userEmails)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
githubUserInfo.Email = idp.getEmailFromEmailsResult(userEmails)
|
||||
}
|
||||
}
|
||||
|
||||
userInfo := UserInfo{
|
||||
Id: strconv.Itoa(githubUserInfo.Id),
|
||||
Username: githubUserInfo.Login,
|
||||
@ -248,3 +297,27 @@ func (idp *GithubIdProvider) postWithBody(body interface{}, url string) ([]byte,
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (idp *GithubIdProvider) getEmailFromEmailsResult(emailInfo []GitHubUserEmailInfo) string {
|
||||
primaryEmail := ""
|
||||
verifiedEmail := ""
|
||||
|
||||
for _, addr := range emailInfo {
|
||||
if !addr.Verified || strings.Contains(addr.Email, "users.noreply.github.com") {
|
||||
continue
|
||||
}
|
||||
|
||||
if addr.Primary {
|
||||
primaryEmail = addr.Email
|
||||
break
|
||||
} else if verifiedEmail == "" {
|
||||
verifiedEmail = addr.Email
|
||||
}
|
||||
}
|
||||
|
||||
if primaryEmail != "" {
|
||||
return primaryEmail
|
||||
}
|
||||
|
||||
return verifiedEmail
|
||||
}
|
||||
|
161
idp/kwai.go
Normal file
161
idp/kwai.go
Normal file
@ -0,0 +1,161 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
package idp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type KwaiIdProvider struct {
|
||||
Client *http.Client
|
||||
Config *oauth2.Config
|
||||
}
|
||||
|
||||
func NewKwaiIdProvider(clientId string, clientSecret string, redirectUrl string) *KwaiIdProvider {
|
||||
idp := &KwaiIdProvider{}
|
||||
idp.Config = idp.getConfig(clientId, clientSecret, redirectUrl)
|
||||
return idp
|
||||
}
|
||||
|
||||
func (idp *KwaiIdProvider) SetHttpClient(client *http.Client) {
|
||||
idp.Client = client
|
||||
}
|
||||
|
||||
func (idp *KwaiIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
|
||||
endpoint := oauth2.Endpoint{
|
||||
TokenURL: "https://open.kuaishou.com/oauth2/access_token",
|
||||
AuthURL: "https://open.kuaishou.com/oauth2/authorize", // qr code: /oauth2/connect
|
||||
}
|
||||
|
||||
config := &oauth2.Config{
|
||||
Scopes: []string{"user_info"},
|
||||
Endpoint: endpoint,
|
||||
ClientID: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
RedirectURL: redirectUrl,
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type KwaiTokenResp struct {
|
||||
Result int `json:"result"`
|
||||
ErrorMsg string `json:"error_msg"`
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
RefreshTokenExpiresIn int `json:"refresh_token_expires_in"`
|
||||
OpenId string `json:"open_id"`
|
||||
Scopes []string `json:"scopes"`
|
||||
}
|
||||
|
||||
// GetToken use code to get access_token
|
||||
func (idp *KwaiIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
params := map[string]string{
|
||||
"app_id": idp.Config.ClientID,
|
||||
"app_secret": idp.Config.ClientSecret,
|
||||
"code": code,
|
||||
"grant_type": "authorization_code",
|
||||
}
|
||||
tokenUrl := fmt.Sprintf("%s?app_id=%s&app_secret=%s&code=%s&grant_type=authorization_code",
|
||||
idp.Config.Endpoint.TokenURL, params["app_id"], params["app_secret"], params["code"])
|
||||
resp, err := idp.Client.Get(tokenUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tokenResp KwaiTokenResp
|
||||
err = json.Unmarshal(body, &tokenResp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tokenResp.Result != 1 {
|
||||
return nil, fmt.Errorf("get token error: %s", tokenResp.ErrorMsg)
|
||||
}
|
||||
|
||||
token := &oauth2.Token{
|
||||
AccessToken: tokenResp.AccessToken,
|
||||
RefreshToken: tokenResp.RefreshToken,
|
||||
Expiry: time.Now().Add(time.Duration(tokenResp.ExpiresIn) * time.Second),
|
||||
}
|
||||
|
||||
raw := make(map[string]interface{})
|
||||
raw["open_id"] = tokenResp.OpenId
|
||||
token = token.WithExtra(raw)
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// More details: https://open.kuaishou.com/openapi/user_info
|
||||
type KwaiUserInfo struct {
|
||||
Result int `json:"result"`
|
||||
ErrorMsg string `json:"error_msg"`
|
||||
UserInfo struct {
|
||||
Head string `json:"head"`
|
||||
Name string `json:"name"`
|
||||
Sex string `json:"sex"`
|
||||
City string `json:"city"`
|
||||
} `json:"user_info"`
|
||||
}
|
||||
|
||||
// GetUserInfo use token to get user profile
|
||||
func (idp *KwaiIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
userInfoUrl := fmt.Sprintf("https://open.kuaishou.com/openapi/user_info?app_id=%s&access_token=%s",
|
||||
idp.Config.ClientID, token.AccessToken)
|
||||
|
||||
resp, err := idp.Client.Get(userInfoUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var kwaiUserInfo KwaiUserInfo
|
||||
err = json.Unmarshal(body, &kwaiUserInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if kwaiUserInfo.Result != 1 {
|
||||
return nil, fmt.Errorf("get user info error: %s", kwaiUserInfo.ErrorMsg)
|
||||
}
|
||||
|
||||
userInfo := &UserInfo{
|
||||
Id: token.Extra("open_id").(string),
|
||||
Username: kwaiUserInfo.UserInfo.Name,
|
||||
DisplayName: kwaiUserInfo.UserInfo.Name,
|
||||
AvatarUrl: kwaiUserInfo.UserInfo.Head,
|
||||
Extra: map[string]string{
|
||||
"gender": kwaiUserInfo.UserInfo.Sex,
|
||||
"city": kwaiUserInfo.UserInfo.City,
|
||||
},
|
||||
}
|
||||
|
||||
return userInfo, nil
|
||||
}
|
@ -113,6 +113,8 @@ func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) (IdProvider, error
|
||||
return NewOktaIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl), nil
|
||||
case "Douyin":
|
||||
return NewDouyinIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
case "Kwai":
|
||||
return NewKwaiIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
case "Bilibili":
|
||||
return NewBilibiliIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
|
||||
case "MetaMask":
|
||||
|
@ -15,6 +15,7 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"log"
|
||||
@ -27,21 +28,68 @@ import (
|
||||
|
||||
func StartLdapServer() {
|
||||
ldapServerPort := conf.GetConfigString("ldapServerPort")
|
||||
if ldapServerPort == "" || ldapServerPort == "0" {
|
||||
return
|
||||
}
|
||||
ldapsServerPort := conf.GetConfigString("ldapsServerPort")
|
||||
|
||||
server := ldap.NewServer()
|
||||
serverSsl := ldap.NewServer()
|
||||
routes := ldap.NewRouteMux()
|
||||
|
||||
routes.Bind(handleBind)
|
||||
routes.Search(handleSearch).Label(" SEARCH****")
|
||||
|
||||
server.Handle(routes)
|
||||
err := server.ListenAndServe("0.0.0.0:" + ldapServerPort)
|
||||
serverSsl.Handle(routes)
|
||||
go func() {
|
||||
if ldapServerPort == "" || ldapServerPort == "0" {
|
||||
return
|
||||
}
|
||||
err := server.ListenAndServe("0.0.0.0:" + ldapServerPort)
|
||||
if err != nil {
|
||||
log.Printf("StartLdapServer() failed, err = %s", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
if ldapsServerPort == "" || ldapsServerPort == "0" {
|
||||
return
|
||||
}
|
||||
ldapsCertId := conf.GetConfigString("ldapsCertId")
|
||||
if ldapsCertId == "" {
|
||||
return
|
||||
}
|
||||
config, err := getTLSconfig(ldapsCertId)
|
||||
if err != nil {
|
||||
log.Printf("StartLdapsServer() failed, err = %s", err.Error())
|
||||
return
|
||||
}
|
||||
secureConn := func(s *ldap.Server) {
|
||||
s.Listener = tls.NewListener(s.Listener, config)
|
||||
}
|
||||
err = serverSsl.ListenAndServe("0.0.0.0:"+ldapsServerPort, secureConn)
|
||||
if err != nil {
|
||||
log.Printf("StartLdapsServer() failed, err = %s", err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func getTLSconfig(ldapsCertId string) (*tls.Config, error) {
|
||||
rawCert, err := object.GetCert(ldapsCertId)
|
||||
if err != nil {
|
||||
log.Printf("StartLdapServer() failed, err = %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
if rawCert == nil {
|
||||
return nil, fmt.Errorf("cert is empty")
|
||||
}
|
||||
cert, err := tls.X509KeyPair([]byte(rawCert.Certificate), []byte(rawCert.PrivateKey))
|
||||
if err != nil {
|
||||
return &tls.Config{}, err
|
||||
}
|
||||
|
||||
return &tls.Config{
|
||||
MinVersion: tls.VersionTLS10,
|
||||
MaxVersion: tls.VersionTLS13,
|
||||
Certificates: []tls.Certificate{cert},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
|
||||
@ -142,7 +190,7 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
||||
}
|
||||
for _, attr := range attrs {
|
||||
e.AddAttribute(message.AttributeDescription(attr), getAttribute(string(attr), user))
|
||||
if string(attr) == "cn" {
|
||||
if string(attr) == "title" {
|
||||
e.AddAttribute(message.AttributeDescription(attr), getAttribute("title", user))
|
||||
}
|
||||
}
|
||||
|
5
main.go
5
main.go
@ -83,6 +83,11 @@ func main() {
|
||||
// logs.SetLevel(logs.LevelInformational)
|
||||
logs.SetLogFuncCall(false)
|
||||
|
||||
err = util.StopOldInstance(port)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go ldap.StartLdapServer()
|
||||
go radius.StartRadiusServer()
|
||||
go object.ClearThroughputPerSecond()
|
||||
|
@ -723,8 +723,15 @@ func (application *Application) GetId() string {
|
||||
}
|
||||
|
||||
func (application *Application) IsRedirectUriValid(redirectUri string) bool {
|
||||
redirectUris := append([]string{"http://localhost:", "https://localhost:", "http://127.0.0.1:", "http://casdoor-app", ".chromiumapp.org"}, application.RedirectUris...)
|
||||
for _, targetUri := range redirectUris {
|
||||
isValid, err := util.IsValidOrigin(redirectUri)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if isValid {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, targetUri := range application.RedirectUris {
|
||||
targetUriRegex := regexp.MustCompile(targetUri)
|
||||
if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) {
|
||||
return true
|
||||
|
@ -273,7 +273,7 @@ func CheckPasswordComplexity(user *User, password string) string {
|
||||
return CheckPasswordComplexityByOrg(organization, password)
|
||||
}
|
||||
|
||||
func checkLdapUserPassword(user *User, password string, lang string) error {
|
||||
func CheckLdapUserPassword(user *User, password string, lang string) error {
|
||||
ldaps, err := GetLdaps(user.Owner)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -368,7 +368,7 @@ func CheckUserPassword(organization string, username string, password string, la
|
||||
}
|
||||
|
||||
// only for LDAP users
|
||||
err = checkLdapUserPassword(user, password, lang)
|
||||
err = CheckLdapUserPassword(user, password, lang)
|
||||
if err != nil {
|
||||
if err.Error() == "user not exist" {
|
||||
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user: %s doesn't exist in LDAP server"), username)
|
||||
@ -381,7 +381,13 @@ func CheckUserPassword(organization string, username string, password string, la
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = checkPasswordExpired(user, lang)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
@ -520,11 +526,46 @@ func CheckUsername(username string, lang string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func CheckUsernameWithEmail(username string, lang string) string {
|
||||
if username == "" {
|
||||
return i18n.Translate(lang, "check:Empty username.")
|
||||
} else if len(username) > 39 {
|
||||
return i18n.Translate(lang, "check:Username is too long (maximum is 39 characters).")
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
|
||||
|
||||
if !util.ReUserNameWithEmail.MatchString(username) {
|
||||
return i18n.Translate(lang, "check:Username supports email format. Also The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline. Also pay attention to the email format.")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func CheckUpdateUser(oldUser, user *User, lang string) string {
|
||||
if oldUser.Name != user.Name {
|
||||
if msg := CheckUsername(user.Name, lang); msg != "" {
|
||||
return msg
|
||||
organizationName := oldUser.Owner
|
||||
if organizationName == "" {
|
||||
organizationName = user.Owner
|
||||
}
|
||||
|
||||
organization, err := getOrganization("admin", organizationName)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
if organization == nil {
|
||||
return fmt.Sprintf(i18n.Translate(lang, "auth:The organization: %s does not exist"), organizationName)
|
||||
}
|
||||
|
||||
if organization.UseEmailAsUsername {
|
||||
if msg := CheckUsernameWithEmail(user.Name, lang); msg != "" {
|
||||
return msg
|
||||
}
|
||||
} else {
|
||||
if msg := CheckUsername(user.Name, lang); msg != "" {
|
||||
return msg
|
||||
}
|
||||
}
|
||||
|
||||
if HasUserByField(user.Owner, "name", user.Name) {
|
||||
return i18n.Translate(lang, "check:Username already exists")
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ func CheckEntryIp(clientIp string, user *User, application *Application, organiz
|
||||
if err != nil {
|
||||
application.IpRestriction = err.Error() + application.Name
|
||||
return fmt.Errorf(err.Error() + application.Name)
|
||||
} else {
|
||||
application.IpRestriction = ""
|
||||
}
|
||||
|
||||
if organization == nil && application.OrganizationObj != nil {
|
||||
@ -55,6 +57,8 @@ func CheckEntryIp(clientIp string, user *User, application *Application, organiz
|
||||
if err != nil {
|
||||
organization.IpRestriction = err.Error() + organization.Name
|
||||
return fmt.Errorf(err.Error() + organization.Name)
|
||||
} else {
|
||||
organization.IpRestriction = ""
|
||||
}
|
||||
}
|
||||
|
||||
|
53
object/check_password_expired.go
Normal file
53
object/check_password_expired.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
func checkPasswordExpired(user *User, lang string) error {
|
||||
organization, err := GetOrganizationByUser(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if organization == nil {
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Organization does not exist"))
|
||||
}
|
||||
|
||||
passwordExpireDays := organization.PasswordExpireDays
|
||||
if passwordExpireDays <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
lastChangePasswordTime := user.LastChangePasswordTime
|
||||
if lastChangePasswordTime == "" {
|
||||
if user.CreatedTime == "" {
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Your password has expired. Please reset your password by clicking \"Forgot password\""))
|
||||
}
|
||||
lastChangePasswordTime = user.CreatedTime
|
||||
}
|
||||
|
||||
lastTime := util.String2Time(lastChangePasswordTime)
|
||||
expireTime := lastTime.AddDate(0, 0, passwordExpireDays)
|
||||
if time.Now().After(expireTime) {
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Your password has expired. Please reset your password by clicking \"Forgot password\""))
|
||||
}
|
||||
return nil
|
||||
}
|
@ -19,124 +19,90 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Dashboard struct {
|
||||
OrganizationCounts []int `json:"organizationCounts"`
|
||||
UserCounts []int `json:"userCounts"`
|
||||
ProviderCounts []int `json:"providerCounts"`
|
||||
ApplicationCounts []int `json:"applicationCounts"`
|
||||
SubscriptionCounts []int `json:"subscriptionCounts"`
|
||||
type DashboardDateItem struct {
|
||||
CreatedTime string `json:"createTime"`
|
||||
}
|
||||
|
||||
func GetDashboard(owner string) (*Dashboard, error) {
|
||||
type DashboardMapItem struct {
|
||||
dashboardDateItems []DashboardDateItem
|
||||
itemCount int64
|
||||
}
|
||||
|
||||
func GetDashboard(owner string) (*map[string][]int64, error) {
|
||||
if owner == "All" {
|
||||
owner = ""
|
||||
}
|
||||
|
||||
dashboard := &Dashboard{
|
||||
OrganizationCounts: make([]int, 31),
|
||||
UserCounts: make([]int, 31),
|
||||
ProviderCounts: make([]int, 31),
|
||||
ApplicationCounts: make([]int, 31),
|
||||
SubscriptionCounts: make([]int, 31),
|
||||
dashboard := make(map[string][]int64)
|
||||
dashboardMap := sync.Map{}
|
||||
tableNames := []string{"organization", "user", "provider", "application", "subscription", "role", "group", "resource", "cert", "permission", "transaction", "model", "adapter", "enforcer"}
|
||||
|
||||
time30day := time.Now().AddDate(0, 0, -30)
|
||||
var wg sync.WaitGroup
|
||||
var err error
|
||||
wg.Add(len(tableNames))
|
||||
ch := make(chan error, len(tableNames))
|
||||
for _, tableName := range tableNames {
|
||||
dashboard[tableName+"Counts"] = make([]int64, 31)
|
||||
tableName := tableName
|
||||
go func(ch chan error) {
|
||||
defer wg.Done()
|
||||
dashboardDateItems := []DashboardDateItem{}
|
||||
var countResult int64
|
||||
|
||||
dbQueryBefore := ormer.Engine.Cols("created_time")
|
||||
dbQueryAfter := ormer.Engine.Cols("created_time")
|
||||
|
||||
if owner != "" {
|
||||
dbQueryAfter = dbQueryAfter.And("owner = ?", owner)
|
||||
dbQueryBefore = dbQueryBefore.And("owner = ?", owner)
|
||||
}
|
||||
|
||||
if countResult, err = dbQueryBefore.And("created_time < ?", time30day).Table(tableName).Count(); err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
if err = dbQueryAfter.And("created_time >= ?", time30day).Table(tableName).Find(&dashboardDateItems); err != nil {
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
|
||||
dashboardMap.Store(tableName, DashboardMapItem{
|
||||
dashboardDateItems: dashboardDateItems,
|
||||
itemCount: countResult,
|
||||
})
|
||||
}(ch)
|
||||
}
|
||||
|
||||
organizations := []Organization{}
|
||||
users := []User{}
|
||||
providers := []Provider{}
|
||||
applications := []Application{}
|
||||
subscriptions := []Subscription{}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(5)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := ormer.Engine.Find(&organizations, &Organization{Owner: owner}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
if err := ormer.Engine.Find(&users, &User{Owner: owner}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
if err := ormer.Engine.Find(&providers, &Provider{Owner: owner}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
if err := ormer.Engine.Find(&applications, &Application{Owner: owner}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
if err := ormer.Engine.Find(&subscriptions, &Subscription{Owner: owner}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
wg.Wait()
|
||||
close(ch)
|
||||
|
||||
for err = range ch {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
nowTime := time.Now()
|
||||
for i := 30; i >= 0; i-- {
|
||||
cutTime := nowTime.AddDate(0, 0, -i)
|
||||
dashboard.OrganizationCounts[30-i] = countCreatedBefore(organizations, cutTime)
|
||||
dashboard.UserCounts[30-i] = countCreatedBefore(users, cutTime)
|
||||
dashboard.ProviderCounts[30-i] = countCreatedBefore(providers, cutTime)
|
||||
dashboard.ApplicationCounts[30-i] = countCreatedBefore(applications, cutTime)
|
||||
dashboard.SubscriptionCounts[30-i] = countCreatedBefore(subscriptions, cutTime)
|
||||
for _, tableName := range tableNames {
|
||||
item, exist := dashboardMap.Load(tableName)
|
||||
if !exist {
|
||||
continue
|
||||
}
|
||||
dashboard[tableName+"Counts"][30-i] = countCreatedBefore(item.(DashboardMapItem), cutTime)
|
||||
}
|
||||
}
|
||||
return dashboard, nil
|
||||
return &dashboard, nil
|
||||
}
|
||||
|
||||
func countCreatedBefore(objects interface{}, before time.Time) int {
|
||||
count := 0
|
||||
switch obj := objects.(type) {
|
||||
case []Organization:
|
||||
for _, o := range obj {
|
||||
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", o.CreatedTime)
|
||||
if createdTime.Before(before) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
case []User:
|
||||
for _, u := range obj {
|
||||
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", u.CreatedTime)
|
||||
if createdTime.Before(before) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
case []Provider:
|
||||
for _, p := range obj {
|
||||
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", p.CreatedTime)
|
||||
if createdTime.Before(before) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
case []Application:
|
||||
for _, a := range obj {
|
||||
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", a.CreatedTime)
|
||||
if createdTime.Before(before) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
case []Subscription:
|
||||
for _, s := range obj {
|
||||
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", s.CreatedTime)
|
||||
if createdTime.Before(before) {
|
||||
count++
|
||||
}
|
||||
func countCreatedBefore(dashboardMapItem DashboardMapItem, before time.Time) int64 {
|
||||
count := dashboardMapItem.itemCount
|
||||
for _, e := range dashboardMapItem.dashboardDateItems {
|
||||
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", e.CreatedTime)
|
||||
if createdTime.Before(before) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
|
@ -20,9 +20,11 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
goldap "github.com/go-ldap/ldap/v3"
|
||||
"github.com/thanhpk/randstr"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
)
|
||||
|
||||
type LdapConn struct {
|
||||
@ -371,6 +373,64 @@ func GetExistUuids(owner string, uuids []string) ([]string, error) {
|
||||
return existUuids, nil
|
||||
}
|
||||
|
||||
func ResetLdapPassword(user *User, newPassword string, lang string) error {
|
||||
ldaps, err := GetLdaps(user.Owner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ldapServer := range ldaps {
|
||||
conn, err := ldapServer.GetLdapConn()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
searchReq := goldap.NewSearchRequest(ldapServer.BaseDn, goldap.ScopeWholeSubtree, goldap.NeverDerefAliases,
|
||||
0, 0, false, ldapServer.buildAuthFilterString(user), []string{}, nil)
|
||||
|
||||
searchResult, err := conn.Conn.Search(searchReq)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if len(searchResult.Entries) == 0 {
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
if len(searchResult.Entries) > 1 {
|
||||
conn.Close()
|
||||
return fmt.Errorf(i18n.Translate(lang, "check:Multiple accounts with same uid, please check your ldap server"))
|
||||
}
|
||||
|
||||
userDn := searchResult.Entries[0].DN
|
||||
|
||||
var pwdEncoded string
|
||||
modifyPasswordRequest := goldap.NewModifyRequest(userDn, nil)
|
||||
if conn.IsAD {
|
||||
utf16 := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM)
|
||||
pwdEncoded, err := utf16.NewEncoder().String("\"" + newPassword + "\"")
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
modifyPasswordRequest.Replace("unicodePwd", []string{pwdEncoded})
|
||||
modifyPasswordRequest.Replace("userAccountControl", []string{"512"})
|
||||
} else {
|
||||
pwdEncoded = newPassword
|
||||
modifyPasswordRequest.Replace("userPassword", []string{pwdEncoded})
|
||||
}
|
||||
|
||||
err = conn.Conn.Modify(modifyPasswordRequest)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return err
|
||||
}
|
||||
conn.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ldapUser *LdapUser) buildLdapUserName(owner string) (string, error) {
|
||||
user := User{}
|
||||
uidWithNumber := fmt.Sprintf("%s_%s", ldapUser.Uid, ldapUser.UidNumber)
|
||||
|
@ -62,6 +62,7 @@ type Organization struct {
|
||||
PasswordOptions []string `xorm:"varchar(100)" json:"passwordOptions"`
|
||||
PasswordObfuscatorType string `xorm:"varchar(100)" json:"passwordObfuscatorType"`
|
||||
PasswordObfuscatorKey string `xorm:"varchar(100)" json:"passwordObfuscatorKey"`
|
||||
PasswordExpireDays int `json:"passwordExpireDays"`
|
||||
CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
|
||||
DefaultAvatar string `xorm:"varchar(200)" json:"defaultAvatar"`
|
||||
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
|
||||
|
@ -16,6 +16,7 @@ package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/beego/beego/context"
|
||||
@ -70,6 +71,7 @@ type Provider struct {
|
||||
IdP string `xorm:"mediumtext" json:"idP"`
|
||||
IssuerUrl string `xorm:"varchar(100)" json:"issuerUrl"`
|
||||
EnableSignAuthnRequest bool `json:"enableSignAuthnRequest"`
|
||||
EmailRegex string `xorm:"varchar(200)" json:"emailRegex"`
|
||||
|
||||
ProviderUrl string `xorm:"varchar(200)" json:"providerUrl"`
|
||||
}
|
||||
@ -200,6 +202,13 @@ func UpdateProvider(id string, provider *Provider) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if provider.EmailRegex != "" {
|
||||
_, err := regexp.Compile(provider.EmailRegex)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if name != provider.Name {
|
||||
err := providerChangeTrigger(name, provider.Name)
|
||||
if err != nil {
|
||||
@ -234,6 +243,13 @@ func AddProvider(provider *Provider) (bool, error) {
|
||||
provider.IntranetEndpoint = util.GetEndPoint(provider.IntranetEndpoint)
|
||||
}
|
||||
|
||||
if provider.EmailRegex != "" {
|
||||
_, err := regexp.Compile(provider.EmailRegex)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.Insert(provider)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@ -421,7 +437,7 @@ func FromProviderToIdpInfo(ctx *context.Context, provider *Provider) *idp.Provid
|
||||
providerInfo.ClientId = provider.ClientId2
|
||||
providerInfo.ClientSecret = provider.ClientSecret2
|
||||
}
|
||||
} else if provider.Type == "AzureAD" || provider.Type == "AzureADB2C" || provider.Type == "ADFS" || provider.Type == "Okta" {
|
||||
} else if provider.Type == "ADFS" || provider.Type == "AzureAD" || provider.Type == "AzureADB2C" || provider.Type == "Casdoor" || provider.Type == "Okta" {
|
||||
providerInfo.HostUrl = provider.Domain
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ var (
|
||||
|
||||
func init() {
|
||||
logPostOnly = conf.GetConfigBool("logPostOnly")
|
||||
passwordRegex = regexp.MustCompile("\"password\":\".+\"")
|
||||
passwordRegex = regexp.MustCompile("\"password\":\"([^\"]*?)\"")
|
||||
}
|
||||
|
||||
type Record struct {
|
||||
|
@ -338,6 +338,10 @@ func roleChangeTrigger(oldName string, newName string) error {
|
||||
|
||||
for _, role := range roles {
|
||||
for j, u := range role.Roles {
|
||||
if u == "*" {
|
||||
continue
|
||||
}
|
||||
|
||||
owner, name := util.GetOwnerAndNameFromId(u)
|
||||
if name == oldName {
|
||||
role.Roles[j] = util.GetId(owner, newName)
|
||||
@ -358,6 +362,10 @@ func roleChangeTrigger(oldName string, newName string) error {
|
||||
for _, permission := range permissions {
|
||||
for j, u := range permission.Roles {
|
||||
// u = organization/username
|
||||
if u == "*" {
|
||||
continue
|
||||
}
|
||||
|
||||
owner, name := util.GetOwnerAndNameFromId(u)
|
||||
if name == oldName {
|
||||
permission.Roles[j] = util.GetId(owner, newName)
|
||||
|
@ -26,6 +26,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/beevik/etree"
|
||||
@ -222,10 +223,13 @@ func GetSamlMeta(application *Application, host string, enablePostBinding bool)
|
||||
originFrontend, originBackend := getOriginFromHost(host)
|
||||
|
||||
idpLocation := ""
|
||||
idpBinding := ""
|
||||
if enablePostBinding {
|
||||
idpLocation = fmt.Sprintf("%s/api/saml/redirect/%s/%s", originBackend, application.Owner, application.Name)
|
||||
idpBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
} else {
|
||||
idpLocation = fmt.Sprintf("%s/login/saml/authorize/%s/%s", originFrontend, application.Owner, application.Name)
|
||||
idpBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
|
||||
}
|
||||
|
||||
d := IdpEntityDescriptor{
|
||||
@ -258,7 +262,7 @@ func GetSamlMeta(application *Application, host string, enablePostBinding bool)
|
||||
{Xmlns: "urn:oasis:names:tc:SAML:2.0:assertion", Name: "Name", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", FriendlyName: "Name"},
|
||||
},
|
||||
SingleSignOnService: SingleSignOnService{
|
||||
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
|
||||
Binding: idpBinding,
|
||||
Location: idpLocation,
|
||||
},
|
||||
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
|
||||
@ -273,29 +277,38 @@ func GetSamlMeta(application *Application, host string, enablePostBinding bool)
|
||||
func GetSamlResponse(application *Application, user *User, samlRequest string, host string) (string, string, string, error) {
|
||||
// request type
|
||||
method := "GET"
|
||||
|
||||
samlRequest = strings.ReplaceAll(samlRequest, " ", "+")
|
||||
// base64 decode
|
||||
defated, err := base64.StdEncoding.DecodeString(samlRequest)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("err: Failed to decode SAML request, %s", err.Error())
|
||||
}
|
||||
|
||||
// decompress
|
||||
var buffer bytes.Buffer
|
||||
rdr := flate.NewReader(bytes.NewReader(defated))
|
||||
var requestByte []byte
|
||||
|
||||
for {
|
||||
_, err = io.CopyN(&buffer, rdr, 1024)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
if strings.Contains(string(defated), "xmlns:") {
|
||||
requestByte = defated
|
||||
} else {
|
||||
// decompress
|
||||
var buffer bytes.Buffer
|
||||
rdr := flate.NewReader(bytes.NewReader(defated))
|
||||
|
||||
for {
|
||||
|
||||
_, err = io.CopyN(&buffer, rdr, 1024)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return "", "", "", err
|
||||
}
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
requestByte = buffer.Bytes()
|
||||
}
|
||||
|
||||
var authnRequest saml.AuthNRequest
|
||||
err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
|
||||
err = xml.Unmarshal(requestByte, &authnRequest)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("err: Failed to unmarshal AuthnRequest, please check the SAML request, %s", err.Error())
|
||||
}
|
||||
|
@ -102,14 +102,6 @@ func GetTokenByAccessToken(accessToken string) (*Token, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !existed {
|
||||
token = Token{AccessToken: accessToken}
|
||||
existed, err = ormer.Engine.Get(&token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !existed {
|
||||
return nil, nil
|
||||
}
|
||||
@ -123,14 +115,6 @@ func GetTokenByRefreshToken(refreshToken string) (*Token, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !existed {
|
||||
token = Token{RefreshToken: refreshToken}
|
||||
existed, err = ormer.Engine.Get(&token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if !existed {
|
||||
return nil, nil
|
||||
}
|
||||
@ -140,6 +124,7 @@ func GetTokenByRefreshToken(refreshToken string) (*Token, error) {
|
||||
func GetTokenByTokenValue(tokenValue, tokenTypeHint string) (*Token, error) {
|
||||
switch tokenTypeHint {
|
||||
case "access_token":
|
||||
case "access-token":
|
||||
token, err := GetTokenByAccessToken(tokenValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -148,6 +133,7 @@ func GetTokenByTokenValue(tokenValue, tokenTypeHint string) (*Token, error) {
|
||||
return token, nil
|
||||
}
|
||||
case "refresh_token":
|
||||
case "refresh-token":
|
||||
token, err := GetTokenByRefreshToken(tokenValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -184,6 +185,15 @@ func StoreCasTokenForProxyTicket(token *CasAuthenticationSuccess, targetService,
|
||||
return proxyTicket
|
||||
}
|
||||
|
||||
func escapeXMLText(input string) (string, error) {
|
||||
var sb strings.Builder
|
||||
err := xml.EscapeText(&sb, []byte(input))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sb.String(), nil
|
||||
}
|
||||
|
||||
func GenerateCasToken(userId string, service string) (string, error) {
|
||||
user, err := GetUser(userId)
|
||||
if err != nil {
|
||||
@ -225,6 +235,11 @@ func GenerateCasToken(userId string, service string) (string, error) {
|
||||
}
|
||||
|
||||
if value != "" {
|
||||
if escapedValue, err := escapeXMLText(value); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
value = escapedValue
|
||||
}
|
||||
authenticationSuccess.Attributes.UserAttributes.Attributes = append(authenticationSuccess.Attributes.UserAttributes.Attributes, &CasNamedAttribute{
|
||||
Name: k,
|
||||
Value: value,
|
||||
|
@ -309,22 +309,29 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
||||
}, nil
|
||||
}
|
||||
|
||||
var oldTokenScope string
|
||||
if application.TokenFormat == "JWT-Standard" {
|
||||
_, err = ParseStandardJwtToken(refreshToken, cert)
|
||||
oldToken, err := ParseStandardJwtToken(refreshToken, cert)
|
||||
if err != nil {
|
||||
return &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
oldTokenScope = oldToken.Scope
|
||||
} else {
|
||||
_, err = ParseJwtToken(refreshToken, cert)
|
||||
oldToken, err := ParseJwtToken(refreshToken, cert)
|
||||
if err != nil {
|
||||
return &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
oldTokenScope = oldToken.Scope
|
||||
}
|
||||
|
||||
if scope == "" {
|
||||
scope = oldTokenScope
|
||||
}
|
||||
|
||||
// generate a new token
|
||||
@ -504,7 +511,7 @@ func GetPasswordToken(application *Application, username string, password string
|
||||
}
|
||||
|
||||
if user.Ldap != "" {
|
||||
err = checkLdapUserPassword(user, password, "en")
|
||||
err = CheckLdapUserPassword(user, password, "en")
|
||||
} else {
|
||||
err = CheckPassword(user, password, "en")
|
||||
}
|
||||
|
@ -129,6 +129,7 @@ type User struct {
|
||||
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
|
||||
Okta string `xorm:"okta varchar(100)" json:"okta"`
|
||||
Douyin string `xorm:"douyin varchar(100)" json:"douyin"`
|
||||
Kwai string `xorm:"kwai varchar(100)" json:"kwai"`
|
||||
Line string `xorm:"line varchar(100)" json:"line"`
|
||||
Amazon string `xorm:"amazon varchar(100)" json:"amazon"`
|
||||
Auth0 string `xorm:"auth0 varchar(100)" json:"auth0"`
|
||||
@ -200,8 +201,9 @@ type User struct {
|
||||
Permissions []*Permission `json:"permissions"`
|
||||
Groups []string `xorm:"groups varchar(1000)" json:"groups"`
|
||||
|
||||
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
|
||||
SigninWrongTimes int `json:"signinWrongTimes"`
|
||||
LastChangePasswordTime string `xorm:"varchar(100)" json:"lastChangePasswordTime"`
|
||||
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
|
||||
SigninWrongTimes int `json:"signinWrongTimes"`
|
||||
|
||||
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
|
||||
MfaAccounts []MfaAccount `xorm:"mfaAccounts blob" json:"mfaAccounts"`
|
||||
@ -236,6 +238,7 @@ type MfaAccount struct {
|
||||
AccountName string `xorm:"varchar(100)" json:"accountName"`
|
||||
Issuer string `xorm:"varchar(100)" json:"issuer"`
|
||||
SecretKey string `xorm:"varchar(100)" json:"secretKey"`
|
||||
Origin string `xorm:"varchar(100)" json:"origin"`
|
||||
}
|
||||
|
||||
type FaceId struct {
|
||||
@ -678,6 +681,10 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
user.Password = oldUser.Password
|
||||
}
|
||||
|
||||
if user.Id != oldUser.Id && user.Id == "" {
|
||||
user.Id = oldUser.Id
|
||||
}
|
||||
|
||||
if user.Avatar != oldUser.Avatar && user.Avatar != "" && user.PermanentAvatar != "*" {
|
||||
user.PermanentAvatar, err = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, false)
|
||||
if err != nil {
|
||||
@ -690,9 +697,9 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
"owner", "display_name", "avatar", "first_name", "last_name",
|
||||
"location", "address", "country_code", "region", "language", "affiliation", "title", "id_card_type", "id_card", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
||||
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", "face_ids", "mfaAccounts",
|
||||
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret", "mfa_phone_enabled", "mfa_email_enabled",
|
||||
"signin_wrong_times", "last_change_password_time", "last_signin_wrong_time", "groups", "access_key", "access_secret", "mfa_phone_enabled", "mfa_email_enabled",
|
||||
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
||||
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "azureadb2c", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
||||
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "azureadb2c", "slack", "steam", "bilibili", "okta", "douyin", "kwai", "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",
|
||||
@ -816,6 +823,10 @@ func AddUser(user *User) (bool, error) {
|
||||
user.UpdateUserPassword(organization)
|
||||
}
|
||||
|
||||
if user.CreatedTime == "" {
|
||||
user.CreatedTime = util.GetCurrentTime()
|
||||
}
|
||||
|
||||
err = user.UpdateUserHash()
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -57,7 +57,7 @@ type VerificationRecord struct {
|
||||
Receiver string `xorm:"varchar(100) index notnull" json:"receiver"`
|
||||
Code string `xorm:"varchar(10) notnull" json:"code"`
|
||||
Time int64 `xorm:"notnull" json:"time"`
|
||||
IsUsed bool
|
||||
IsUsed bool `xorm:"notnull" json:"isUsed"`
|
||||
}
|
||||
|
||||
func IsAllowSend(user *User, remoteAddr, recordType string) error {
|
||||
|
@ -68,8 +68,10 @@ func handleAccessRequest(w radius.ResponseWriter, r *radius.Request) {
|
||||
log.Printf("handleAccessRequest() username=%v, org=%v, password=%v", username, organization, password)
|
||||
|
||||
if organization == "" {
|
||||
w.Write(r.Response(radius.CodeAccessReject))
|
||||
return
|
||||
organization = conf.GetConfigString("radiusDefaultOrganization")
|
||||
if organization == "" {
|
||||
organization = "built-in"
|
||||
}
|
||||
}
|
||||
|
||||
var user *object.User
|
||||
|
@ -16,11 +16,11 @@ package routers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/beego/beego/context"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -52,7 +52,13 @@ func CorsFilter(ctx *context.Context) {
|
||||
origin = ""
|
||||
}
|
||||
|
||||
if strings.HasPrefix(origin, "http://localhost") || strings.HasPrefix(origin, "https://localhost") || strings.HasPrefix(origin, "http://127.0.0.1") || strings.HasPrefix(origin, "http://casdoor-app") || strings.Contains(origin, ".chromiumapp.org") {
|
||||
isValid, err := util.IsValidOrigin(origin)
|
||||
if err != nil {
|
||||
ctx.ResponseWriter.WriteHeader(http.StatusForbidden)
|
||||
responseError(ctx, err.Error())
|
||||
return
|
||||
}
|
||||
if isValid {
|
||||
setCorsHeaders(ctx, origin)
|
||||
return
|
||||
}
|
||||
|
@ -174,6 +174,8 @@ func initAPI() {
|
||||
beego.Router("/api/get-all-actions", &controllers.ApiController{}, "GET:GetAllActions")
|
||||
beego.Router("/api/get-all-roles", &controllers.ApiController{}, "GET:GetAllRoles")
|
||||
|
||||
beego.Router("/api/run-casbin-command", &controllers.ApiController{}, "GET:RunCasbinCommand")
|
||||
|
||||
beego.Router("/api/get-sessions", &controllers.ApiController{}, "GET:GetSessions")
|
||||
beego.Router("/api/get-session", &controllers.ApiController{}, "GET:GetSingleSession")
|
||||
beego.Router("/api/update-session", &controllers.ApiController{}, "POST:UpdateSession")
|
||||
|
21
storage/cucloud_oss.go
Normal file
21
storage/cucloud_oss.go
Normal file
@ -0,0 +1,21 @@
|
||||
package storage
|
||||
|
||||
import (
|
||||
awss3 "github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/casdoor/oss"
|
||||
"github.com/casdoor/oss/s3"
|
||||
)
|
||||
|
||||
func NewCUCloudOssStorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
|
||||
sp := s3.New(&s3.Config{
|
||||
AccessID: clientId,
|
||||
AccessKey: clientSecret,
|
||||
Region: region,
|
||||
Bucket: bucket,
|
||||
Endpoint: endpoint,
|
||||
S3Endpoint: endpoint,
|
||||
ACL: awss3.BucketCannedACLPublicRead,
|
||||
})
|
||||
|
||||
return sp
|
||||
}
|
@ -23,7 +23,10 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
|
||||
case "AWS S3":
|
||||
return NewAwsS3StorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
|
||||
case "MinIO":
|
||||
return NewMinIOS3StorageProvider(clientId, clientSecret, "_", bucket, endpoint), nil
|
||||
if region == "" {
|
||||
region = "_"
|
||||
}
|
||||
return NewMinIOS3StorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
|
||||
case "Aliyun OSS":
|
||||
return NewAliyunOssStorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
|
||||
case "Tencent Cloud COS":
|
||||
@ -38,6 +41,8 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
|
||||
return NewSynologyNasStorageProvider(clientId, clientSecret, endpoint), nil
|
||||
case "Casdoor":
|
||||
return NewCasdoorStorageProvider(providerType, clientId, clientSecret, region, bucket, endpoint, cert, content), nil
|
||||
case "CUCloud OSS":
|
||||
return NewCUCloudOssStorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
@ -7558,6 +7558,9 @@
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"kwai": {
|
||||
"type": "string"
|
||||
},
|
||||
"language": {
|
||||
"type": "string"
|
||||
},
|
||||
|
@ -4981,6 +4981,8 @@ definitions:
|
||||
karma:
|
||||
type: integer
|
||||
format: int64
|
||||
kwai:
|
||||
type: string
|
||||
language:
|
||||
type: string
|
||||
lark:
|
||||
|
97
util/process.go
Normal file
97
util/process.go
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2025 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.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getPidByPort(port int) (int, error) {
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd = exec.Command("cmd", "/c", "netstat -ano | findstr :"+strconv.Itoa(port))
|
||||
case "darwin", "linux":
|
||||
cmd = exec.Command("lsof", "-t", "-i", ":"+strconv.Itoa(port))
|
||||
default:
|
||||
return 0, fmt.Errorf("unsupported OS: %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||
if exitErr.ExitCode() == 1 {
|
||||
return 0, nil
|
||||
}
|
||||
} else {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
lines := strings.Split(string(output), "\n")
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) > 0 {
|
||||
if runtime.GOOS == "windows" {
|
||||
if fields[1] == "0.0.0.0:"+strconv.Itoa(port) {
|
||||
pid, err := strconv.Atoi(fields[len(fields)-1])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return pid, nil
|
||||
}
|
||||
} else {
|
||||
pid, err := strconv.Atoi(fields[0])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return pid, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func StopOldInstance(port int) error {
|
||||
pid, err := getPidByPort(port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if pid == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
process, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = process.Kill()
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
fmt.Printf("The old instance with pid: %d has been stopped\n", pid)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -17,6 +17,7 @@ package util
|
||||
import (
|
||||
"fmt"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
@ -24,10 +25,11 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
rePhone *regexp.Regexp
|
||||
ReWhiteSpace *regexp.Regexp
|
||||
ReFieldWhiteList *regexp.Regexp
|
||||
ReUserName *regexp.Regexp
|
||||
rePhone *regexp.Regexp
|
||||
ReWhiteSpace *regexp.Regexp
|
||||
ReFieldWhiteList *regexp.Regexp
|
||||
ReUserName *regexp.Regexp
|
||||
ReUserNameWithEmail *regexp.Regexp
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -35,6 +37,7 @@ func init() {
|
||||
ReWhiteSpace, _ = regexp.Compile(`\s`)
|
||||
ReFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
|
||||
ReUserName, _ = regexp.Compile("^[a-zA-Z0-9]+([-._][a-zA-Z0-9]+)*$")
|
||||
ReUserNameWithEmail, _ = regexp.Compile(`^([a-zA-Z0-9]+([-._][a-zA-Z0-9]+)*)|([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$`) // Add support for email formats
|
||||
}
|
||||
|
||||
func IsEmailValid(email string) bool {
|
||||
@ -100,3 +103,21 @@ func GetCountryCode(prefix string, phone string) (string, error) {
|
||||
func FilterField(field string) bool {
|
||||
return ReFieldWhiteList.MatchString(field)
|
||||
}
|
||||
|
||||
func IsValidOrigin(origin string) (bool, error) {
|
||||
urlObj, err := url.Parse(origin)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if urlObj == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
originHostOnly := ""
|
||||
if urlObj.Host != "" {
|
||||
originHostOnly = fmt.Sprintf("%s://%s", urlObj.Scheme, urlObj.Hostname())
|
||||
}
|
||||
|
||||
res := originHostOnly == "http://localhost" || originHostOnly == "https://localhost" || originHostOnly == "http://127.0.0.1" || originHostOnly == "http://casdoor-app" || strings.HasSuffix(originHostOnly, ".chromiumapp.org")
|
||||
return res, nil
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
const CracoLessPlugin = require("craco-less");
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
devServer: {
|
||||
@ -55,47 +56,42 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
webpack: {
|
||||
configure: {
|
||||
// ignore webpack warnings by source-map-loader
|
||||
configure: (webpackConfig, { env, paths }) => {
|
||||
paths.appBuild = path.resolve(__dirname, "build-temp");
|
||||
webpackConfig.output.path = path.resolve(__dirname, "build-temp");
|
||||
|
||||
// ignore webpack warnings by source-map-loader
|
||||
// https://github.com/facebook/create-react-app/pull/11752#issuecomment-1345231546
|
||||
ignoreWarnings: [
|
||||
webpackConfig.ignoreWarnings = [
|
||||
function ignoreSourcemapsloaderWarnings(warning) {
|
||||
return (
|
||||
warning.module &&
|
||||
warning.module.resource.includes('node_modules') &&
|
||||
warning.module.resource.includes("node_modules") &&
|
||||
warning.details &&
|
||||
warning.details.includes('source-map-loader')
|
||||
)
|
||||
warning.details.includes("source-map-loader")
|
||||
);
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
// use polyfill Buffer with Webpack 5
|
||||
// https://viglucci.io/articles/how-to-polyfill-buffer-with-webpack-5
|
||||
// https://craco.js.org/docs/configuration/webpack/
|
||||
resolve: {
|
||||
fallback: {
|
||||
// "process": require.resolve('process/browser'),
|
||||
// "util": require.resolve("util/"),
|
||||
// "url": require.resolve("url/"),
|
||||
// "zlib": require.resolve("browserify-zlib"),
|
||||
// "stream": require.resolve("stream-browserify"),
|
||||
// "http": require.resolve("stream-http"),
|
||||
// "https": require.resolve("https-browserify"),
|
||||
// "assert": require.resolve("assert/"),
|
||||
"buffer": require.resolve('buffer/'),
|
||||
"process": false,
|
||||
"util": false,
|
||||
"url": false,
|
||||
"zlib": false,
|
||||
"stream": false,
|
||||
"http": false,
|
||||
"https": false,
|
||||
"assert": false,
|
||||
"buffer": false,
|
||||
"crypto": false,
|
||||
"os": false,
|
||||
"fs": false,
|
||||
},
|
||||
}
|
||||
webpackConfig.resolve.fallback = {
|
||||
buffer: require.resolve("buffer/"),
|
||||
process: false,
|
||||
util: false,
|
||||
url: false,
|
||||
zlib: false,
|
||||
stream: false,
|
||||
http: false,
|
||||
https: false,
|
||||
assert: false,
|
||||
crypto: false,
|
||||
os: false,
|
||||
fs: false,
|
||||
};
|
||||
|
||||
return webpackConfig;
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
21
web/mv.js
Normal file
21
web/mv.js
Normal file
@ -0,0 +1,21 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const sourceDir = path.join(__dirname, "build-temp");
|
||||
const targetDir = path.join(__dirname, "build");
|
||||
|
||||
if (!fs.existsSync(sourceDir)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Source directory "${sourceDir}" does not exist.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (fs.existsSync(targetDir)) {
|
||||
fs.rmSync(targetDir, {recursive: true, force: true});
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Target directory "${targetDir}" has been deleted successfully.`);
|
||||
}
|
||||
|
||||
fs.renameSync(sourceDir, targetDir);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Renamed "${sourceDir}" to "${targetDir}" successfully.`);
|
@ -57,6 +57,7 @@
|
||||
"scripts": {
|
||||
"start": "cross-env PORT=7001 craco start",
|
||||
"build": "craco build",
|
||||
"postbuild": "node mv.js",
|
||||
"test": "craco test",
|
||||
"eject": "craco eject",
|
||||
"crowdin:sync": "crowdin upload && crowdin download",
|
||||
|
@ -308,7 +308,7 @@ class App extends Component {
|
||||
AI Assistant
|
||||
</a>
|
||||
</Tooltip>
|
||||
<a className="custom-link" style={{float: "right", marginTop: "2px"}} target="_blank" rel="noreferrer" href={"https://ai.casbin.com"}>
|
||||
<a className="custom-link" style={{float: "right", marginTop: "2px"}} target="_blank" rel="noreferrer" href={`${Conf.AiAssistantUrl}`}>
|
||||
<ShareAltOutlined className="custom-link" style={{fontSize: "20px", color: "rgb(140,140,140)"}} />
|
||||
</a>
|
||||
<a className="custom-link" style={{float: "right", marginRight: "30px", marginTop: "2px"}} target="_blank" rel="noreferrer" href={"https://github.com/casibase/casibase"}>
|
||||
@ -326,7 +326,7 @@ class App extends Component {
|
||||
}}
|
||||
visible={this.state.isAiAssistantOpen}
|
||||
>
|
||||
<iframe id="iframeHelper" title={"iframeHelper"} src={"https://ai.casbin.com/?isRaw=1"} width="100%" height="100%" scrolling="no" frameBorder="no" />
|
||||
<iframe id="iframeHelper" title={"iframeHelper"} src={`${Conf.AiAssistantUrl}/?isRaw=1`} width="100%" height="100%" scrolling="no" frameBorder="no" />
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
@ -603,7 +603,7 @@ class ApplicationEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:IP whitelist"), i18next.t("general:IP whitelist - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input placeholder = {this.state.application.organizationObj?.ipWhitelist} value={this.state.application.ipWhiteList} onChange={e => {
|
||||
<Input placeholder = {this.state.application.organizationObj?.ipWhitelist} value={this.state.application.ipWhitelist} onChange={e => {
|
||||
this.updateApplicationField("ipWhitelist", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
@ -765,7 +765,7 @@ class ApplicationEditPage extends React.Component {
|
||||
/>
|
||||
<br />
|
||||
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
|
||||
copy(`${window.location.origin}/api/saml/metadata?application=admin/${encodeURIComponent(this.state.applicationName)}&post=${this.state.application.enableSamlPostBinding}`);
|
||||
copy(`${window.location.origin}/api/saml/metadata?application=admin/${encodeURIComponent(this.state.applicationName)}&enablePostBinding=${this.state.application.enableSamlPostBinding}`);
|
||||
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||
}}
|
||||
>
|
||||
|
@ -19,6 +19,7 @@ import "codemirror/mode/properties/properties";
|
||||
import * as Setting from "./Setting";
|
||||
import IframeEditor from "./IframeEditor";
|
||||
import {Tabs} from "antd";
|
||||
import i18next from "i18next";
|
||||
|
||||
const {TabPane} = Tabs;
|
||||
|
||||
@ -68,8 +69,8 @@ const CasbinEditor = ({model, onModelTextChange}) => {
|
||||
return (
|
||||
<div style={{height: "100%", width: "100%", display: "flex", flexDirection: "column"}}>
|
||||
<Tabs activeKey={activeKey} onChange={handleTabChange} style={{flex: "0 0 auto", marginTop: "-10px"}}>
|
||||
<TabPane tab="Basic Editor" key="basic" />
|
||||
<TabPane tab="Advanced Editor" key="advanced" />
|
||||
<TabPane tab={i18next.t("model:Basic Editor")} key="basic" />
|
||||
<TabPane tab={i18next.t("model:Advanced Editor")} key="advanced" />
|
||||
</Tabs>
|
||||
<div style={{flex: "1 1 auto", overflow: "hidden"}}>
|
||||
{activeKey === "advanced" ? (
|
||||
|
@ -31,3 +31,6 @@ export const ThemeDefault = {
|
||||
};
|
||||
|
||||
export const CustomFooter = null;
|
||||
|
||||
// Blank or null to hide Ai Assistant button
|
||||
export const AiAssistantUrl = "https://ai.casbin.com";
|
||||
|
@ -17,6 +17,7 @@ import React, {forwardRef, useEffect, useImperativeHandle, useRef, useState} fro
|
||||
const IframeEditor = forwardRef(({initialModelText, onModelTextChange}, ref) => {
|
||||
const iframeRef = useRef(null);
|
||||
const [iframeReady, setIframeReady] = useState(false);
|
||||
const currentLang = localStorage.getItem("language") || "en";
|
||||
|
||||
useEffect(() => {
|
||||
const handleMessage = (event) => {
|
||||
@ -26,24 +27,31 @@ const IframeEditor = forwardRef(({initialModelText, onModelTextChange}, ref) =>
|
||||
onModelTextChange(event.data.modelText);
|
||||
} else if (event.data.type === "iframeReady") {
|
||||
setIframeReady(true);
|
||||
iframeRef.current?.contentWindow.postMessage({
|
||||
type: "initializeModel",
|
||||
modelText: initialModelText,
|
||||
}, "*");
|
||||
if (initialModelText && iframeRef.current?.contentWindow) {
|
||||
iframeRef.current.contentWindow.postMessage({
|
||||
type: "initializeModel",
|
||||
modelText: initialModelText,
|
||||
lang: currentLang,
|
||||
}, "*");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("message", handleMessage);
|
||||
return () => window.removeEventListener("message", handleMessage);
|
||||
}, [onModelTextChange, initialModelText]);
|
||||
}, [onModelTextChange, initialModelText, currentLang]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getModelText: () => {
|
||||
iframeRef.current?.contentWindow.postMessage({type: "getModelText"}, "*");
|
||||
if (iframeRef.current?.contentWindow) {
|
||||
iframeRef.current.contentWindow.postMessage({
|
||||
type: "getModelText",
|
||||
}, "*");
|
||||
}
|
||||
},
|
||||
updateModelText: (newModelText) => {
|
||||
if (iframeReady) {
|
||||
iframeRef.current?.contentWindow.postMessage({
|
||||
if (iframeReady && iframeRef.current?.contentWindow) {
|
||||
iframeRef.current.contentWindow.postMessage({
|
||||
type: "updateModelText",
|
||||
modelText: newModelText,
|
||||
}, "*");
|
||||
@ -54,7 +62,7 @@ const IframeEditor = forwardRef(({initialModelText, onModelTextChange}, ref) =>
|
||||
return (
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
src="https://editor.casbin.org/model-editor"
|
||||
src={`https://editor.casbin.org/model-editor?lang=${currentLang}`}
|
||||
frameBorder="0"
|
||||
width="100%"
|
||||
height="500px"
|
||||
|
@ -192,17 +192,21 @@ function ManagementPage(props) {
|
||||
themeAlgorithm={props.themeAlgorithm}
|
||||
onChange={props.setLogoAndThemeAlgorithm} />
|
||||
<LanguageSelect languages={props.account.organization.languages} />
|
||||
<Tooltip title="Click to open AI assitant">
|
||||
<div className="select-box" onClick={props.openAiAssistant}>
|
||||
<DeploymentUnitOutlined style={{fontSize: "24px"}} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
{
|
||||
Conf.AiAssistantUrl?.trim() && (
|
||||
<Tooltip title="Click to open AI assistant">
|
||||
<div className="select-box" onClick={props.openAiAssistant}>
|
||||
<DeploymentUnitOutlined style={{fontSize: "24px"}} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
<OpenTour />
|
||||
{Setting.isAdminUser(props.account) && !Setting.isMobile() && (props.uri.indexOf("/trees") === -1) &&
|
||||
{Setting.isAdminUser(props.account) && (props.uri.indexOf("/trees") === -1) &&
|
||||
<OrganizationSelect
|
||||
initValue={Setting.getOrganization()}
|
||||
withAll={true}
|
||||
style={{marginRight: "20px", width: "180px", display: "flex"}}
|
||||
style={{marginRight: "20px", width: "180px", display: !Setting.isMobile() ? "flex" : "none"}}
|
||||
onChange={(value) => {
|
||||
Setting.setOrganization(value);
|
||||
}}
|
||||
|
@ -339,6 +339,16 @@ class OrganizationEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>)
|
||||
}
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||
{Setting.getLabel(i18next.t("organization:Password expire days"), i18next.t("organization:Password expire days - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={4} >
|
||||
<InputNumber value={this.state.organization.passwordExpireDays} onChange={value => {
|
||||
this.updateOrganizationField("passwordExpireDays", value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Supported country codes"), i18next.t("general:Supported country codes - Tooltip"))} :
|
||||
|
@ -37,6 +37,7 @@ class OrganizationListPage extends BaseListPage {
|
||||
passwordOptions: [],
|
||||
passwordObfuscatorType: "Plain",
|
||||
passwordObfuscatorKey: "",
|
||||
passwordExpireDays: 0,
|
||||
countryCodes: ["US"],
|
||||
defaultAvatar: `${Setting.StaticBaseUrl}/img/casbin.svg`,
|
||||
defaultApplication: "",
|
||||
|
@ -123,6 +123,22 @@ class ProductBuyPage extends React.Component {
|
||||
return "$";
|
||||
} else if (product?.currency === "CNY") {
|
||||
return "¥";
|
||||
} else if (product?.currency === "EUR") {
|
||||
return "€";
|
||||
} else if (product?.currency === "JPY") {
|
||||
return "¥";
|
||||
} else if (product?.currency === "GBP") {
|
||||
return "£";
|
||||
} else if (product?.currency === "AUD") {
|
||||
return "A$";
|
||||
} else if (product?.currency === "CAD") {
|
||||
return "C$";
|
||||
} else if (product?.currency === "CHF") {
|
||||
return "CHF";
|
||||
} else if (product?.currency === "HKD") {
|
||||
return "HK$";
|
||||
} else if (product?.currency === "SGD") {
|
||||
return "S$";
|
||||
} else {
|
||||
return "(Unknown currency)";
|
||||
}
|
||||
|
@ -209,6 +209,14 @@ class ProductEditPage extends React.Component {
|
||||
[
|
||||
{id: "USD", name: "USD"},
|
||||
{id: "CNY", name: "CNY"},
|
||||
{id: "EUR", name: "EUR"},
|
||||
{id: "JPY", name: "JPY"},
|
||||
{id: "GBP", name: "GBP"},
|
||||
{id: "AUD", name: "AUD"},
|
||||
{id: "CAD", name: "CAD"},
|
||||
{id: "CHF", name: "CHF"},
|
||||
{id: "HKD", name: "HKD"},
|
||||
{id: "SGD", name: "SGD"},
|
||||
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
|
@ -633,6 +633,20 @@ class ProviderEditPage extends React.Component {
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
{
|
||||
this.state.provider.category === "OAuth" ? (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Email regex"), i18next.t("provider:Email regex - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
<TextArea rows={4} value={this.state.provider.emailRegex} onChange={e => {
|
||||
this.updateProviderField("emailRegex", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
) : null
|
||||
}
|
||||
{
|
||||
this.state.provider.type === "Custom" ? (
|
||||
<React.Fragment>
|
||||
@ -908,7 +922,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{["Custom HTTP SMS", "Qiniu Cloud Kodo", "Synology", "Casdoor"].includes(this.state.provider.type) ? null : (
|
||||
{["Custom HTTP SMS", "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"))} :
|
||||
@ -932,7 +946,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
) : null}
|
||||
{["AWS S3", "Tencent Cloud COS", "Qiniu Cloud Kodo", "Casdoor"].includes(this.state.provider.type) ? (
|
||||
{["AWS S3", "Tencent Cloud COS", "Qiniu Cloud Kodo", "Casdoor", "CUCloud OSS", "MinIO"].includes(this.state.provider.type) ? (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{["Casdoor"].includes(this.state.provider.type) ?
|
||||
|
@ -187,7 +187,7 @@ class RoleEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("role:Sub users"), i18next.t("role:Sub users - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.role.users}
|
||||
<Select virtual={true} mode="multiple" style={{width: "100%"}} value={this.state.role.users}
|
||||
onChange={(value => {this.updateRoleField("users", value);})}
|
||||
options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))}
|
||||
/>
|
||||
|
@ -233,6 +233,10 @@ export const OtherProviderInfo = {
|
||||
logo: `${StaticBaseUrl}/img/casdoor.png`,
|
||||
url: "https://casdoor.org/docs/provider/storage/overview",
|
||||
},
|
||||
"CUCloud OSS": {
|
||||
logo: `${StaticBaseUrl}/img/social_cucloud.png`,
|
||||
url: "https://www.cucloud.cn/product/oss.html",
|
||||
},
|
||||
},
|
||||
SAML: {
|
||||
"Aliyun IDaaS": {
|
||||
@ -920,7 +924,7 @@ export function getClickable(text) {
|
||||
return (
|
||||
<a onClick={() => {
|
||||
copy(text);
|
||||
showMessage("success", "Copied to clipboard");
|
||||
showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||
}}>
|
||||
{text}
|
||||
</a>
|
||||
@ -981,6 +985,7 @@ export function getProviderTypeOptions(category) {
|
||||
{id: "Bilibili", name: "Bilibili"},
|
||||
{id: "Okta", name: "Okta"},
|
||||
{id: "Douyin", name: "Douyin"},
|
||||
{id: "Kwai", name: "Kwai"},
|
||||
{id: "Line", name: "Line"},
|
||||
{id: "Amazon", name: "Amazon"},
|
||||
{id: "Auth0", name: "Auth0"},
|
||||
@ -1078,6 +1083,7 @@ export function getProviderTypeOptions(category) {
|
||||
{id: "Google Cloud Storage", name: "Google Cloud Storage"},
|
||||
{id: "Synology", name: "Synology"},
|
||||
{id: "Casdoor", name: "Casdoor"},
|
||||
{id: "CUCloud OSS", name: "CUCloud OSS"},
|
||||
]
|
||||
);
|
||||
} else if (category === "SAML") {
|
||||
@ -1171,7 +1177,7 @@ export function renderLogo(application) {
|
||||
|
||||
function isSigninMethodEnabled(application, signinMethod) {
|
||||
if (application && application.signinMethods) {
|
||||
return application.signinMethods.filter(item => item.name === signinMethod && item.rule !== "Hide-Password").length > 0;
|
||||
return application.signinMethods.filter(item => item.name === signinMethod && item.rule !== "Hide password").length > 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -1550,9 +1556,25 @@ export function getDefaultHtmlEmailContent() {
|
||||
|
||||
export function getCurrencyText(product) {
|
||||
if (product?.currency === "USD") {
|
||||
return i18next.t("product:USD");
|
||||
return i18next.t("currency:USD");
|
||||
} else if (product?.currency === "CNY") {
|
||||
return i18next.t("product:CNY");
|
||||
return i18next.t("currency:CNY");
|
||||
} else if (product?.currency === "EUR") {
|
||||
return i18next.t("currency:EUR");
|
||||
} else if (product?.currency === "JPY") {
|
||||
return i18next.t("currency:JPY");
|
||||
} else if (product?.currency === "GBP") {
|
||||
return i18next.t("currency:GBP");
|
||||
} else if (product?.currency === "AUD") {
|
||||
return i18next.t("currency:AUD");
|
||||
} else if (product?.currency === "CAD") {
|
||||
return i18next.t("currency:CAD");
|
||||
} else if (product?.currency === "CHF") {
|
||||
return i18next.t("currency:CHF");
|
||||
} else if (product?.currency === "HKD") {
|
||||
return i18next.t("currency:HKD");
|
||||
} else if (product?.currency === "SGD") {
|
||||
return i18next.t("currency:SGD");
|
||||
} else {
|
||||
return "(Unknown currency)";
|
||||
}
|
||||
|
@ -1009,6 +1009,19 @@ class UserEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
} else if (accountItem.name === "Last change password time") {
|
||||
return (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("user:Last change password time"), i18next.t("user:Last change password time"))} :
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
<Input value={this.state.user.lastChangePasswordTime} onChange={e => {
|
||||
this.updateUserField("lastChangePasswordTime", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
} else if (accountItem.name === "Managed accounts") {
|
||||
return (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
|
31
web/src/auth/KwaiLoginButton.js
Normal file
31
web/src/auth/KwaiLoginButton.js
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2024 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}) {
|
||||
return <img src={`${StaticBaseUrl}/buttons/kwai.svg`} alt="Sign in with Kwai" style={{width: width, height: height}} />;
|
||||
}
|
||||
|
||||
const config = {
|
||||
text: "Sign in with Kwai",
|
||||
icon: Icon,
|
||||
style: {background: "#ffffff", color: "#000000"},
|
||||
activeStyle: {background: "#ededee"},
|
||||
};
|
||||
|
||||
const KwaiLoginButton = createButton(config);
|
||||
|
||||
export default KwaiLoginButton;
|
@ -227,7 +227,26 @@ class LoginPage extends React.Component {
|
||||
return "password";
|
||||
}
|
||||
|
||||
getPlaceholder() {
|
||||
getCurrentLoginMethod() {
|
||||
if (this.state.loginMethod === "password") {
|
||||
return "Password";
|
||||
} else if (this.state.loginMethod?.includes("verificationCode")) {
|
||||
return "Verification code";
|
||||
} else if (this.state.loginMethod === "webAuthn") {
|
||||
return "WebAuthn";
|
||||
} else if (this.state.loginMethod === "ldap") {
|
||||
return "LDAP";
|
||||
} else if (this.state.loginMethod === "faceId") {
|
||||
return "Face ID";
|
||||
} else {
|
||||
return "Password";
|
||||
}
|
||||
}
|
||||
|
||||
getPlaceholder(defaultPlaceholder = null) {
|
||||
if (defaultPlaceholder) {
|
||||
return defaultPlaceholder;
|
||||
}
|
||||
switch (this.state.loginMethod) {
|
||||
case "verificationCode": return i18next.t("login:Email or phone");
|
||||
case "verificationCodeEmail": return i18next.t("login:Email");
|
||||
@ -262,17 +281,7 @@ class LoginPage extends React.Component {
|
||||
values["organization"] = this.getApplicationObj().organization;
|
||||
}
|
||||
|
||||
if (this.state.loginMethod === "password") {
|
||||
values["signinMethod"] = "Password";
|
||||
} else if (this.state.loginMethod?.includes("verificationCode")) {
|
||||
values["signinMethod"] = "Verification code";
|
||||
} else if (this.state.loginMethod === "webAuthn") {
|
||||
values["signinMethod"] = "WebAuthn";
|
||||
} else if (this.state.loginMethod === "ldap") {
|
||||
values["signinMethod"] = "LDAP";
|
||||
} else if (this.state.loginMethod === "faceId") {
|
||||
values["signinMethod"] = "Face ID";
|
||||
}
|
||||
values["signinMethod"] = this.getCurrentLoginMethod();
|
||||
const oAuthParams = Util.getOAuthGetParameters();
|
||||
|
||||
values["type"] = oAuthParams?.responseType ?? this.state.type;
|
||||
@ -409,6 +418,7 @@ class LoginPage extends React.Component {
|
||||
if (this.state.type === "cas") {
|
||||
// CAS
|
||||
const casParams = Util.getCasParameters();
|
||||
values["signinMethod"] = this.getCurrentLoginMethod();
|
||||
values["type"] = this.state.type;
|
||||
AuthBackend.loginCas(values, casParams).then((res) => {
|
||||
const loginHandler = (res) => {
|
||||
@ -437,8 +447,8 @@ class LoginPage extends React.Component {
|
||||
formValues={values}
|
||||
authParams={casParams}
|
||||
application={this.getApplicationObj()}
|
||||
onFail={() => {
|
||||
Setting.showMessage("error", i18next.t("mfa:Verification failed"));
|
||||
onFail={(errorMessage) => {
|
||||
Setting.showMessage("error", errorMessage);
|
||||
}}
|
||||
onSuccess={(res) => loginHandler(res)}
|
||||
/>);
|
||||
@ -478,6 +488,10 @@ class LoginPage extends React.Component {
|
||||
const accessToken = res.data;
|
||||
Setting.goToLink(`${oAuthParams.redirectUri}#${amendatoryResponseType}=${accessToken}&state=${oAuthParams.state}&token_type=bearer`);
|
||||
} else if (responseType === "saml") {
|
||||
if (res.data === RequiredMfa) {
|
||||
this.props.onLoginSuccess(window.location.href);
|
||||
return;
|
||||
}
|
||||
if (res.data2.needUpdatePassword) {
|
||||
sessionStorage.setItem("signinUrl", window.location.href);
|
||||
Setting.goToLink(this, `/forget/${this.state.applicationName}`);
|
||||
@ -506,8 +520,8 @@ class LoginPage extends React.Component {
|
||||
formValues={values}
|
||||
authParams={oAuthParams}
|
||||
application={this.getApplicationObj()}
|
||||
onFail={() => {
|
||||
Setting.showMessage("error", i18next.t("mfa:Verification failed"));
|
||||
onFail={(errorMessage) => {
|
||||
Setting.showMessage("error", errorMessage);
|
||||
}}
|
||||
onSuccess={(res) => loginHandler(res)}
|
||||
/>);
|
||||
@ -672,7 +686,7 @@ class LoginPage extends React.Component {
|
||||
id="input"
|
||||
className="login-username-input"
|
||||
prefix={<UserOutlined className="site-form-item-icon" />}
|
||||
placeholder={this.getPlaceholder()}
|
||||
placeholder={this.getPlaceholder(signinItem.placeholder)}
|
||||
onChange={e => {
|
||||
this.setState({
|
||||
username: e.target.value,
|
||||
@ -1086,7 +1100,7 @@ class LoginPage extends React.Component {
|
||||
className="login-password-input"
|
||||
prefix={<LockOutlined className="site-form-item-icon" />}
|
||||
type="password"
|
||||
placeholder={i18next.t("general:Password")}
|
||||
placeholder={signinItem.placeholder ? signinItem.placeholder : i18next.t("general:Password")}
|
||||
disabled={this.state.loginMethod === "password" ? !Setting.isPasswordEnabled(application) : !Setting.isLdapEnabled(application)}
|
||||
/>
|
||||
</Form.Item>
|
||||
@ -1136,7 +1150,7 @@ class LoginPage extends React.Component {
|
||||
]);
|
||||
|
||||
application?.signinMethods?.forEach((signinMethod) => {
|
||||
if (signinMethod.rule === "Hide-Password") {
|
||||
if (signinMethod.rule === "Hide password") {
|
||||
return;
|
||||
}
|
||||
const item = itemsMap.get(generateItemKey(signinMethod.name, signinMethod.rule));
|
||||
|
@ -37,7 +37,7 @@ class MfaSetupPage extends React.Component {
|
||||
this.state = {
|
||||
account: props.account,
|
||||
application: null,
|
||||
applicationName: props.account.signupApplication ?? "",
|
||||
applicationName: props.account.signupApplication ?? localStorage.getItem("applicationName") ?? "",
|
||||
current: location.state?.from !== undefined ? 1 : 0,
|
||||
mfaProps: null,
|
||||
mfaType: params.get("mfaType") ?? SmsMfaType,
|
||||
@ -179,8 +179,10 @@ class MfaSetupPage extends React.Component {
|
||||
mfaProps={this.state.mfaProps}
|
||||
application={this.state.application}
|
||||
user={this.props.account}
|
||||
onSuccess={() => {
|
||||
onSuccess={(res) => {
|
||||
this.setState({
|
||||
dest: res.dest,
|
||||
countryCode: res.countryCode,
|
||||
current: this.state.current + 1,
|
||||
});
|
||||
}}
|
||||
@ -195,7 +197,7 @@ class MfaSetupPage extends React.Component {
|
||||
);
|
||||
case 2:
|
||||
return (
|
||||
<MfaEnableForm user={this.getUser()} mfaType={this.state.mfaType} recoveryCodes={this.state.mfaProps.recoveryCodes}
|
||||
<MfaEnableForm user={this.getUser()} mfaType={this.state.mfaType} secret={this.state.mfaProps.secret} recoveryCodes={this.state.mfaProps.recoveryCodes} dest={this.state.dest} countryCode={this.state.countryCode}
|
||||
onSuccess={() => {
|
||||
Setting.showMessage("success", i18next.t("general:Enabled successfully"));
|
||||
this.props.onfinish();
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
import CryptoJS from "crypto-js";
|
||||
import i18next from "i18next";
|
||||
import {Buffer} from "buffer";
|
||||
|
||||
export function getRandomKeyForObfuscator(obfuscatorType) {
|
||||
if (obfuscatorType === "DES") {
|
||||
@ -45,17 +45,17 @@ function encrypt(cipher, key, iv, password) {
|
||||
|
||||
export function checkPasswordObfuscator(passwordObfuscatorType, passwordObfuscatorKey) {
|
||||
if (passwordObfuscatorType === undefined) {
|
||||
return i18next.t("organization:failed to get password obfuscator");
|
||||
return "passwordObfuscatorType should not be undefined";
|
||||
} else if (passwordObfuscatorType === "Plain" || passwordObfuscatorType === "") {
|
||||
return "";
|
||||
} else if (passwordObfuscatorType === "AES" || passwordObfuscatorType === "DES") {
|
||||
if (passwordObfuscatorKeyRegexes[passwordObfuscatorType].test(passwordObfuscatorKey)) {
|
||||
return "";
|
||||
} else {
|
||||
return `${i18next.t("organization:The password obfuscator key doesn't match the regex")}: ${passwordObfuscatorKeyRegexes[passwordObfuscatorType].source}`;
|
||||
return `The password obfuscator key doesn't match the regex: ${passwordObfuscatorKeyRegexes[passwordObfuscatorType].source}`;
|
||||
}
|
||||
} else {
|
||||
return `${i18next.t("organization:unsupported password obfuscator type")}: ${passwordObfuscatorType}`;
|
||||
return `unsupported password obfuscator type: ${passwordObfuscatorType}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,6 +119,10 @@ const authInfo = {
|
||||
scope: "user_info",
|
||||
endpoint: "https://open.douyin.com/platform/oauth/connect",
|
||||
},
|
||||
Kwai: {
|
||||
scope: "user_info",
|
||||
endpoint: "https://open.kuaishou.com/oauth2/connect",
|
||||
},
|
||||
Custom: {
|
||||
endpoint: "https://example.com/",
|
||||
},
|
||||
@ -470,6 +474,8 @@ export function getAuthUrl(application, provider, method, code) {
|
||||
return `${provider.domain}/v1/authorize?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}`;
|
||||
} 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 === "Kwai") {
|
||||
return `${endpoint}?app_id=${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.scopes}&response_type=code&state=${state}`;
|
||||
} else if (provider.type === "Bilibili") {
|
||||
|
@ -40,6 +40,7 @@ import SteamLoginButton from "./SteamLoginButton";
|
||||
import BilibiliLoginButton from "./BilibiliLoginButton";
|
||||
import OktaLoginButton from "./OktaLoginButton";
|
||||
import DouyinLoginButton from "./DouyinLoginButton";
|
||||
import KwaiLoginButton from "./KwaiLoginButton";
|
||||
import LoginButton from "./LoginButton";
|
||||
import * as AuthBackend from "./AuthBackend";
|
||||
import {WechatOfficialAccountModal} from "./Util";
|
||||
@ -96,6 +97,8 @@ function getSigninButton(provider) {
|
||||
return <OktaLoginButton text={text} align={"center"} />;
|
||||
} else if (provider.type === "Douyin") {
|
||||
return <DouyinLoginButton text={text} align={"center"} />;
|
||||
} else if (provider.type === "Kwai") {
|
||||
return <KwaiLoginButton text={text} align={"center"} />;
|
||||
} else {
|
||||
return <LoginButton key={provider.type} type={provider.type} logoUrl={getProviderLogoURL(provider)} />;
|
||||
}
|
||||
|
@ -113,6 +113,9 @@ export function getCasLoginParameters(owner, name) {
|
||||
|
||||
export function getOAuthGetParameters(params) {
|
||||
const queries = (params !== undefined) ? params : new URLSearchParams(window.location.search);
|
||||
const lowercaseQueries = {};
|
||||
queries.forEach((val, key) => {lowercaseQueries[key.toLowerCase()] = val;});
|
||||
|
||||
const clientId = getRefinedValue(queries.get("client_id"));
|
||||
const responseType = getRefinedValue(queries.get("response_type"));
|
||||
|
||||
@ -138,9 +141,9 @@ export function getOAuthGetParameters(params) {
|
||||
const nonce = getRefinedValue(queries.get("nonce"));
|
||||
const challengeMethod = getRefinedValue(queries.get("code_challenge_method"));
|
||||
const codeChallenge = getRefinedValue(queries.get("code_challenge"));
|
||||
const samlRequest = getRefinedValue(queries.get("SAMLRequest"));
|
||||
const relayState = getRefinedValue(queries.get("RelayState"));
|
||||
const noRedirect = getRefinedValue(queries.get("noRedirect"));
|
||||
const samlRequest = getRefinedValue(lowercaseQueries["samlRequest".toLowerCase()]);
|
||||
const relayState = getRefinedValue(lowercaseQueries["RelayState".toLowerCase()]);
|
||||
const noRedirect = getRefinedValue(lowercaseQueries["noRedirect".toLowerCase()]);
|
||||
|
||||
if (clientId === "" && samlRequest === "") {
|
||||
// login
|
||||
|
@ -3,11 +3,15 @@ import i18next from "i18next";
|
||||
import React, {useState} from "react";
|
||||
import * as MfaBackend from "../../backend/MfaBackend";
|
||||
|
||||
export function MfaEnableForm({user, mfaType, recoveryCodes, onSuccess, onFail}) {
|
||||
export function MfaEnableForm({user, mfaType, secret, recoveryCodes, dest, countryCode, onSuccess, onFail}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const requestEnableMfa = () => {
|
||||
const data = {
|
||||
mfaType,
|
||||
secret,
|
||||
recoveryCodes,
|
||||
dest,
|
||||
countryCode,
|
||||
...user,
|
||||
};
|
||||
setLoading(true);
|
||||
|
@ -26,11 +26,13 @@ export const mfaSetup = "mfaSetup";
|
||||
|
||||
export function MfaVerifyForm({mfaProps, application, user, onSuccess, onFail}) {
|
||||
const [form] = Form.useForm();
|
||||
const onFinish = ({passcode}) => {
|
||||
const data = {passcode, mfaType: mfaProps.mfaType, ...user};
|
||||
const onFinish = ({passcode, countryCode, dest}) => {
|
||||
const data = {passcode, mfaType: mfaProps.mfaType, secret: mfaProps.secret, dest: dest, countryCode: countryCode, ...user};
|
||||
MfaBackend.MfaSetupVerify(data)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
res.dest = dest;
|
||||
res.countryCode = countryCode;
|
||||
onSuccess(res);
|
||||
} else {
|
||||
onFail(res);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {UserOutlined} from "@ant-design/icons";
|
||||
import {Button, Form, Input} from "antd";
|
||||
import {Button, Form, Input, Space} from "antd";
|
||||
import i18next from "i18next";
|
||||
import React, {useEffect} from "react";
|
||||
import {CountryCodeSelect} from "../../common/select/CountryCodeSelect";
|
||||
@ -15,15 +15,18 @@ export const MfaVerifySmsForm = ({mfaProps, application, onFinish, method, user}
|
||||
useEffect(() => {
|
||||
if (method === mfaAuth) {
|
||||
setDest(mfaProps.secret);
|
||||
form.setFieldValue("dest", mfaProps.secret);
|
||||
return;
|
||||
}
|
||||
if (mfaProps.mfaType === SmsMfaType) {
|
||||
setDest(user.phone);
|
||||
form.setFieldValue("dest", user.phone);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mfaProps.mfaType === EmailMfaType) {
|
||||
setDest(user.email);
|
||||
form.setFieldValue("dest", user.email);
|
||||
}
|
||||
}, [mfaProps.mfaType]);
|
||||
|
||||
@ -57,45 +60,44 @@ export const MfaVerifySmsForm = ({mfaProps, application, onFinish, method, user}
|
||||
<div style={{marginBottom: 20, textAlign: "left", gap: 8}}>
|
||||
{isEmail() ? i18next.t("mfa:Your email is") : i18next.t("mfa:Your phone is")} {dest}
|
||||
</div> :
|
||||
(<React.Fragment>
|
||||
(
|
||||
<p>{isEmail() ? i18next.t("mfa:Please bind your email first, the system will automatically uses the mail for multi-factor authentication") :
|
||||
i18next.t("mfa:Please bind your phone first, the system automatically uses the phone for multi-factor authentication")}
|
||||
</p>
|
||||
<Input.Group compact style={{width: "300Px", marginBottom: "30px"}}>
|
||||
{isEmail() ? null :
|
||||
<Form.Item
|
||||
name="countryCode"
|
||||
noStyle
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: i18next.t("signup:Please select your country code!"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<CountryCodeSelect
|
||||
initValue={mfaProps.countryCode}
|
||||
style={{width: "30%"}}
|
||||
countryCodes={application.organizationObj.countryCodes}
|
||||
/>
|
||||
</Form.Item>
|
||||
}
|
||||
<Form.Item
|
||||
name="dest"
|
||||
noStyle
|
||||
rules={[{required: true, message: i18next.t("login:Please input your Email or Phone!")}]}
|
||||
>
|
||||
<Input
|
||||
style={{width: isEmail() ? "100% " : "70%"}}
|
||||
onChange={(e) => {setDest(e.target.value);}}
|
||||
prefix={<UserOutlined />}
|
||||
placeholder={isEmail() ? i18next.t("general:Email") : i18next.t("general:Phone")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Input.Group>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
<Space.Compact style={{width: "300Px", marginBottom: "30px", display: isShowText() ? "none" : ""}}>
|
||||
{isEmail() || isShowText() ? null :
|
||||
<Form.Item
|
||||
name="countryCode"
|
||||
noStyle
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: i18next.t("signup:Please select your country code!"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<CountryCodeSelect
|
||||
initValue={mfaProps.countryCode}
|
||||
style={{width: "30%"}}
|
||||
countryCodes={application.organizationObj.countryCodes}
|
||||
/>
|
||||
</Form.Item>
|
||||
}
|
||||
<Form.Item
|
||||
name="dest"
|
||||
noStyle
|
||||
rules={[{required: true, message: i18next.t("login:Please input your Email or Phone!")}]}
|
||||
>
|
||||
<Input
|
||||
style={{width: isEmail() ? "100% " : "70%"}}
|
||||
onChange={(e) => {setDest(e.target.value);}}
|
||||
prefix={<UserOutlined />}
|
||||
placeholder={isEmail() ? i18next.t("general:Email") : i18next.t("general:Phone")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Space.Compact>
|
||||
<Form.Item
|
||||
name="passcode"
|
||||
rules={[{required: true, message: i18next.t("login:Please input your code!")}]}
|
||||
|
@ -32,6 +32,9 @@ export function MfaSetupVerify(values) {
|
||||
formData.append("name", values.name);
|
||||
formData.append("mfaType", values.mfaType);
|
||||
formData.append("passcode", values.passcode);
|
||||
formData.append("secret", values.secret);
|
||||
formData.append("dest", values.dest);
|
||||
formData.append("countryCode", values.countryCode);
|
||||
return fetch(`${Setting.ServerUrl}/api/mfa/setup/verify`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
@ -44,6 +47,10 @@ export function MfaSetupEnable(values) {
|
||||
formData.append("mfaType", values.mfaType);
|
||||
formData.append("owner", values.owner);
|
||||
formData.append("name", values.name);
|
||||
formData.append("secret", values.secret);
|
||||
formData.append("recoveryCodes", values.recoveryCodes);
|
||||
formData.append("dest", values.dest);
|
||||
formData.append("countryCode", values.countryCode);
|
||||
return fetch(`${Setting.ServerUrl}/api/mfa/setup/enable`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
|
@ -135,6 +135,15 @@ const Dashboard = (props) => {
|
||||
i18next.t("general:Applications"),
|
||||
i18next.t("general:Organizations"),
|
||||
i18next.t("general:Subscriptions"),
|
||||
i18next.t("general:Roles"),
|
||||
i18next.t("general:Groups"),
|
||||
i18next.t("general:Resources"),
|
||||
i18next.t("general:Certs"),
|
||||
i18next.t("general:Permissions"),
|
||||
i18next.t("general:Transactions"),
|
||||
i18next.t("general:Models"),
|
||||
i18next.t("general:Adapters"),
|
||||
i18next.t("general:Enforcers"),
|
||||
], top: "10%"},
|
||||
grid: {left: "3%", right: "4%", bottom: "0", top: "25%", containLabel: true},
|
||||
xAxis: {type: "category", boundaryGap: false, data: dateArray},
|
||||
@ -145,6 +154,15 @@ const Dashboard = (props) => {
|
||||
{name: i18next.t("general:Providers"), type: "line", data: dashboardData.providerCounts},
|
||||
{name: i18next.t("general:Applications"), type: "line", data: dashboardData.applicationCounts},
|
||||
{name: i18next.t("general:Subscriptions"), type: "line", data: dashboardData.subscriptionCounts},
|
||||
{name: i18next.t("general:Roles"), type: "line", data: dashboardData.roleCounts},
|
||||
{name: i18next.t("general:Groups"), type: "line", data: dashboardData.groupCounts},
|
||||
{name: i18next.t("general:Resources"), type: "line", data: dashboardData.resourceCounts},
|
||||
{name: i18next.t("general:Certs"), type: "line", data: dashboardData.certCounts},
|
||||
{name: i18next.t("general:Permissions"), type: "line", data: dashboardData.permissionCounts},
|
||||
{name: i18next.t("general:Transactions"), type: "line", data: dashboardData.transactionCounts},
|
||||
{name: i18next.t("general:Models"), type: "line", data: dashboardData.modelCounts},
|
||||
{name: i18next.t("general:Adapters"), type: "line", data: dashboardData.adapterCounts},
|
||||
{name: i18next.t("general:Enforcers"), type: "line", data: dashboardData.enforcerCounts},
|
||||
],
|
||||
};
|
||||
myChart.setOption(option);
|
||||
|
113
web/src/common/CasdoorAppConnector.js
Normal file
113
web/src/common/CasdoorAppConnector.js
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2024 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 React from "react";
|
||||
import {Alert, Button, QRCode} from "antd";
|
||||
import copy from "copy-to-clipboard";
|
||||
import * as Setting from "../Setting";
|
||||
import i18next from "i18next";
|
||||
|
||||
export const generateCasdoorAppUrl = (accessToken, forQrCode = true) => {
|
||||
let qrUrl = "";
|
||||
let error = null;
|
||||
|
||||
if (!accessToken) {
|
||||
error = i18next.t("general:Access token is empty");
|
||||
return {qrUrl, error};
|
||||
}
|
||||
|
||||
qrUrl = `casdoor-app://login?serverUrl=${window.location.origin}&accessToken=${accessToken}`;
|
||||
|
||||
if (forQrCode && qrUrl.length >= 2000) {
|
||||
qrUrl = "";
|
||||
error = i18next.t("general:QR code is too large");
|
||||
}
|
||||
|
||||
return {qrUrl, error};
|
||||
};
|
||||
|
||||
export const CasdoorAppQrCode = ({accessToken, icon}) => {
|
||||
const {qrUrl, error} = generateCasdoorAppUrl(accessToken, true);
|
||||
|
||||
if (error) {
|
||||
return <Alert message={error} type="error" showIcon />;
|
||||
}
|
||||
|
||||
return (
|
||||
<QRCode
|
||||
value={qrUrl}
|
||||
icon={icon}
|
||||
errorLevel="M"
|
||||
size={230}
|
||||
bordered={false}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const CasdoorAppUrl = ({accessToken}) => {
|
||||
const {qrUrl, error} = generateCasdoorAppUrl(accessToken, false);
|
||||
|
||||
const handleCopyUrl = async() => {
|
||||
if (!window.isSecureContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy(qrUrl);
|
||||
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||
};
|
||||
|
||||
if (error) {
|
||||
return <Alert message={error} type="error" showIcon />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: "10px",
|
||||
}}>
|
||||
{window.isSecureContext && (
|
||||
<Button size="small" type="primary" onClick={handleCopyUrl} style={{marginLeft: "10px"}}>
|
||||
{i18next.t("resource:Copy Link")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
padding: "10px",
|
||||
maxWidth: "400px",
|
||||
maxHeight: "100px",
|
||||
overflow: "auto",
|
||||
wordBreak: "break-all",
|
||||
whiteSpace: "pre-wrap",
|
||||
cursor: "pointer",
|
||||
userSelect: "all",
|
||||
backgroundColor: "#f5f5f5",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
onClick={(e) => {
|
||||
const selection = window.getSelection();
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(e.target);
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
}}
|
||||
>
|
||||
{qrUrl}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Go to writable demo site?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Home",
|
||||
"Home - Tooltip": "Home page of the application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unique random string",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Is enabled",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Make sure the password is correct",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Different combinations of password complexity options",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Password salt",
|
||||
"Password salt - Tooltip": "Random parameter used for password encryption",
|
||||
"Password type": "Password type",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "Edit Model",
|
||||
"Model text": "Model text",
|
||||
"Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modify rule",
|
||||
"New Organization": "New Organization",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Soft deletion",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Test buy page..",
|
||||
"There is no payment channel for this product.": "There is no payment channel for this product.",
|
||||
"This product is currently not in sale.": "This product is currently not in sale.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Signup HTML - Edit",
|
||||
"Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "Site key",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Keys",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Location",
|
||||
"Location - Tooltip": "City of residence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Odesílání",
|
||||
"Submit and complete": "Odeslat a dokončit"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Upravit Enforcer",
|
||||
"New Enforcer": "Nový Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Přejít na zapisovatelnou demo stránku?",
|
||||
"Groups": "Skupiny",
|
||||
"Groups - Tooltip": "Skupiny - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Domů",
|
||||
"Home - Tooltip": "Domovská stránka aplikace",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unikátní náhodný řetězec",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identita",
|
||||
"Invitations": "Pozvánky",
|
||||
"Is enabled": "Je povoleno",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Ujistěte se, že heslo je správné",
|
||||
"Password complexity options": "Možnosti složitosti hesla",
|
||||
"Password complexity options - Tooltip": "Různé kombinace možností složitosti hesla",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Heslová sůl",
|
||||
"Password salt - Tooltip": "Náhodný parametr použitý pro šifrování hesla",
|
||||
"Password type": "Typ hesla",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Použít SMS",
|
||||
"Use SMS verification code": "Použít ověřovací kód SMS",
|
||||
"Use a recovery code": "Použít obnovovací kód",
|
||||
"Verification failed": "Ověření selhalo",
|
||||
"Verify Code": "Ověřit kód",
|
||||
"Verify Password": "Ověřit heslo",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Pokročilý editor",
|
||||
"Basic Editor": "Základní editor",
|
||||
"Edit Model": "Upravit model",
|
||||
"Model text": "Text modelu",
|
||||
"Model text - Tooltip": "Casbin model řízení přístupu, včetně vestavěných modelů jako ACL, RBAC, ABAC, RESTful, atd. Můžete také vytvářet vlastní modely. Pro více informací navštivte webové stránky Casbin",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Upravit pravidlo",
|
||||
"New Organization": "Nová organizace",
|
||||
"Optional": "Volitelný",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Výzva",
|
||||
"Required": "Povinné",
|
||||
"Soft deletion": "Měkké smazání",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Koupit",
|
||||
"Buy Product": "Koupit produkt",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail produktu",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Testovací stránka nákupu..",
|
||||
"There is no payment channel for this product.": "Pro tento produkt neexistuje žádný platební kanál.",
|
||||
"This product is currently not in sale.": "Tento produkt není momentálně v prodeji.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Upravit HTML pro registraci",
|
||||
"Signup HTML - Tooltip": "Vlastní HTML pro nahrazení výchozího stylu registrační stránky",
|
||||
"Signup group": "Skupina pro registraci",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Tiché",
|
||||
"Site key": "Klíč stránky",
|
||||
"Site key - Tooltip": "Nápověda ke klíči stránky",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Klíče",
|
||||
"Language": "Jazyk",
|
||||
"Language - Tooltip": "Jazyk - Nápověda",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Odkaz",
|
||||
"Location": "Místo",
|
||||
"Location - Tooltip": "Město bydliště",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Sendet",
|
||||
"Submit and complete": "Einreichen und abschließen"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Gehe zur beschreibbaren Demo-Website?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Zuhause",
|
||||
"Home - Tooltip": "Homepage der Anwendung",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Einzigartiger Zufallsstring",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Ist aktiviert",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Stellen Sie sicher, dass das Passwort korrekt ist",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Password complexity options - Tooltip",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Passwort-Salt",
|
||||
"Password salt - Tooltip": "Zufälliger Parameter, der für die Verschlüsselung von Passwörtern verwendet wird",
|
||||
"Password type": "Passworttyp",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Erweiterter Editor",
|
||||
"Basic Editor": "Basis-Editor",
|
||||
"Edit Model": "Modell bearbeiten",
|
||||
"Model text": "Modelltext",
|
||||
"Model text - Tooltip": "Casbin Zugriffskontrollmodell inklusive integrierter Modelle wie ACL, RBAC, ABAC, RESTful, usw. Sie können auch benutzerdefinierte Modelle erstellen. Weitere Informationen finden Sie auf der Casbin-Website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Regel ändern",
|
||||
"New Organization": "Neue Organisation",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Softe Löschung",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Kaufen",
|
||||
"Buy Product": "Produkt kaufen",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail des Produkts",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Testkaufseite.",
|
||||
"There is no payment channel for this product.": "Es gibt keinen Zahlungskanal für dieses Produkt.",
|
||||
"This product is currently not in sale.": "Dieses Produkt steht derzeit nicht zum Verkauf.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Registrierung HTML - Bearbeiten",
|
||||
"Signup HTML - Tooltip": "Benutzerdefiniertes HTML zur Ersetzung des Standard-Registrierungs-Seitenstils",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site-Key",
|
||||
"Site key - Tooltip": "Seitenschlüssel",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Schlüssel",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Ort",
|
||||
"Location - Tooltip": "Stadt des Wohnsitzes",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Go to writable demo site?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Home",
|
||||
"Home - Tooltip": "Home page of the application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unique random string",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Is enabled",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Make sure the password is correct",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Different combinations of password complexity options",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Password salt",
|
||||
"Password salt - Tooltip": "Random parameter used for password encryption",
|
||||
"Password type": "Password type",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "Edit Model",
|
||||
"Model text": "Model text",
|
||||
"Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modify rule",
|
||||
"New Organization": "New Organization",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Soft deletion",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Test buy page..",
|
||||
"There is no payment channel for this product.": "There is no payment channel for this product.",
|
||||
"This product is currently not in sale.": "This product is currently not in sale.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Signup HTML - Edit",
|
||||
"Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "Site key",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Keys",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Location",
|
||||
"Location - Tooltip": "City of residence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Envío",
|
||||
"Submit and complete": "Enviar y completar"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "¿Ir al sitio demo editable?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Hogar",
|
||||
"Home - Tooltip": "Página de inicio de la aplicación",
|
||||
"ID": "identificación",
|
||||
"ID - Tooltip": "Cadena aleatoria única",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Está habilitado",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Asegúrate de que la contraseña sea correcta",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Password complexity options - Tooltip",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Sal de contraseña",
|
||||
"Password salt - Tooltip": "Parámetro aleatorio utilizado para la encriptación de contraseñas",
|
||||
"Password type": "Tipo de contraseña",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Editor avanzado",
|
||||
"Basic Editor": "Editor básico",
|
||||
"Edit Model": "Editar modelo",
|
||||
"Model text": "Texto modelo",
|
||||
"Model text - Tooltip": "Modelo de control de acceso Casbin, incluyendo modelos integrados como ACL, RBAC, ABAC, RESTful, etc. También puede crear modelos personalizados. Para obtener más información, visite el sitio web de Casbin",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modificar regla",
|
||||
"New Organization": "Nueva organización",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Eliminación suave",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Comprar",
|
||||
"Buy Product": "Comprar producto",
|
||||
"CNY": "Año Nuevo Chino (ANC)",
|
||||
"Detail": "Detalle",
|
||||
"Detail - Tooltip": "Detalle del producto",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Página de compra de prueba.",
|
||||
"There is no payment channel for this product.": "No hay canal de pago para este producto.",
|
||||
"This product is currently not in sale.": "Este producto actualmente no está a la venta.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Registro HTML - Editar",
|
||||
"Signup HTML - Tooltip": "HTML personalizado para reemplazar el estilo predeterminado de la página de registro",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Clave del sitio",
|
||||
"Site key - Tooltip": "Clave del sitio",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Claves",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Enlace",
|
||||
"Location": "Ubicación",
|
||||
"Location - Tooltip": "Ciudad de residencia",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -161,6 +161,18 @@
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Go to writable demo site?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Home",
|
||||
"Home - Tooltip": "Home page of the application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unique random string",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Is enabled",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Make sure the password is correct",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Different combinations of password complexity options",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Password salt",
|
||||
"Password salt - Tooltip": "Random parameter used for password encryption",
|
||||
"Password type": "Password type",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "Edit Model",
|
||||
"Model text": "Model text",
|
||||
"Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modify rule",
|
||||
"New Organization": "New Organization",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Soft deletion",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Test buy page..",
|
||||
"There is no payment channel for this product.": "There is no payment channel for this product.",
|
||||
"This product is currently not in sale.": "This product is currently not in sale.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Signup HTML - Edit",
|
||||
"Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "Site key",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Keys",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Location",
|
||||
"Location - Tooltip": "City of residence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Envoi en cours",
|
||||
"Submit and complete": "Soumettre et compléter"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Éditer l'exécuteur",
|
||||
"New Enforcer": "Ajouter un exécuteur"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Allez sur le site de démonstration modifiable ?",
|
||||
"Groups": "Groupes",
|
||||
"Groups - Tooltip": "Groupes - infobulle",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Accueil",
|
||||
"Home - Tooltip": "Page d'accueil de l'application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Chaîne unique aléatoire",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identité",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Est activé",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Assurez-vous que le mot de passe soit correct",
|
||||
"Password complexity options": "Options de complexité du mot de passe",
|
||||
"Password complexity options - Tooltip": "Différentes combinaisons d'options de complexité de mot de passe",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Sel de mot de passe",
|
||||
"Password salt - Tooltip": "Paramètre aléatoire utilisé pour le chiffrement des mots de passe",
|
||||
"Password type": "Type de mot de passe",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Utiliser les SMS",
|
||||
"Use SMS verification code": "Utiliser la vérification par code SMS",
|
||||
"Use a recovery code": "Utiliser un code de récupération",
|
||||
"Verification failed": "Échec de la vérification",
|
||||
"Verify Code": "Vérifier le code",
|
||||
"Verify Password": "Confirmez le mot de passe",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Éditeur avancé",
|
||||
"Basic Editor": "Éditeur de base",
|
||||
"Edit Model": "Modifier le modèle",
|
||||
"Model text": "Définition du modèle",
|
||||
"Model text - Tooltip": "Modèle de contrôle d'accès Casbin, comprenant des modèles intégrés tels que ACL, RBAC, ABAC, RESTful, etc. Vous pouvez également créer des modèles personnalisés. Pour plus d'informations, veuillez visiter le site web de Casbin",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Règle de modification",
|
||||
"New Organization": "Nouvelle organisation",
|
||||
"Optional": "Optionnel",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Requis",
|
||||
"Soft deletion": "Suppression douce",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Acheter",
|
||||
"Buy Product": "Acheter un produit",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Détail",
|
||||
"Detail - Tooltip": "Détail du produit",
|
||||
"Dummy": "Exemple",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Page d'achat de test.",
|
||||
"There is no payment channel for this product.": "Il n'y a aucun canal de paiement pour ce produit.",
|
||||
"This product is currently not in sale.": "Ce produit n'est actuellement pas en vente.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "HTML de la page d'inscription - Modifier",
|
||||
"Signup HTML - Tooltip": "HTML personnalisé pour remplacer le style par défaut de la page d'inscription",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silencieux",
|
||||
"Site key": "Clé de site",
|
||||
"Site key - Tooltip": "Clé de site",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Clés",
|
||||
"Language": "Langue",
|
||||
"Language - Tooltip": "Langue - Infobulle",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Lier",
|
||||
"Location": "Localisation",
|
||||
"Location - Tooltip": "Ville de résidence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Go to writable demo site?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Home",
|
||||
"Home - Tooltip": "Home page of the application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unique random string",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Is enabled",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Make sure the password is correct",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Different combinations of password complexity options",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Password salt",
|
||||
"Password salt - Tooltip": "Random parameter used for password encryption",
|
||||
"Password type": "Password type",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "Edit Model",
|
||||
"Model text": "Model text",
|
||||
"Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modify rule",
|
||||
"New Organization": "New Organization",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Soft deletion",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Test buy page..",
|
||||
"There is no payment channel for this product.": "There is no payment channel for this product.",
|
||||
"This product is currently not in sale.": "This product is currently not in sale.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Signup HTML - Edit",
|
||||
"Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "Site key",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Keys",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Location",
|
||||
"Location - Tooltip": "City of residence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Mengirimkan",
|
||||
"Submit and complete": "Kirim dan selesaikan"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Pergi ke situs demo yang dapat ditulis?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Rumah",
|
||||
"Home - Tooltip": "Halaman utama aplikasi",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Karakter acak unik",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Diaktifkan",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Pastikan kata sandi yang benar",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Password complexity options - Tooltip",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Garam sandi",
|
||||
"Password salt - Tooltip": "Parameter acak yang digunakan untuk enkripsi kata sandi",
|
||||
"Password type": "Jenis kata sandi",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Editor lanjutan",
|
||||
"Basic Editor": "Editor dasar",
|
||||
"Edit Model": "Mengedit Model",
|
||||
"Model text": "Teks Model",
|
||||
"Model text - Tooltip": "Model kontrol akses Casbin, termasuk model bawaan seperti ACL, RBAC, ABAC, RESTful, dll. Anda juga dapat membuat model kustom. Untuk informasi lebih lanjut, silakan kunjungi situs web Casbin",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Mengubah aturan",
|
||||
"New Organization": "Organisasi baru",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Penghapusan lunak",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Beli",
|
||||
"Buy Product": "Beli Produk",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Rincian",
|
||||
"Detail - Tooltip": "Detail produk",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Halaman pembelian uji coba.",
|
||||
"There is no payment channel for this product.": "Tidak ada saluran pembayaran untuk produk ini.",
|
||||
"This product is currently not in sale.": "Produk ini saat ini tidak dijual.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Pendaftaran HTML - Sunting",
|
||||
"Signup HTML - Tooltip": "HTML khusus untuk mengganti gaya halaman pendaftaran bawaan",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Kunci situs",
|
||||
"Site key - Tooltip": "Kunci situs atau kunci halaman web",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Kunci",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Tautan",
|
||||
"Location": "Lokasi",
|
||||
"Location - Tooltip": "Kota tempat tinggal",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Go to writable demo site?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Home",
|
||||
"Home - Tooltip": "Home page of the application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unique random string",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Is enabled",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Make sure the password is correct",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Different combinations of password complexity options",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Password salt",
|
||||
"Password salt - Tooltip": "Random parameter used for password encryption",
|
||||
"Password type": "Password type",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "Edit Model",
|
||||
"Model text": "Model text",
|
||||
"Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modify rule",
|
||||
"New Organization": "New Organization",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Soft deletion",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Test buy page..",
|
||||
"There is no payment channel for this product.": "There is no payment channel for this product.",
|
||||
"This product is currently not in sale.": "This product is currently not in sale.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Signup HTML - Edit",
|
||||
"Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "Site key",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Keys",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Location",
|
||||
"Location - Tooltip": "City of residence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "送信",
|
||||
"Submit and complete": "提出して完了してください"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "書き込み可能なデモサイトに移動しますか?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "ホーム",
|
||||
"Home - Tooltip": "アプリケーションのホームページ",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "ユニークなランダム文字列",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "可能になっています",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "パスワードが正しいことを確認してください",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Password complexity options - Tooltip",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "パスワードのソルト",
|
||||
"Password salt - Tooltip": "ランダムパラメーターは、パスワードの暗号化に使用されます",
|
||||
"Password type": "パスワードタイプ",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "編集モデル",
|
||||
"Model text": "モデルテキスト",
|
||||
"Model text - Tooltip": "Casbinのアクセス制御モデルには、ACL、RBAC、ABAC、RESTfulなどの組み込みモデルが含まれています。カスタムモデルも作成できます。詳細については、Casbinのウェブサイトをご覧ください",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "ルールを変更する",
|
||||
"New Organization": "新しい組織",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "ソフト削除",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "購入",
|
||||
"Buy Product": "製品を購入する",
|
||||
"CNY": "CNY",
|
||||
"Detail": "詳細",
|
||||
"Detail - Tooltip": "製品の詳細",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "テスト購入ページ。",
|
||||
"There is no payment channel for this product.": "この製品には支払いチャネルがありません。",
|
||||
"This product is currently not in sale.": "この製品は現在販売されていません。",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "サインアップ HTML - 編集",
|
||||
"Signup HTML - Tooltip": "デフォルトのサインアップページスタイルを置き換えるためのカスタムHTML",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "サイトキー",
|
||||
"Site key - Tooltip": "サイトキー",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "鍵",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "リンク",
|
||||
"Location": "場所",
|
||||
"Location - Tooltip": "居住都市",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Go to writable demo site?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Home",
|
||||
"Home - Tooltip": "Home page of the application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unique random string",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Is enabled",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Make sure the password is correct",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Different combinations of password complexity options",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Password salt",
|
||||
"Password salt - Tooltip": "Random parameter used for password encryption",
|
||||
"Password type": "Password type",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "Edit Model",
|
||||
"Model text": "Model text",
|
||||
"Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modify rule",
|
||||
"New Organization": "New Organization",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Soft deletion",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Test buy page..",
|
||||
"There is no payment channel for this product.": "There is no payment channel for this product.",
|
||||
"This product is currently not in sale.": "This product is currently not in sale.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Signup HTML - Edit",
|
||||
"Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "Site key",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Keys",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Location",
|
||||
"Location - Tooltip": "City of residence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "전송하기",
|
||||
"Submit and complete": "제출하고 완료하십시오"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "쓰기 가능한 데모 사이트로 이동하시겠습니까?",
|
||||
"Groups": "그룹",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "집",
|
||||
"Home - Tooltip": "어플리케이션 홈 페이지",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "유일한 랜덤 문자열",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "활성화됩니다",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "비밀번호가 올바른지 확인하세요",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Password complexity options - Tooltip",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "비밀번호 솔트",
|
||||
"Password salt - Tooltip": "암호화에 사용되는 임의 매개변수",
|
||||
"Password type": "암호 유형",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "고급 편집기",
|
||||
"Basic Editor": "기본 편집기",
|
||||
"Edit Model": "편집 형태 모델",
|
||||
"Model text": "모델 텍스트",
|
||||
"Model text - Tooltip": "Casbin 액세스 제어 모델은 ACL, RBAC, ABAC, RESTful 등의 내장된 모델을 포함하며 사용자 정의 모델도 만들 수 있습니다. 자세한 정보는 Casbin 웹 사이트를 방문하십시오",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "규칙 수정",
|
||||
"New Organization": "새로운 조직",
|
||||
"Optional": "선택사항",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "소프트 삭제",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "구매하다",
|
||||
"Buy Product": "제품을 구입하세요",
|
||||
"CNY": "CNY",
|
||||
"Detail": "세부사항",
|
||||
"Detail - Tooltip": "제품의 세부사항",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "시험 구매 페이지.",
|
||||
"There is no payment channel for this product.": "이 제품에 대한 결제 채널이 없습니다.",
|
||||
"This product is currently not in sale.": "이 제품은 현재 판매되지 않고 있습니다.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "가입 HTML - 수정",
|
||||
"Signup HTML - Tooltip": "기본 가입 페이지 스타일을 바꾸기 위한 사용자 지정 HTML",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "사이트 키",
|
||||
"Site key - Tooltip": "사이트 키",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "열쇠",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "링크",
|
||||
"Location": "장소",
|
||||
"Location - Tooltip": "거주 도시",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Go to writable demo site?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Home",
|
||||
"Home - Tooltip": "Home page of the application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unique random string",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Is enabled",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Make sure the password is correct",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Different combinations of password complexity options",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Password salt",
|
||||
"Password salt - Tooltip": "Random parameter used for password encryption",
|
||||
"Password type": "Password type",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "Edit Model",
|
||||
"Model text": "Model text",
|
||||
"Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modify rule",
|
||||
"New Organization": "New Organization",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Soft deletion",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Test buy page..",
|
||||
"There is no payment channel for this product.": "There is no payment channel for this product.",
|
||||
"This product is currently not in sale.": "This product is currently not in sale.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Signup HTML - Edit",
|
||||
"Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "Site key",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Keys",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Location",
|
||||
"Location - Tooltip": "City of residence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Go to writable demo site?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Home",
|
||||
"Home - Tooltip": "Home page of the application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unique random string",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Is enabled",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Make sure the password is correct",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Different combinations of password complexity options",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Password salt",
|
||||
"Password salt - Tooltip": "Random parameter used for password encryption",
|
||||
"Password type": "Password type",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "Edit Model",
|
||||
"Model text": "Model text",
|
||||
"Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modify rule",
|
||||
"New Organization": "New Organization",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Soft deletion",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Test buy page..",
|
||||
"There is no payment channel for this product.": "There is no payment channel for this product.",
|
||||
"This product is currently not in sale.": "This product is currently not in sale.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Signup HTML - Edit",
|
||||
"Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "Site key",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Keys",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Location",
|
||||
"Location - Tooltip": "City of residence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Go to writable demo site?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Home",
|
||||
"Home - Tooltip": "Home page of the application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unique random string",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Is enabled",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Make sure the password is correct",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Different combinations of password complexity options",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Password salt",
|
||||
"Password salt - Tooltip": "Random parameter used for password encryption",
|
||||
"Password type": "Password type",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "Edit Model",
|
||||
"Model text": "Model text",
|
||||
"Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modify rule",
|
||||
"New Organization": "New Organization",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Soft deletion",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Test buy page..",
|
||||
"There is no payment channel for this product.": "There is no payment channel for this product.",
|
||||
"This product is currently not in sale.": "This product is currently not in sale.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Signup HTML - Edit",
|
||||
"Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "Site key",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Keys",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Location",
|
||||
"Location - Tooltip": "City of residence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Enviando",
|
||||
"Submit and complete": "Enviar e concluir"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Editar Executor",
|
||||
"New Enforcer": "Novo Executor"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Acessar o site de demonstração gravável?",
|
||||
"Groups": "Grupos",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Página Inicial",
|
||||
"Home - Tooltip": "Página inicial do aplicativo",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "String única aleatória",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identidade",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Está habilitado",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Certifique-se de que a senha está correta",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Password complexity options - Tooltip",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Salt de senha",
|
||||
"Password salt - Tooltip": "Parâmetro aleatório usado para criptografia de senha",
|
||||
"Password type": "Tipo de senha",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Editor Avançado",
|
||||
"Basic Editor": "Editor Básico",
|
||||
"Edit Model": "Editar Modelo",
|
||||
"Model text": "Texto do Modelo",
|
||||
"Model text - Tooltip": "Modelo de controle de acesso Casbin, incluindo modelos incorporados como ACL, RBAC, ABAC, RESTful, etc. Você também pode criar modelos personalizados. Para obter mais informações, visite o site do Casbin",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modificar regra",
|
||||
"New Organization": "Nova Organização",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Exclusão suave",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Comprar",
|
||||
"Buy Product": "Comprar Produto",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detalhe",
|
||||
"Detail - Tooltip": "Detalhes do produto",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Página de teste de compra...",
|
||||
"There is no payment channel for this product.": "Não há canal de pagamento disponível para este produto.",
|
||||
"This product is currently not in sale.": "Este produto não está disponível para venda no momento.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Editar HTML de inscrição",
|
||||
"Signup HTML - Tooltip": "HTML personalizado para substituir o estilo padrão da página de inscrição",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silencioso",
|
||||
"Site key": "Chave do site",
|
||||
"Site key - Tooltip": "Chave do site",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Chaves",
|
||||
"Language": "Idioma",
|
||||
"Language - Tooltip": "Idioma - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Localização",
|
||||
"Location - Tooltip": "Cidade de residência",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Отправка",
|
||||
"Submit and complete": "Отправить и завершить"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Редактировать контролёра доступа",
|
||||
"New Enforcer": "Новый контролёр доступа"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Перейти на демонстрационный сайт для записи данных?",
|
||||
"Groups": "Группы",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Дом",
|
||||
"Home - Tooltip": "Главная страница приложения",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Уникальная случайная строка",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Включен",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Убедитесь, что пароль правильный",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Password complexity options - Tooltip",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Соль пароля",
|
||||
"Password salt - Tooltip": "Случайный параметр, используемый для шифрования пароля",
|
||||
"Password type": "Тип пароля",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Использовать SMS",
|
||||
"Use SMS verification code": "Использовать SMS код для проверки",
|
||||
"Use a recovery code": "Использовать код восстановления",
|
||||
"Verification failed": "Проверка не удалась",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Расширенный редактор",
|
||||
"Basic Editor": "Базовый редактор",
|
||||
"Edit Model": "Редактировать модель",
|
||||
"Model text": "Модельный текст",
|
||||
"Model text - Tooltip": "Модель контроля доступа Casbin, включая встроенные модели, такие как ACL, RBAC, ABAC, RESTful и т. д. Вы также можете создавать свои собственные модели. Для получения дополнительной информации, пожалуйста, посетите веб-сайт Casbin",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Изменить правило",
|
||||
"New Organization": "Новая организация",
|
||||
"Optional": "Опционально",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Мягкое удаление",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Купить",
|
||||
"Buy Product": "Купить продукт",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Деталь",
|
||||
"Detail - Tooltip": "Деталь продукта",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Страница для тестовой покупки.",
|
||||
"There is no payment channel for this product.": "Для этого продукта нет канала оплаты.",
|
||||
"This product is currently not in sale.": "Этот продукт в настоящее время не продается.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Регистрационная форма HTML - Редактировать",
|
||||
"Signup HTML - Tooltip": "Пользовательский HTML для замены стиля стандартной страницы регистрации",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Ключ сайта",
|
||||
"Site key - Tooltip": "Ключ сайта",
|
||||
@ -972,7 +993,7 @@
|
||||
"Please input your affiliation!": "Пожалуйста, укажите свою принадлежность!",
|
||||
"Please input your display name!": "Пожалуйста, введите своё отображаемое имя!",
|
||||
"Please input your first name!": "Пожалуйста, введите свое имя!",
|
||||
"Please input your invitation code!": "Please input your invitation code!",
|
||||
"Please input your invitation code!": "Пожалуйста, введите код приглашения!",
|
||||
"Please input your last name!": "Введите свою фамилию!",
|
||||
"Please input your phone number!": "Пожалуйста, введите свой номер телефона!",
|
||||
"Please input your real name!": "Пожалуйста, введите своё настоящее имя!",
|
||||
@ -1157,15 +1178,16 @@
|
||||
"Keys": "Ключи",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Ссылка",
|
||||
"Location": "Местоположение",
|
||||
"Location - Tooltip": "Город проживания",
|
||||
"MFA accounts": "MFA accounts",
|
||||
"Managed accounts": "Управляемые аккаунты",
|
||||
"Modify password...": "Изменить пароль...",
|
||||
"Multi-factor authentication": "Multi-factor authentication",
|
||||
"Need update password": "Need update password",
|
||||
"Need update password - Tooltip": "Force user update password after login",
|
||||
"Multi-factor authentication": "Многофакторная аутентификация",
|
||||
"Need update password": "Необходимо обновить пароль",
|
||||
"Need update password - Tooltip": "Заставить пользователя обновить пароль после входа в систему",
|
||||
"New Email": "Новое электронное письмо",
|
||||
"New Password": "Новый пароль",
|
||||
"New User": "Новый пользователь",
|
||||
@ -1189,26 +1211,26 @@
|
||||
"Set password...": "Установить пароль...",
|
||||
"Tag": "Метка",
|
||||
"Tag - Tooltip": "Тег пользователя",
|
||||
"The password must contain at least one special character": "The password must contain at least one special character",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "The password must contain at least one uppercase letter, one lowercase letter and one digit",
|
||||
"The password must have at least 6 characters": "The password must have at least 6 characters",
|
||||
"The password must have at least 8 characters": "The password must have at least 8 characters",
|
||||
"The password must not contain any repeated characters": "The password must not contain any repeated characters",
|
||||
"This field value doesn't match the pattern rule": "This field value doesn't match the pattern rule",
|
||||
"The password must contain at least one special character": "Пароль должен содержать хотя бы один специальный символ",
|
||||
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Пароль должен содержать как минимум одну заглавную букву, одну строчную букву и одну цифру",
|
||||
"The password must have at least 6 characters": "Пароль должен быть минимум 6 символов",
|
||||
"The password must have at least 8 characters": "Пароль должен быть минимум 8 символов",
|
||||
"The password must not contain any repeated characters": "Пароль не должен содержать повторяющиеся символы",
|
||||
"This field value doesn't match the pattern rule": "Значение поля не соответствует шаблону",
|
||||
"Title": "Заголовок",
|
||||
"Title - Tooltip": "Положение в аффилиации",
|
||||
"Two passwords you typed do not match.": "Два введенных вами пароля не совпадают.",
|
||||
"Unlink": "Отсоединить",
|
||||
"Upload (.xlsx)": "Загрузить (.xlsx)",
|
||||
"Upload ID card back picture": "Upload ID card back picture",
|
||||
"Upload ID card front picture": "Upload ID card front picture",
|
||||
"Upload ID card with person picture": "Upload ID card with person picture",
|
||||
"Upload ID card back picture": "Загрузите заднюю сторону удостоверения личности",
|
||||
"Upload ID card front picture": "Загрузите переднюю сторону удостоверения личности",
|
||||
"Upload ID card with person picture": "Загрузите удостоверение личности с фотографией",
|
||||
"Upload a photo": "Загрузить фото",
|
||||
"User Profile": "User Profile",
|
||||
"User Profile": "Профиль пользователя",
|
||||
"Values": "Значения",
|
||||
"Verification code sent": "Код подтверждения отправлен",
|
||||
"WebAuthn credentials": "WebAuthn удостоверения",
|
||||
"You have changed the username, please save your change first before modifying the password": "You have changed the username, please save your change first before modifying the password",
|
||||
"You have changed the username, please save your change first before modifying the password": "Имя было изменено, сохраните изменения перед сменой пароля",
|
||||
"input password": "введите пароль"
|
||||
},
|
||||
"verification": {
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Odosielanie",
|
||||
"Submit and complete": "Odoslať a dokončiť"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Upraviť vynútiteľa",
|
||||
"New Enforcer": "Nový vynútiteľ"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Prejsť na zapisovateľnú demo stránku?",
|
||||
"Groups": "Skupiny",
|
||||
"Groups - Tooltip": "Skupiny",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Domov",
|
||||
"Home - Tooltip": "Domovská stránka aplikácie",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Jedinečný náhodný reťazec",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identita",
|
||||
"Invitations": "Pozvánky",
|
||||
"Is enabled": "Je povolené",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Uistite sa, že heslo je správne",
|
||||
"Password complexity options": "Možnosti zložitosti hesla",
|
||||
"Password complexity options - Tooltip": "Rôzne kombinácie možností zložitosti hesla",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Soľ hesla",
|
||||
"Password salt - Tooltip": "Náhodný parameter používaný na šifrovanie hesla",
|
||||
"Password type": "Typ hesla",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Použiť SMS",
|
||||
"Use SMS verification code": "Použiť overovací kód SMS",
|
||||
"Use a recovery code": "Použiť obnovovací kód",
|
||||
"Verification failed": "Overenie zlyhalo",
|
||||
"Verify Code": "Overiť kód",
|
||||
"Verify Password": "Overiť heslo",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Rozšírený editor",
|
||||
"Basic Editor": "Základný editor",
|
||||
"Edit Model": "Upraviť model",
|
||||
"Model text": "Text modelu",
|
||||
"Model text - Tooltip": "Model prístupu Casbin, vrátane vstavaných modelov ako ACL, RBAC, ABAC, RESTful, atď. Môžete tiež vytvoriť vlastné modely. Pre viac informácií navštívte web Casbin",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Upraviť pravidlo",
|
||||
"New Organization": "Nová organizácia",
|
||||
"Optional": "Voliteľné",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Výzva",
|
||||
"Required": "Povinné",
|
||||
"Soft deletion": "Mäkké vymazanie",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Kúpiť",
|
||||
"Buy Product": "Kúpiť produkt",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail produktu",
|
||||
"Dummy": "Vzorkový",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Testovať stránku nákupu..",
|
||||
"There is no payment channel for this product.": "Pre tento produkt neexistuje platobný kanál.",
|
||||
"This product is currently not in sale.": "Tento produkt momentálne nie je v predaji.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Upraviť HTML registrácie",
|
||||
"Signup HTML - Tooltip": "Vlastné HTML na nahradenie predvoleného štýlu registračnej stránky",
|
||||
"Signup group": "Skupina registrácie",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Tichý",
|
||||
"Site key": "Kľúč stránky",
|
||||
"Site key - Tooltip": "Kľúč stránky",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Kľúče",
|
||||
"Language": "Jazyk",
|
||||
"Language - Tooltip": "Jazyk - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Odkaz",
|
||||
"Location": "Miesto",
|
||||
"Location - Tooltip": "Mesto bydliska",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Go to writable demo site?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Home",
|
||||
"Home - Tooltip": "Home page of the application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unique random string",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Is enabled",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Make sure the password is correct",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Different combinations of password complexity options",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Password salt",
|
||||
"Password salt - Tooltip": "Random parameter used for password encryption",
|
||||
"Password type": "Password type",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "Edit Model",
|
||||
"Model text": "Model text",
|
||||
"Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modify rule",
|
||||
"New Organization": "New Organization",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Soft deletion",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Test buy page..",
|
||||
"There is no payment channel for this product.": "There is no payment channel for this product.",
|
||||
"This product is currently not in sale.": "This product is currently not in sale.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Signup HTML - Edit",
|
||||
"Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "Site key",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Keys",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Location",
|
||||
"Location - Tooltip": "City of residence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Gönderiliyor",
|
||||
"Submit and complete": "Gönder ve Tamamla"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Go to writable demo site?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Ana sayfa",
|
||||
"Home - Tooltip": "Home page of the application",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Unique random string",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Davetler",
|
||||
"Is enabled": "Aktif Mi?",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Şifrenizin doğru olduğundan emin olun",
|
||||
"Password complexity options": "Şifre karmaşıklık seçenekleri",
|
||||
"Password complexity options - Tooltip": "Different combinations of password complexity options",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Password salt",
|
||||
"Password salt - Tooltip": "Random parameter used for password encryption",
|
||||
"Password type": "Password type",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "SMS kullan",
|
||||
"Use SMS verification code": "SMS doğrulama kodunu kullan",
|
||||
"Use a recovery code": "Kurtarma kodu kullan",
|
||||
"Verification failed": "Doğrulama başarısız",
|
||||
"Verify Code": "Kodu doğrula",
|
||||
"Verify Password": "Parolayı Doğrula",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Advanced Editor",
|
||||
"Basic Editor": "Basic Editor",
|
||||
"Edit Model": "Modeli Düzenle",
|
||||
"Model text": "Model text",
|
||||
"Model text - Tooltip": "Casbin access control model, including built-in models like ACL, RBAC, ABAC, RESTful, etc. You can also create custom models. For more information, please visit the Casbin website",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Modify rule",
|
||||
"New Organization": "New Organization",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Gerekli",
|
||||
"Soft deletion": "Soft deletion",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Buy",
|
||||
"Buy Product": "Buy Product",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail of product",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Test buy page..",
|
||||
"There is no payment channel for this product.": "There is no payment channel for this product.",
|
||||
"This product is currently not in sale.": "This product is currently not in sale.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Signup HTML - Edit",
|
||||
"Signup HTML - Tooltip": "Custom HTML for replacing the default signup page style",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Silent",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "Site key",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Keys",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Link",
|
||||
"Location": "Location",
|
||||
"Location - Tooltip": "City of residence",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Відправка",
|
||||
"Submit and complete": "Надішліть і заповніть"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Редагувати Enforcer",
|
||||
"New Enforcer": "Новий Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Перейти на демонстраційний сайт для запису?",
|
||||
"Groups": "Групи",
|
||||
"Groups - Tooltip": "Групи – підказка",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "додому",
|
||||
"Home - Tooltip": "Домашня сторінка програми",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Унікальний випадковий рядок",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Ідентичність",
|
||||
"Invitations": "Запрошення",
|
||||
"Is enabled": "Увімкнено",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Переконайтеся, що пароль правильний",
|
||||
"Password complexity options": "Параметри складності пароля",
|
||||
"Password complexity options - Tooltip": "Різні комбінації параметрів складності пароля",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Сіль пароля",
|
||||
"Password salt - Tooltip": "Випадковий параметр, який використовується для шифрування пароля",
|
||||
"Password type": "Тип пароля",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Використовуйте SMS",
|
||||
"Use SMS verification code": "Використовуйте код підтвердження SMS",
|
||||
"Use a recovery code": "Використовуйте код відновлення",
|
||||
"Verification failed": "Не вдалося перевірити",
|
||||
"Verify Code": "Підтвердити код",
|
||||
"Verify Password": "Підтвердіть пароль",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Розширений редактор",
|
||||
"Basic Editor": "Базовий редактор",
|
||||
"Edit Model": "Редагувати модель",
|
||||
"Model text": "Текст моделі",
|
||||
"Model text - Tooltip": "Модель контролю доступу Casbin, включаючи такі вбудовані моделі, як ACL, RBAC, ABAC, RESTful тощо. Ви також можете створювати власні моделі. ",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Змінити правило",
|
||||
"New Organization": "Нова організація",
|
||||
"Optional": "Додатково",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Підкажіть",
|
||||
"Required": "вимагається",
|
||||
"Soft deletion": "М'яке видалення",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "купити",
|
||||
"Buy Product": "Купити товар",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Деталь",
|
||||
"Detail - Tooltip": "Деталь продукту",
|
||||
"Dummy": "манекен",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Сторінка тестової покупки..",
|
||||
"There is no payment channel for this product.": "Для цього продукту немає платіжного каналу.",
|
||||
"This product is currently not in sale.": "Цей товар наразі відсутній у продажу.",
|
||||
"USD": "доларів США",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Реєстраційний HTML - Редагувати",
|
||||
"Signup HTML - Tooltip": "Спеціальний HTML для заміни стилю сторінки реєстрації за умовчанням",
|
||||
"Signup group": "Група реєстрації",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Мовчазний",
|
||||
"Site key": "Ключ сайту",
|
||||
"Site key - Tooltip": "Ключ сайту",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Ключі",
|
||||
"Language": "Мова",
|
||||
"Language - Tooltip": "Мова – підказка",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Посилання",
|
||||
"Location": "Місцезнаходження",
|
||||
"Location - Tooltip": "Місто проживання",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "Gửi",
|
||||
"Submit and complete": "Nộp và hoàn thành"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "Bạn có muốn đi đến trang demo có thể viết được không?",
|
||||
"Groups": "Groups",
|
||||
"Groups - Tooltip": "Groups - Tooltip",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "Nhà",
|
||||
"Home - Tooltip": "Trang chủ của ứng dụng",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "Chuỗi ngẫu nhiên độc nhất",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "Identity",
|
||||
"Invitations": "Invitations",
|
||||
"Is enabled": "Đã được kích hoạt",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "Hãy đảm bảo rằng mật khẩu là chính xác",
|
||||
"Password complexity options": "Password complexity options",
|
||||
"Password complexity options - Tooltip": "Password complexity options - Tooltip",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "Muối mật khẩu",
|
||||
"Password salt - Tooltip": "Tham số ngẫu nhiên được sử dụng để mã hóa mật khẩu",
|
||||
"Password type": "Loại mật khẩu",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
"Verify Code": "Verify Code",
|
||||
"Verify Password": "Verify Password",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "Secret Key"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "Editor nâng cao",
|
||||
"Basic Editor": "Editor cơ bản",
|
||||
"Edit Model": "Sửa mô hình",
|
||||
"Model text": "Văn bản mẫu",
|
||||
"Model text - Tooltip": "Mô hình kiểm soát truy cập Casbin, bao gồm các mô hình tích hợp như ACL, RBAC, ABAC, RESTful, v.v. Bạn cũng có thể tạo các mô hình tùy chỉnh. Để biết thêm thông tin, vui lòng truy cập trang web Casbin",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "Sửa đổi quy tắc",
|
||||
"New Organization": "Tổ chức mới",
|
||||
"Optional": "Optional",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "Prompt",
|
||||
"Required": "Required",
|
||||
"Soft deletion": "Xóa mềm",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "Alipay",
|
||||
"Buy": "Mua",
|
||||
"Buy Product": "Mua sản phẩm",
|
||||
"CNY": "CNY",
|
||||
"Detail": "Chi tiết",
|
||||
"Detail - Tooltip": "Chi tiết sản phẩm",
|
||||
"Dummy": "Dummy",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "Trang mua thử.",
|
||||
"There is no payment channel for this product.": "Không có kênh thanh toán cho sản phẩm này.",
|
||||
"This product is currently not in sale.": "Sản phẩm này hiện không được bán.",
|
||||
"USD": "USD",
|
||||
"WeChat Pay": "WeChat Pay"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "Đăng ký HTML - Chỉnh sửa",
|
||||
"Signup HTML - Tooltip": "Trang HTML tùy chỉnh để thay thế phong cách trang đăng ký mặc định",
|
||||
"Signup group": "Signup group",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "Im lặng",
|
||||
"Site key": "Khóa trang web",
|
||||
"Site key - Tooltip": "Khóa trang web",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "Chìa khóa",
|
||||
"Language": "Language",
|
||||
"Language - Tooltip": "Language - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "Liên kết",
|
||||
"Location": "Vị trí",
|
||||
"Location - Tooltip": "Thành phố cư trú",
|
||||
|
@ -161,6 +161,18 @@
|
||||
"Sending": "发送中",
|
||||
"Submit and complete": "完成提交"
|
||||
},
|
||||
"currency": {
|
||||
"AUD": "AUD",
|
||||
"CAD": "CAD",
|
||||
"CHF": "CHF",
|
||||
"CNY": "CNY",
|
||||
"EUR": "EUR",
|
||||
"GBP": "GBP",
|
||||
"HKD": "HKD",
|
||||
"JPY": "JPY",
|
||||
"SGD": "SGD",
|
||||
"USD": "USD"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "编辑Casbin执行器",
|
||||
"New Enforcer": "新建Casbin执行器"
|
||||
@ -266,10 +278,13 @@
|
||||
"Go to writable demo site?": "跳转至可写演示站点?",
|
||||
"Groups": "群组",
|
||||
"Groups - Tooltip": "组",
|
||||
"Hide password": "Hide password",
|
||||
"Home": "首页",
|
||||
"Home - Tooltip": "应用的首页",
|
||||
"ID": "ID",
|
||||
"ID - Tooltip": "唯一的随机字符串",
|
||||
"IP whitelist": "IP whitelist",
|
||||
"IP whitelist - Tooltip": "IP whitelist - Tooltip",
|
||||
"Identity": "身份认证",
|
||||
"Invitations": "邀请码",
|
||||
"Is enabled": "已启用",
|
||||
@ -312,6 +327,10 @@
|
||||
"Password - Tooltip": "请确认密码正确",
|
||||
"Password complexity options": "密码复杂度选项",
|
||||
"Password complexity options - Tooltip": "密码复杂度组合,登录密码复杂度必须符合该规范",
|
||||
"Password obf key": "Password obf key",
|
||||
"Password obf key - Tooltip": "Password obf key - Tooltip",
|
||||
"Password obfuscator": "Password obfuscator",
|
||||
"Password obfuscator - Tooltip": "Password obfuscator - Tooltip",
|
||||
"Password salt": "密码Salt值",
|
||||
"Password salt - Tooltip": "用于密码加密的随机参数",
|
||||
"Password type": "密码类型",
|
||||
@ -559,7 +578,6 @@
|
||||
"Use SMS": "使用短信",
|
||||
"Use SMS verification code": "使用手机或电子邮件发送验证码认证",
|
||||
"Use a recovery code": "使用恢复代码",
|
||||
"Verification failed": "验证失败",
|
||||
"Verify Code": "验证码",
|
||||
"Verify Password": "验证密码",
|
||||
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "您已经启用多因素认证, 请点击 '发送验证码' 继续",
|
||||
@ -574,6 +592,8 @@
|
||||
"Secret Key": "密钥"
|
||||
},
|
||||
"model": {
|
||||
"Advanced Editor": "高级编辑器",
|
||||
"Basic Editor": "基础编辑器",
|
||||
"Edit Model": "编辑模型",
|
||||
"Model text": "模型文本",
|
||||
"Model text - Tooltip": "Casbin访问控制模型,支持ACL、RBAC、ABAC、RESTful等内置模型,也可以自定义模型,具体请查看Casbin官网",
|
||||
@ -592,6 +612,8 @@
|
||||
"Modify rule": "修改规则",
|
||||
"New Organization": "添加组织",
|
||||
"Optional": "可选",
|
||||
"Password expire days": "Password expire days",
|
||||
"Password expire days - Tooltip": "Password expire days - Tooltip",
|
||||
"Prompt": "提示",
|
||||
"Required": "必须",
|
||||
"Soft deletion": "软删除",
|
||||
@ -709,7 +731,6 @@
|
||||
"Alipay": "支付宝",
|
||||
"Buy": "购买",
|
||||
"Buy Product": "购买商品",
|
||||
"CNY": "人民币",
|
||||
"Detail": "详情",
|
||||
"Detail - Tooltip": "商品详情",
|
||||
"Dummy": "虚拟",
|
||||
@ -740,7 +761,6 @@
|
||||
"Test buy page..": "测试购买页面..",
|
||||
"There is no payment channel for this product.": "该商品没有付款方式。",
|
||||
"This product is currently not in sale.": "该商品目前未在售。",
|
||||
"USD": "美元",
|
||||
"WeChat Pay": "微信支付"
|
||||
},
|
||||
"provider": {
|
||||
@ -893,6 +913,7 @@
|
||||
"Signup HTML - Edit": "注册页面HTML - 编辑",
|
||||
"Signup HTML - Tooltip": "自定义HTML,用于替换默认的注册页面样式",
|
||||
"Signup group": "注册后的群组",
|
||||
"Signup group - Tooltip": "Signup group - Tooltip",
|
||||
"Silent": "静默",
|
||||
"Site key": "Site key",
|
||||
"Site key - Tooltip": "站点密钥",
|
||||
@ -1157,6 +1178,7 @@
|
||||
"Keys": "键",
|
||||
"Language": "语言",
|
||||
"Language - Tooltip": "语言 - Tooltip",
|
||||
"Last change password time": "Last change password time",
|
||||
"Link": "绑定",
|
||||
"Location": "城市",
|
||||
"Location - Tooltip": "居住地址所在的城市",
|
||||
|
@ -14,9 +14,10 @@
|
||||
|
||||
import React from "react";
|
||||
import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
|
||||
import {Alert, Button, Col, Image, Input, Popover, QRCode, Row, Table, Tooltip} from "antd";
|
||||
import {Button, Col, Image, Input, Popover, Row, Table, Tooltip} from "antd";
|
||||
import * as Setting from "../Setting";
|
||||
import i18next from "i18next";
|
||||
import {CasdoorAppQrCode, CasdoorAppUrl} from "../common/CasdoorAppConnector";
|
||||
|
||||
class MfaAccountTable extends React.Component {
|
||||
constructor(props) {
|
||||
@ -76,42 +77,6 @@ class MfaAccountTable extends React.Component {
|
||||
this.updateTable(table);
|
||||
}
|
||||
|
||||
getQrUrl() {
|
||||
const {accessToken} = this.props;
|
||||
let qrUrl = `casdoor-app://login?serverUrl=${window.location.origin}&accessToken=${accessToken}`;
|
||||
let error = null;
|
||||
|
||||
if (!accessToken) {
|
||||
qrUrl = "";
|
||||
error = i18next.t("general:Access token is empty");
|
||||
}
|
||||
|
||||
if (qrUrl.length >= 2000) {
|
||||
qrUrl = "";
|
||||
error = i18next.t("general:QR code is too large");
|
||||
}
|
||||
|
||||
return {qrUrl, error};
|
||||
}
|
||||
|
||||
renderQrCode() {
|
||||
const {qrUrl, error} = this.getQrUrl();
|
||||
|
||||
if (error) {
|
||||
return <Alert message={error} type="error" showIcon />;
|
||||
} else {
|
||||
return (
|
||||
<QRCode
|
||||
value={qrUrl}
|
||||
icon={this.state.icon}
|
||||
errorLevel="M"
|
||||
size={230}
|
||||
bordered={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderTable(table) {
|
||||
const columns = [
|
||||
{
|
||||
@ -140,6 +105,18 @@ class MfaAccountTable extends React.Component {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("mfaAccount:Origin"),
|
||||
dataIndex: "origin",
|
||||
key: "origin",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Input value={text} onChange={e => {
|
||||
this.updateField(table, index, "origin", e.target.value);
|
||||
}} />
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("mfaAccount:Secret Key"),
|
||||
dataIndex: "secretKey",
|
||||
@ -194,10 +171,25 @@ class MfaAccountTable extends React.Component {
|
||||
title={() => (
|
||||
<div>
|
||||
{this.props.title}
|
||||
<Button style={{marginRight: "10px"}} type="primary" size="small" onClick={() => this.addRow(table)}>{i18next.t("general:Add")}</Button>
|
||||
<Popover trigger="focus" overlayInnerStyle={{padding: 0}}
|
||||
content={this.renderQrCode()}>
|
||||
<Button style={{marginLeft: "5px"}} type="primary" size="small">{i18next.t("general:QR Code")}</Button>
|
||||
<Button style={{marginRight: "10px"}} type="primary" size="small" onClick={() => this.addRow(table)}>
|
||||
{i18next.t("general:Add")}
|
||||
</Button>
|
||||
<Popover
|
||||
trigger="focus"
|
||||
overlayInnerStyle={{padding: 0}}
|
||||
content={<CasdoorAppQrCode accessToken={this.props.accessToken} icon={this.state.icon} />}
|
||||
>
|
||||
<Button style={{marginRight: "10px"}} type="primary" size="small">
|
||||
{i18next.t("general:QR Code")}
|
||||
</Button>
|
||||
</Popover>
|
||||
<Popover
|
||||
trigger="click"
|
||||
content={<CasdoorAppUrl accessToken={this.props.accessToken} />}
|
||||
>
|
||||
<Button type="primary" size="small">
|
||||
{i18next.t("general:URL")}
|
||||
</Button>
|
||||
</Popover>
|
||||
</div>
|
||||
)}
|
||||
|
@ -136,7 +136,7 @@ class SigninMethodTable extends React.Component {
|
||||
options = [
|
||||
{id: "All", name: i18next.t("general:All")},
|
||||
{id: "Non-LDAP", name: i18next.t("general:Non-LDAP")},
|
||||
{id: "Hide-Password", name: i18next.t("general:Hide-Password")},
|
||||
{id: "Hide password", name: i18next.t("general:Hide password")},
|
||||
];
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user