Compare commits

...

31 Commits

Author SHA1 Message Date
abfc464155 Remove isEnabled for model, adapter and enforcer, improve UI 2023-08-18 19:22:47 +08:00
a41f6880a2 feat: move policy table from adapter to enforcer and improve it (#2228)
* feat: improve policiy table

* feat: add connection test in AdapterEditPage.js

* feat: update button style
2023-08-18 19:00:21 +08:00
d12117324c feat: support admin to enable MFA for other users (#2221)
* feat: support admin enable user sms and email mfa

* chore: update ci

* chore: update ci
2023-08-17 17:19:24 +08:00
1a6c9fbf69 Fix typo in README 2023-08-17 14:47:09 +08:00
dd60d79af9 Fix typo in README 2023-08-17 14:46:10 +08:00
73d314c7fe Add MfaTotpPeriodInSeconds param 2023-08-16 21:48:54 +08:00
27959e0f6f fix: fix crash in UserEditPage.js 2023-08-16 15:57:48 +08:00
47f40c5b24 feat: support 3 more UI languages (#2218)
Signed-off-by: baihhh <2542274498@qq.com>
2023-08-16 15:54:34 +08:00
2ff9020884 feat: support Stripe payment provider (#2204)
* feat: add stripe payment provider

* feat: support stripe payment

* feat: delete todo comment

* feat: remove description struct

* feat: change outOrderId->orderId
2023-08-15 00:16:30 +08:00
abaf4ca8d9 Make GetDashboard() faster 2023-08-14 15:43:09 +08:00
8ff0cfd6ec feat: support dashboard in homepage (#2207)
* feat: support dashboard

* feat: support dashboard
2023-08-14 15:31:29 +08:00
7a2a40edcc Improve table columns 2023-08-14 12:19:02 +08:00
b7a001ea39 Fix property empty issue 2023-08-14 12:09:50 +08:00
891e8e21d8 feat: support Web3-Onboard provider (#2209)
* feat: add Web3-Onboard idp

* feat: update Web3-Onboard logo

* feat: update package.json

* feat: remove unused package

* feat: add yarn build param --max_old_space_size=4096

* feat: remove log

* feat: add Wallet configure

* feat: remove hardware wallets
2023-08-13 23:58:57 +08:00
80b0d26813 fix: synchronize update the syncers (#2201)
Signed-off-by: baihhh <2542274498@qq.com>
2023-08-13 22:30:57 +08:00
db4ac60bb6 feat: fix LDAP mobile field incorrect mapped (#2206) 2023-08-12 13:45:26 +08:00
33a922f026 Add custom HTTP SMS provider 2023-08-12 12:52:53 +08:00
9f65053d04 Improve i18n 2023-08-12 02:44:38 +08:00
be969e5efa Fix typo 2023-08-11 22:18:35 +08:00
9156bd426b ci: Show provider.displayName in signin button 2023-08-11 16:29:52 +08:00
fe4a4328aa feat: refactor code in InitApi() 2023-08-11 16:17:29 +08:00
9899022bcd fix: check enforcer should not be nil (#2199)
* fix: check enforcer should not be nil

* fix: check enforcer should not be nil

* Update user.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-11 12:31:49 +08:00
1a9d02be46 feat: use the casbin model to store relationships between users and groups (#2178)
* fix:reslove conflict

* fix: remove interface
2023-08-11 10:59:18 +08:00
eafaa135b4 Change builtInAvailableField back to 5 2023-08-11 02:45:11 +08:00
6746551447 Improve error message in InitEnforcer() 2023-08-11 02:36:29 +08:00
3cb46c3628 Add isKey to syncer's table 2023-08-09 00:33:04 +08:00
558bcf95d6 feat: save policy in adapter edit page (#2190)
* fix: save policy in adapter

* fix: disable edit for builtin adapter
2023-08-09 00:12:53 +08:00
bb937c30c1 Fix empty cert in getPaymentProvider() 2023-08-08 22:37:48 +08:00
8dfdf7f767 ci: add GoogleCloud and QiNiu in Storage (#2188)
* feat: add GoogleCloud and QiNiu in Storage

Signed-off-by: baihhh <2542274498@qq.com>

* Update qiniu_cloud.go

* Update storage.go

---------

Signed-off-by: baihhh <2542274498@qq.com>
Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-08 22:34:55 +08:00
62b2082e82 Add getUserOrganization() to user edit page 2023-08-08 21:58:27 +08:00
a1806439f8 Add UserPrincipalName and MemberOf to get-ldap-users API 2023-08-08 20:18:47 +08:00
104 changed files with 10093 additions and 991 deletions

View File

@ -110,7 +110,7 @@ jobs:
with:
start: yarn start
wait-on: 'http://localhost:7001'
wait-on-timeout: 180
wait-on-timeout: 210
working-directory: ./web
- uses: actions/upload-artifact@v3

View File

@ -11,7 +11,7 @@
<img alt="GitHub Workflow Status (branch)" src="https://github.com/casdoor/casdoor/workflows/Build/badge.svg?style=flat-square">
</a>
<a href="https://github.com/casdoor/casdoor/releases/latest">
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/casbin/casdoor.svg">
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/casdoor/casdoor.svg">
</a>
<a href="https://hub.docker.com/repository/docker/casbin/casdoor">
<img alt="Docker Image Version (latest semver)" src="https://img.shields.io/badge/Docker%20Hub-latest-brightgreen">
@ -23,16 +23,16 @@
<img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/casdoor/casdoor?style=flat-square">
</a>
<a href="https://github.com/casdoor/casdoor/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/casbin/casdoor?style=flat-square" alt="license">
<img src="https://img.shields.io/github/license/casdoor/casdoor?style=flat-square" alt="license">
</a>
<a href="https://github.com/casdoor/casdoor/issues">
<img alt="GitHub issues" src="https://img.shields.io/github/issues/casbin/casdoor?style=flat-square">
<img alt="GitHub issues" src="https://img.shields.io/github/issues/casdoor/casdoor?style=flat-square">
</a>
<a href="#">
<img alt="GitHub stars" src="https://img.shields.io/github/stars/casbin/casdoor?style=flat-square">
<img alt="GitHub stars" src="https://img.shields.io/github/stars/casdoor/casdoor?style=flat-square">
</a>
<a href="https://github.com/casdoor/casdoor/network">
<img alt="GitHub forks" src="https://img.shields.io/github/forks/casbin/casdoor?style=flat-square">
<img alt="GitHub forks" src="https://img.shields.io/github/forks/casdoor/casdoor?style=flat-square">
</a>
<a href="https://crowdin.com/project/casdoor-site">
<img alt="Crowdin" src="https://badges.crowdin.net/casdoor-site/localized.svg">

View File

@ -27,13 +27,7 @@ import (
var Enforcer *casbin.Enforcer
func InitApi() {
var err error
e, err := object.GetEnforcer(util.GetId("built-in", "api-enforcer-built-in"))
if err != nil {
panic(err)
}
err = e.InitEnforcer()
e, err := object.GetInitializedEnforcer(util.GetId("built-in", "api-enforcer-built-in"))
if err != nil {
panic(err)
}

View File

@ -20,7 +20,6 @@ import (
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
)
// GetAdapters
@ -144,92 +143,3 @@ func (c *ApiController) DeleteAdapter() {
c.Data["json"] = wrapActionResponse(object.DeleteAdapter(&adapter))
c.ServeJSON()
}
func (c *ApiController) GetPolicies() {
id := c.Input().Get("id")
adapter, err := object.GetAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
policies, err := object.GetPolicies(adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(policies)
}
func (c *ApiController) UpdatePolicy() {
id := c.Input().Get("id")
adapter, err := object.GetAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
var policies []xormadapter.CasbinRule
err = json.Unmarshal(c.Ctx.Input.RequestBody, &policies)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.UpdatePolicy(util.CasbinToSlice(policies[0]), util.CasbinToSlice(policies[1]), adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}
func (c *ApiController) AddPolicy() {
id := c.Input().Get("id")
adapter, err := object.GetAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
var policy xormadapter.CasbinRule
err = json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.AddPolicy(util.CasbinToSlice(policy), adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}
func (c *ApiController) RemovePolicy() {
id := c.Input().Get("id")
adapter, err := object.GetAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
var policy xormadapter.CasbinRule
err = json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.RemovePolicy(util.CasbinToSlice(policy), adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}

View File

@ -45,13 +45,7 @@ func (c *ApiController) Enforce() {
}
if enforcerId != "" {
enforcer, err := object.GetEnforcer(enforcerId)
if err != nil {
c.ResponseError(err.Error())
return
}
err = enforcer.InitEnforcer()
enforcer, err := object.GetInitializedEnforcer(enforcerId)
if err != nil {
c.ResponseError(err.Error())
return
@ -155,13 +149,7 @@ func (c *ApiController) BatchEnforce() {
}
if enforcerId != "" {
enforcer, err := object.GetEnforcer(enforcerId)
if err != nil {
c.ResponseError(err.Error())
return
}
err = enforcer.InitEnforcer()
enforcer, err := object.GetInitializedEnforcer(enforcerId)
if err != nil {
c.ResponseError(err.Error())
return

View File

@ -20,6 +20,7 @@ import (
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
)
// GetEnforcers
@ -74,6 +75,7 @@ func (c *ApiController) GetEnforcers() {
// @router /get-enforcer [get]
func (c *ApiController) GetEnforcer() {
id := c.Input().Get("id")
loadModelCfg := c.Input().Get("loadModelCfg")
enforcer, err := object.GetEnforcer(id)
if err != nil {
@ -81,6 +83,13 @@ func (c *ApiController) GetEnforcer() {
return
}
if loadModelCfg == "true" {
err := enforcer.LoadModelCfg()
if err != nil {
return
}
}
c.ResponseOk(enforcer)
}
@ -143,3 +152,88 @@ func (c *ApiController) DeleteEnforcer() {
c.Data["json"] = wrapActionResponse(object.DeleteEnforcer(&enforcer))
c.ServeJSON()
}
func (c *ApiController) GetPolicies() {
id := c.Input().Get("id")
adapterId := c.Input().Get("adapterId")
if adapterId != "" {
adapter, err := object.GetAdapter(adapterId)
if err != nil {
c.ResponseError(err.Error())
return
}
err = adapter.InitAdapter()
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk()
return
}
policies, err := object.GetPolicies(id)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(policies)
}
func (c *ApiController) UpdatePolicy() {
id := c.Input().Get("id")
var policies []xormadapter.CasbinRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policies)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.UpdatePolicy(id, util.CasbinToSlice(policies[0]), util.CasbinToSlice(policies[1]))
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}
func (c *ApiController) AddPolicy() {
id := c.Input().Get("id")
var policy xormadapter.CasbinRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.AddPolicy(id, util.CasbinToSlice(policy))
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}
func (c *ApiController) RemovePolicy() {
id := c.Input().Get("id")
var policy xormadapter.CasbinRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.RemovePolicy(id, util.CasbinToSlice(policy))
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}

View File

@ -0,0 +1,33 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controllers
import "github.com/casdoor/casdoor/object"
// GetDashboard
// @Title GetDashboard
// @Tag GetDashboard API
// @Description get information of dashboard
// @Success 200 {object} controllers.Response The Response object
// @router /get-dashboard [get]
func (c *ApiController) GetDashboard() {
data, err := object.GetDashboard()
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(data)
}

View File

@ -361,6 +361,9 @@ func (c *ApiController) UploadResource() {
return
}
if user.Properties == nil {
user.Properties = map[string]string{}
}
user.Properties[tag] = fileUrl
user.Properties["isIdCardVerified"] = "false"
_, err = object.UpdateUser(user.GetId(), user, []string{"properties"}, false)

View File

@ -140,10 +140,12 @@ func (c *ApiController) SendSms() {
return
}
invalidReceivers := getInvalidSmsReceivers(smsForm)
if len(invalidReceivers) != 0 {
c.ResponseError(fmt.Sprintf(c.T("service:Invalid phone receivers: %s"), strings.Join(invalidReceivers, ", ")))
return
if provider.Type != "Custom HTTP SMS" {
invalidReceivers := getInvalidSmsReceivers(smsForm)
if len(invalidReceivers) != 0 {
c.ResponseError(fmt.Sprintf(c.T("service:Invalid phone receivers: %s"), strings.Join(invalidReceivers, ", ")))
return
}
}
err = object.SendSms(provider, smsForm.Content, smsForm.Receivers...)

View File

@ -90,7 +90,7 @@ func (c *ApiController) GetUsers() {
if limit == "" || page == "" {
if groupName != "" {
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupName))
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(util.GetId(owner, groupName)))
if err != nil {
c.ResponseError(err.Error())
return
@ -567,6 +567,22 @@ func (c *ApiController) RemoveUserFromGroup() {
name := c.Ctx.Request.Form.Get("name")
groupName := c.Ctx.Request.Form.Get("groupName")
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, util.GetId(owner, groupName)))
c.ServeJSON()
organization, err := object.GetOrganization(util.GetId("admin", owner))
if err != nil {
return
}
item := object.GetAccountItemByName("Groups", organization)
res, msg := object.CheckAccountItemModifyRule(item, c.IsAdmin(), c.GetAcceptLanguage())
if !res {
c.ResponseError(msg)
return
}
affected, err := object.DeleteGroupForUser(util.GetId(owner, name), groupName)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(affected)
}

19
go.mod
View File

@ -14,7 +14,7 @@ require (
github.com/casbin/casbin/v2 v2.30.1
github.com/casdoor/go-sms-sender v0.12.0
github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/oss v1.2.1
github.com/casdoor/oss v1.3.0
github.com/casdoor/xorm-adapter/v3 v3.0.4
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
github.com/denisenkom/go-mssqldb v0.9.0
@ -28,9 +28,6 @@ require (
github.com/go-sql-driver/mysql v1.6.0
github.com/go-webauthn/webauthn v0.6.0
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.7.3 // indirect
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
@ -43,7 +40,7 @@ require (
github.com/nyaruka/phonenumbers v1.1.5
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.11.1
github.com/prometheus/client_model v0.2.0
github.com/prometheus/client_model v0.3.0
github.com/qiangmzsx/string-adapter/v2 v2.1.0
github.com/robfig/cron/v3 v3.0.1
github.com/russellhaering/gosaml2 v0.9.0
@ -53,7 +50,8 @@ require (
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stretchr/testify v1.8.2
github.com/stretchr/testify v1.8.3
github.com/stripe/stripe-go/v74 v74.29.0 // indirect
github.com/tealeg/xlsx v1.0.5
github.com/thanhpk/randstr v1.0.4
github.com/tklauser/go-sysconf v0.3.10 // indirect
@ -61,12 +59,11 @@ require (
github.com/xorm-io/core v0.7.4
github.com/xorm-io/xorm v1.1.6
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.6.0
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/net v0.7.0
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
golang.org/x/crypto v0.11.0
golang.org/x/net v0.13.0
golang.org/x/oauth2 v0.10.0
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v2 v2.4.0 // indirect
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84
modernc.org/sqlite v1.18.2
)

1405
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,9 @@ func TestGenerateI18nFrontend(t *testing.T) {
applyToOtherLanguage("frontend", "it", data)
applyToOtherLanguage("frontend", "ms", data)
applyToOtherLanguage("frontend", "tr", data)
applyToOtherLanguage("frontend", "ar", data)
applyToOtherLanguage("frontend", "he", data)
applyToOtherLanguage("frontend", "fi", data)
}
func TestGenerateI18nBackend(t *testing.T) {
@ -55,4 +58,7 @@ func TestGenerateI18nBackend(t *testing.T) {
applyToOtherLanguage("backend", "it", data)
applyToOtherLanguage("backend", "ms", data)
applyToOtherLanguage("backend", "tr", data)
applyToOtherLanguage("backend", "ar", data)
applyToOtherLanguage("backend", "he", data)
applyToOtherLanguage("backend", "fi", data)
}

142
i18n/locales/ar/data.json Normal file
View File

@ -0,0 +1,142 @@
{
"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"
},
"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 password is not enabled for the application": "The login method: login with password is not enabled for the application",
"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"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"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.",
"FirstName cannot be blank": "FirstName cannot be blank",
"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",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"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.",
"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"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"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"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"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"
},
"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."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"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"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not 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"
},
"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"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"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"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space."
},
"user_upload": {
"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"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Invalid captcha provider.": "Invalid captcha provider.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"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!",
"the user does not exist, please sign up first": "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"
}
}

142
i18n/locales/fi/data.json Normal file
View File

@ -0,0 +1,142 @@
{
"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"
},
"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 password is not enabled for the application": "The login method: login with password is not enabled for the application",
"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"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"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.",
"FirstName cannot be blank": "FirstName cannot be blank",
"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",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"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.",
"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"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"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"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"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"
},
"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."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"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"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not 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"
},
"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"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"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"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space."
},
"user_upload": {
"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"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Invalid captcha provider.": "Invalid captcha provider.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"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!",
"the user does not exist, please sign up first": "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"
}
}

142
i18n/locales/he/data.json Normal file
View File

@ -0,0 +1,142 @@
{
"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"
},
"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 password is not enabled for the application": "The login method: login with password is not enabled for the application",
"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"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"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.",
"FirstName cannot be blank": "FirstName cannot be blank",
"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",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"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.",
"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"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"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"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"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"
},
"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."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"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"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not 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"
},
"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"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"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"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space."
},
"user_upload": {
"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"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Invalid captcha provider.": "Invalid captcha provider.",
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"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!",
"the user does not exist, please sign up first": "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"
}
}

View File

@ -24,20 +24,10 @@ import (
"golang.org/x/oauth2"
)
const Web3AuthTokenKey = "web3AuthToken"
type MetaMaskIdProvider struct {
Client *http.Client
}
type Web3AuthToken struct {
Address string `json:"address"`
Nonce string `json:"nonce"`
CreateAt uint64 `json:"createAt"`
TypedData string `json:"typedData"`
Signature string `json:"signature"` // signature for typed data
}
func NewMetaMaskIdProvider() *MetaMaskIdProvider {
idp := &MetaMaskIdProvider{}
return idp

View File

@ -111,6 +111,8 @@ func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) IdProvider {
return NewBilibiliIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
case "MetaMask":
return NewMetaMaskIdProvider()
case "Web3Onboard":
return NewWeb3OnboardIdProvider()
default:
if isGothSupport(idpInfo.Type) {
return NewGothIdProvider(idpInfo.Type, idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)

103
idp/web3onboard.go Normal file
View File

@ -0,0 +1,103 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package idp
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"time"
"golang.org/x/oauth2"
)
const Web3AuthTokenKey = "web3AuthToken"
type Web3AuthToken struct {
Address string `json:"address"`
Nonce string `json:"nonce"`
CreateAt uint64 `json:"createAt"`
TypedData string `json:"typedData"` // typed data use for application
Signature string `json:"signature"` // signature for typed data
WalletType string `json:"walletType"` // e.g."MetaMask", "Coinbase"
}
type Web3OnboardIdProvider struct {
Client *http.Client
}
func NewWeb3OnboardIdProvider() *Web3OnboardIdProvider {
idp := &Web3OnboardIdProvider{}
return idp
}
func (idp *Web3OnboardIdProvider) SetHttpClient(client *http.Client) {
idp.Client = client
}
func (idp *Web3OnboardIdProvider) GetToken(code string) (*oauth2.Token, error) {
web3AuthToken := Web3AuthToken{}
if err := json.Unmarshal([]byte(code), &web3AuthToken); err != nil {
return nil, err
}
token := &oauth2.Token{
AccessToken: fmt.Sprintf("%v:%v", Web3AuthTokenKey, web3AuthToken.Address),
TokenType: "Bearer",
Expiry: time.Now().AddDate(0, 1, 0),
}
token = token.WithExtra(map[string]interface{}{
Web3AuthTokenKey: web3AuthToken,
})
return token, nil
}
func (idp *Web3OnboardIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
web3AuthToken, ok := token.Extra(Web3AuthTokenKey).(Web3AuthToken)
if !ok {
return nil, errors.New("invalid web3AuthToken")
}
fmtAddress := fmt.Sprintf("%v_%v",
strings.ReplaceAll(strings.TrimSpace(web3AuthToken.WalletType), " ", "_"),
web3AuthToken.Address,
)
userInfo := &UserInfo{
Id: fmtAddress,
Username: fmtAddress,
DisplayName: fmtAddress,
AvatarUrl: fmt.Sprintf("metamask:%v", forceEthereumAddress(web3AuthToken.Address)),
}
return userInfo, nil
}
func forceEthereumAddress(address string) string {
// The required address to general MetaMask avatar is a string of length 42 that represents an Ethereum address.
// This function is used to force any address as an Ethereum address
address = strings.TrimSpace(address)
var builder strings.Builder
for _, ch := range address {
builder.WriteRune(ch)
}
for len(builder.String()) < 42 {
builder.WriteString("0")
}
if len(builder.String()) > 42 {
return builder.String()[:42]
}
return builder.String()
}

View File

@ -9,11 +9,11 @@
"passwordType": "plain",
"passwordSalt": "",
"passwordOptions": ["AtLeast6"],
"countryCodes": ["US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN", "IT", "MY", "TR"],
"countryCodes": ["US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN", "IT", "MY", "TR", "DZ", "IL", "PH"],
"defaultAvatar": "",
"defaultApplication": "",
"tags": [],
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "it", "ms", "tr"],
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "it", "ms", "tr","ar", "he", "fi"],
"masterPassword": "",
"initScore": 2000,
"enableSoftDeletion": false,

View File

@ -49,6 +49,7 @@ func main() {
object.InitLdapAutoSynchronizer()
proxy.InitHttpClient()
authz.InitApi()
object.InitUserManager()
util.SafeGoroutine(func() { object.RunSyncUsersJob() })

View File

@ -18,11 +18,11 @@ import (
"fmt"
"strings"
"github.com/casbin/casbin/v2/model"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
"github.com/xorm-io/core"
"github.com/xorm-io/xorm"
)
type Adapter struct {
@ -40,8 +40,6 @@ type Adapter struct {
Table string `xorm:"varchar(100)" json:"table"`
TableNamePrefix string `xorm:"varchar(100)" json:"tableNamePrefix"`
IsEnabled bool `json:"isEnabled"`
*xormadapter.Adapter `xorm:"-" json:"-"`
}
@ -149,20 +147,23 @@ func (adapter *Adapter) getTable() string {
}
}
func (adapter *Adapter) initAdapter() error {
func (adapter *Adapter) InitAdapter() error {
if adapter.Adapter == nil {
var dataSourceName string
if adapter.builtInAdapter() {
dataSourceName = conf.GetConfigString("dataSourceName")
if adapter.DatabaseType == "mysql" {
dataSourceName = dataSourceName + adapter.Database
}
} else {
switch adapter.DatabaseType {
case "mssql":
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s", adapter.User,
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
case "mysql":
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/", adapter.User,
adapter.Password, adapter.Host, adapter.Port)
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", adapter.User,
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
case "postgres":
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=disable dbname=%s", adapter.User,
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
@ -181,7 +182,8 @@ func (adapter *Adapter) initAdapter() error {
}
var err error
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(adapter.DatabaseType, dataSourceName, adapter.Database).Engine, adapter.getTable(), adapter.TableNamePrefix)
engine, err := xorm.NewEngine(adapter.DatabaseType, dataSourceName)
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(engine, adapter.getTable(), adapter.TableNamePrefix)
if err != nil {
return err
}
@ -209,116 +211,10 @@ func adapterChangeTrigger(oldName string, newName string) error {
return session.Commit()
}
func safeReturn(policy []string, i int) string {
if len(policy) > i {
return policy[i]
} else {
return ""
}
}
func matrixToCasbinRules(Ptype string, policies [][]string) []*xormadapter.CasbinRule {
res := []*xormadapter.CasbinRule{}
for _, policy := range policies {
line := xormadapter.CasbinRule{
Ptype: Ptype,
V0: safeReturn(policy, 0),
V1: safeReturn(policy, 1),
V2: safeReturn(policy, 2),
V3: safeReturn(policy, 3),
V4: safeReturn(policy, 4),
V5: safeReturn(policy, 5),
}
res = append(res, &line)
}
return res
}
func GetPolicies(adapter *Adapter) ([]*xormadapter.CasbinRule, error) {
err := adapter.initAdapter()
if err != nil {
return nil, err
}
casbinModel := getModelDef()
err = adapter.LoadPolicy(casbinModel)
if err != nil {
return nil, err
}
policies := matrixToCasbinRules("p", casbinModel.GetPolicy("p", "p"))
policies = append(policies, matrixToCasbinRules("g", casbinModel.GetPolicy("g", "g"))...)
return policies, nil
}
func UpdatePolicy(oldPolicy, newPolicy []string, adapter *Adapter) (bool, error) {
err := adapter.initAdapter()
if err != nil {
return false, err
}
casbinModel := getModelDef()
err = adapter.LoadPolicy(casbinModel)
if err != nil {
return false, err
}
affected := casbinModel.UpdatePolicy("p", "p", oldPolicy, newPolicy)
if err != nil {
return affected, err
}
return affected, nil
}
func AddPolicy(policy []string, adapter *Adapter) (bool, error) {
err := adapter.initAdapter()
if err != nil {
return false, err
}
casbinModel := getModelDef()
err = adapter.LoadPolicy(casbinModel)
if err != nil {
return false, err
}
casbinModel.AddPolicy("p", "p", policy)
return true, nil
}
func RemovePolicy(policy []string, adapter *Adapter) (bool, error) {
err := adapter.initAdapter()
if err != nil {
return false, err
}
casbinModel := getModelDef()
err = adapter.LoadPolicy(casbinModel)
if err != nil {
return false, err
}
affected := casbinModel.RemovePolicy("p", "p", policy)
if err != nil {
return affected, err
}
return affected, nil
}
func (adapter *Adapter) builtInAdapter() bool {
if adapter.Owner != "built-in" {
return false
}
return adapter.Name == "permission-adapter-built-in" || adapter.Name == "api-adapter-built-in"
}
func getModelDef() model.Model {
casbinModel := model.NewModel()
casbinModel.AddDef("p", "p", "_, _, _, _, _, _")
casbinModel.AddDef("g", "g", "_, _, _, _, _, _")
return casbinModel
return adapter.Name == "user-adapter-built-in" || adapter.Name == "api-adapter-built-in"
}

View File

@ -15,10 +15,12 @@
package object
import (
"errors"
"fmt"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/config"
"github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
"github.com/xorm-io/core"
)
@ -30,10 +32,10 @@ type Enforcer struct {
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Description string `xorm:"varchar(100)" json:"description"`
Model string `xorm:"varchar(100)" json:"model"`
Adapter string `xorm:"varchar(100)" json:"adapter"`
IsEnabled bool `json:"isEnabled"`
Model string `xorm:"varchar(100)" json:"model"`
Adapter string `xorm:"varchar(100)" json:"adapter"`
ModelCfg map[string]string `xorm:"-" json:"modelCfg"`
*casbin.Enforcer
}
@ -120,44 +122,131 @@ func DeleteEnforcer(enforcer *Enforcer) (bool, error) {
return affected != 0, nil
}
func (enforcer *Enforcer) GetId() string {
return fmt.Sprintf("%s/%s", enforcer.Owner, enforcer.Name)
}
func (enforcer *Enforcer) InitEnforcer() error {
if enforcer.Enforcer == nil {
if enforcer == nil {
return errors.New("enforcer is nil")
}
if enforcer.Model == "" || enforcer.Adapter == "" {
return errors.New("missing model or adapter")
}
if enforcer.Enforcer != nil {
return nil
}
var err error
var m *Model
var a *Adapter
if enforcer.Model == "" {
return fmt.Errorf("the model for enforcer: %s should not be empty", enforcer.GetId())
}
if enforcer.Adapter == "" {
return fmt.Errorf("the adapter for enforcer: %s should not be empty", enforcer.GetId())
}
if m, err = GetModel(enforcer.Model); err != nil {
return err
} else if m == nil {
return errors.New("model not found")
}
if a, err = GetAdapter(enforcer.Adapter); err != nil {
return err
} else if a == nil {
return errors.New("adapter not found")
}
m, err := GetModel(enforcer.Model)
if err != nil {
return err
} else if m == nil {
return fmt.Errorf("the model: %s for enforcer: %s is not found", enforcer.Model, enforcer.GetId())
}
err = m.initModel()
if err != nil {
return err
}
err = a.initAdapter()
if err != nil {
return err
}
a, err := GetAdapter(enforcer.Adapter)
if err != nil {
return err
} else if a == nil {
return fmt.Errorf("the adapter: %s for enforcer: %s is not found", enforcer.Adapter, enforcer.GetId())
}
casbinEnforcer, err := casbin.NewEnforcer(m.Model, a.Adapter)
if err != nil {
return err
}
enforcer.Enforcer = casbinEnforcer
err = m.initModel()
if err != nil {
return err
}
err = a.InitAdapter()
if err != nil {
return err
}
casbinEnforcer, err := casbin.NewEnforcer(m.Model, a.Adapter)
if err != nil {
return err
}
enforcer.Enforcer = casbinEnforcer
return nil
}
func GetInitializedEnforcer(enforcerId string) (*Enforcer, error) {
enforcer, err := GetEnforcer(enforcerId)
if err != nil {
return nil, err
} else if enforcer == nil {
return nil, fmt.Errorf("the enforcer: %s is not found", enforcerId)
}
err = enforcer.InitEnforcer()
if err != nil {
return nil, err
}
return enforcer, nil
}
func GetPolicies(id string) ([]*xormadapter.CasbinRule, error) {
enforcer, err := GetInitializedEnforcer(id)
if err != nil {
return nil, err
}
policies := util.MatrixToCasbinRules("p", enforcer.GetPolicy())
if enforcer.GetModel()["g"] != nil {
policies = append(policies, util.MatrixToCasbinRules("g", enforcer.GetGroupingPolicy())...)
}
return policies, nil
}
func UpdatePolicy(id string, oldPolicy, newPolicy []string) (bool, error) {
enforcer, err := GetInitializedEnforcer(id)
if err != nil {
return false, err
}
return enforcer.UpdatePolicy(oldPolicy, newPolicy)
}
func AddPolicy(id string, policy []string) (bool, error) {
enforcer, err := GetInitializedEnforcer(id)
if err != nil {
return false, err
}
return enforcer.AddPolicy(policy)
}
func RemovePolicy(id string, policy []string) (bool, error) {
enforcer, err := GetInitializedEnforcer(id)
if err != nil {
return false, err
}
return enforcer.RemovePolicy(policy)
}
func (enforcer *Enforcer) LoadModelCfg() error {
if enforcer.ModelCfg != nil {
return nil
}
model, err := GetModel(enforcer.Model)
if err != nil {
return err
} else if model == nil {
return fmt.Errorf("the model: %s for enforcer: %s is not found", enforcer.Model, enforcer.GetId())
}
cfg, err := config.NewConfigFromText(model.ModelText)
if err != nil {
return err
}
enforcer.ModelCfg = make(map[string]string)
enforcer.ModelCfg["p"] = cfg.String("policy_definition::p")
if cfg.String("role_definition::g") != "" {
enforcer.ModelCfg["g"] = cfg.String("role_definition::g")
}
return nil

140
object/get-dashboard.go Normal file
View File

@ -0,0 +1,140 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"sync"
"time"
)
type Dashboard struct {
OrganizationCounts []int `json:"organizationCounts"`
UserCounts []int `json:"userCounts"`
ProviderCounts []int `json:"providerCounts"`
ApplicationCounts []int `json:"applicationCounts"`
SubscriptionCounts []int `json:"subscriptionCounts"`
}
func GetDashboard() (*Dashboard, error) {
dashboard := &Dashboard{
OrganizationCounts: make([]int, 31),
UserCounts: make([]int, 31),
ProviderCounts: make([]int, 31),
ApplicationCounts: make([]int, 31),
SubscriptionCounts: make([]int, 31),
}
var wg sync.WaitGroup
organizations := []Organization{}
users := []User{}
providers := []Provider{}
applications := []Application{}
subscriptions := []Subscription{}
wg.Add(5)
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&organizations); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&users); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&providers); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&applications); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&subscriptions); err != nil {
panic(err)
}
}()
wg.Wait()
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)
}
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++
}
}
}
return count
}

View File

@ -214,30 +214,18 @@ func ConvertToTreeData(groups []*Group, parentId string) []*Group {
return treeData
}
func RemoveUserFromGroup(owner, name, groupId string) (bool, error) {
user, err := getUser(owner, name)
if err != nil {
return false, err
}
if user == nil {
return false, errors.New("user not exist")
}
user.Groups = util.DeleteVal(user.Groups, groupId)
affected, err := updateUser(user.GetId(), user, []string{"groups"})
if err != nil {
return false, err
}
return affected != 0, err
}
func GetGroupUserCount(groupId string, field, value string) (int64, error) {
owner, _ := util.GetOwnerAndNameFromId(groupId)
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
if err != nil {
return 0, err
}
if field == "" && value == "" {
return ormer.Engine.Where(builder.Like{"`groups`", groupId}).
Count(&User{})
return int64(len(names)), nil
} else {
return ormer.Engine.Table("user").
Where(builder.Like{"`groups`", groupId}).
Where("owner = ?", owner).In("name", names).
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
Count()
}
@ -245,8 +233,14 @@ func GetGroupUserCount(groupId string, field, value string) (int64, error) {
func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
users := []*User{}
owner, _ := util.GetOwnerAndNameFromId(groupId)
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
if err != nil {
return nil, err
}
session := ormer.Engine.Table("user").
Where(builder.Like{"`groups`", groupId + "\""})
Where("owner = ?", owner).In("name", names)
if offset != -1 && limit != -1 {
session.Limit(limit, offset)
@ -265,7 +259,7 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
}
err := session.Find(&users)
err = session.Find(&users)
if err != nil {
return nil, err
}
@ -275,13 +269,13 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
func GetGroupUsers(groupId string) ([]*User, error) {
users := []*User{}
err := ormer.Engine.Table("user").
Where(builder.Like{"`groups`", groupId + "\""}).
Find(&users)
owner, _ := util.GetOwnerAndNameFromId(groupId)
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
err = ormer.Engine.Where("owner = ?", owner).In("name", names).Find(&users)
if err != nil {
return nil, err
}
return users, nil
}

View File

@ -318,7 +318,6 @@ func initBuiltInUserModel() {
Name: "user-model-built-in",
CreatedTime: util.GetCurrentTime(),
DisplayName: "Built-in Model",
IsEnabled: true,
ModelText: `[request_definition]
r = sub, obj, act
@ -376,7 +375,6 @@ m = (r.subOwner == p.subOwner || p.subOwner == "*") && \
Name: "api-model-built-in",
CreatedTime: util.GetCurrentTime(),
DisplayName: "API Model",
IsEnabled: true,
ModelText: modelText,
}
_, err = AddModel(model)
@ -435,7 +433,6 @@ func initBuiltInUserAdapter() {
TableNamePrefix: conf.GetConfigString("tableNamePrefix"),
Database: conf.GetConfigString("dbName"),
Table: "casbin_user_rule",
IsEnabled: true,
}
_, err = AddAdapter(adapter)
if err != nil {
@ -462,7 +459,6 @@ func initBuiltInApiAdapter() {
TableNamePrefix: conf.GetConfigString("tableNamePrefix"),
Database: conf.GetConfigString("dbName"),
Table: "casbin_api_rule",
IsEnabled: true,
}
_, err = AddAdapter(adapter)
if err != nil {
@ -484,10 +480,9 @@ func initBuiltInUserEnforcer() {
Owner: "built-in",
Name: "user-enforcer-built-in",
CreatedTime: util.GetCurrentTime(),
DisplayName: "Permission Enforcer",
DisplayName: "User Enforcer",
Model: "built-in/user-model-built-in",
Adapter: "built-in/user-adapter-built-in",
IsEnabled: true,
}
_, err = AddEnforcer(enforcer)
@ -513,7 +508,6 @@ func initBuiltInApiEnforcer() {
DisplayName: "API Enforcer",
Model: "built-in/api-model-built-in",
Adapter: "built-in/api-adapter-built-in",
IsEnabled: true,
}
_, err = AddEnforcer(enforcer)

View File

@ -41,19 +41,20 @@ type LdapUser struct {
GidNumber string `json:"gidNumber"`
// Gcn string
Uuid string `json:"uuid"`
UserPrincipalName string `json:"userPrincipalName"`
DisplayName string `json:"displayName"`
Mail string
Email string `json:"email"`
EmailAddress string
TelephoneNumber string
Mobile string
Mobile string `json:"mobile"`
MobileTelephoneNumber string
RegisteredAddress string
PostalAddress string
GroupId string `json:"groupId"`
Phone string `json:"phone"`
Address string `json:"address"`
GroupId string `json:"groupId"`
Address string `json:"address"`
MemberOf string `json:"memberOf"`
}
func (ldap *Ldap) GetLdapConn() (c *LdapConn, err error) {
@ -168,6 +169,8 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
user.Uuid = attribute.Values[0]
case "objectGUID":
user.Uuid = attribute.Values[0]
case "userPrincipalName":
user.UserPrincipalName = attribute.Values[0]
case "displayName":
user.DisplayName = attribute.Values[0]
case "mail":
@ -186,6 +189,8 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
user.RegisteredAddress = attribute.Values[0]
case "postalAddress":
user.PostalAddress = attribute.Values[0]
case "memberOf":
user.MemberOf = attribute.Values[0]
}
}
ldapUsers = append(ldapUsers, user)
@ -312,7 +317,7 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
DisplayName: syncUser.buildLdapDisplayName(),
Avatar: organization.DefaultAvatar,
Email: syncUser.Email,
Phone: syncUser.Phone,
Phone: syncUser.Mobile,
Address: []string{syncUser.Address},
Affiliation: affiliation,
Tag: tag,

View File

@ -17,6 +17,7 @@ package object
import (
"errors"
"fmt"
"time"
"github.com/beego/beego"
"github.com/beego/beego/context"
@ -25,7 +26,10 @@ import (
"github.com/pquerna/otp/totp"
)
const MfaTotpSecretSession = "mfa_totp_secret"
const (
MfaTotpSecretSession = "mfa_totp_secret"
MfaTotpPeriodInSeconds = 30
)
type TotpMfa struct {
Config *MfaProps
@ -76,7 +80,13 @@ func (mfa *TotpMfa) SetupVerify(ctx *context.Context, passcode string) error {
if secret == nil {
return errors.New("totp secret is missing")
}
result := totp.Validate(passcode, secret.(string))
result, _ := totp.ValidateCustom(passcode, secret.(string), time.Now().UTC(), totp.ValidateOpts{
Period: MfaTotpPeriodInSeconds,
Skew: 1,
Digits: otp.DigitsSix,
Algorithm: otp.AlgorithmSHA1,
})
if result {
return nil
@ -133,7 +143,7 @@ func NewTotpMfaUtil(config *MfaProps) *TotpMfa {
return &TotpMfa{
Config: config,
period: 30,
period: MfaTotpPeriodInSeconds,
secretSize: 20,
digits: otp.DigitsSix,
}

View File

@ -30,7 +30,6 @@ type Model struct {
Description string `xorm:"varchar(100)" json:"description"`
ModelText string `xorm:"mediumtext" json:"modelText"`
IsEnabled bool `json:"isEnabled"`
model.Model `xorm:"-" json:"-"`
}

View File

@ -58,7 +58,7 @@ type PermissionRule struct {
Id string `xorm:"varchar(100) index not null default ''" json:"id"`
}
const builtInAvailableField = 10
const builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
func (p *Permission) GetId() string {
return util.GetId(p.Owner, p.Name)

View File

@ -309,8 +309,7 @@ func GetAllRoles(userId string) []string {
func GetBuiltInModel(modelText string) (model.Model, error) {
if modelText == "" {
modelText = `
[request_definition]
modelText = `[request_definition]
r = sub, obj, act
[policy_definition]

View File

@ -36,7 +36,7 @@ type Provider struct {
Type string `xorm:"varchar(100)" json:"type"`
SubType string `xorm:"varchar(100)" json:"subType"`
Method string `xorm:"varchar(100)" json:"method"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientId string `xorm:"varchar(200)" json:"clientId"`
ClientSecret string `xorm:"varchar(2000)" json:"clientSecret"`
ClientId2 string `xorm:"varchar(100)" json:"clientId2"`
ClientSecret2 string `xorm:"varchar(100)" json:"clientSecret2"`
@ -254,7 +254,8 @@ func DeleteProvider(provider *Provider) (bool, error) {
func (p *Provider) getPaymentProvider() (pp.PaymentProvider, *Cert, error) {
cert := &Cert{}
if p.Cert != "" {
cert, err := getCert(p.Owner, p.Cert)
var err error
cert, err = getCert(p.Owner, p.Cert)
if err != nil {
return nil, nil, err
}

View File

@ -26,6 +26,8 @@ func getSmsClient(provider *Provider) (sender.SmsClient, error) {
if provider.Type == sender.HuaweiCloud || provider.Type == sender.AzureACS {
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.ProviderUrl, provider.AppId)
} else if provider.Type == "Custom HTTP SMS" {
client, err = newHttpSmsClient(provider.Endpoint, provider.Method, provider.ClientId, provider.Title)
} else {
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId)
}

75
object/sms_custom.go Normal file
View File

@ -0,0 +1,75 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"bytes"
"fmt"
"net/http"
"github.com/casdoor/casdoor/proxy"
)
type HttpSmsClient struct {
endpoint string
method string
paramName string
text string
}
func newHttpSmsClient(endpoint string, method string, paramName string, text string) (*HttpSmsClient, error) {
client := &HttpSmsClient{
endpoint: endpoint,
method: method,
paramName: paramName,
text: text,
}
return client, nil
}
func (c *HttpSmsClient) SendMessage(param map[string]string, targetPhoneNumber ...string) error {
var err error
content := param["code"]
httpClient := proxy.DefaultHttpClient
req, err := http.NewRequest(c.method, c.endpoint, bytes.NewBufferString(content))
if err != nil {
return err
}
if c.method == "POST" {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.PostForm = map[string][]string{
c.paramName: {content},
}
} else if c.method == "GET" {
q := req.URL.Query()
q.Add(c.paramName, content)
req.URL.RawQuery = q.Encode()
}
resp, err := httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("SendMessage() error, custom HTTP SMS request failed with status: %s", resp.Status)
}
return err
}

View File

@ -25,6 +25,7 @@ type TableColumn struct {
Name string `json:"name"`
Type string `json:"type"`
CasdoorName string `json:"casdoorName"`
IsKey bool `json:"isKey"`
IsHashed bool `json:"isHashed"`
Values []string `json:"values"`
}
@ -44,7 +45,6 @@ type Syncer struct {
DatabaseType string `xorm:"varchar(100)" json:"databaseType"`
Database string `xorm:"varchar(100)" json:"database"`
Table string `xorm:"varchar(100)" json:"table"`
TablePrimaryKey string `xorm:"varchar(100)" json:"tablePrimaryKey"`
TableColumns []*TableColumn `xorm:"mediumtext" json:"tableColumns"`
AffiliationTable string `xorm:"varchar(100)" json:"affiliationTable"`
AvatarBaseUrl string `xorm:"varchar(100)" json:"avatarBaseUrl"`
@ -229,6 +229,27 @@ func (syncer *Syncer) getTable() string {
}
}
func (syncer *Syncer) getKey() string {
key := "id"
hasKey := false
hasId := false
for _, tableColumn := range syncer.TableColumns {
if tableColumn.IsKey {
hasKey = true
key = tableColumn.Name
}
if tableColumn.Name == "id" {
hasId = true
}
}
if !hasKey && !hasId {
key = syncer.TableColumns[0].Name
}
return key
}
func RunSyncer(syncer *Syncer) {
syncer.initAdapter()
syncer.syncUsers()

View File

@ -20,16 +20,24 @@ import (
)
func (syncer *Syncer) syncUsers() {
if len(syncer.TableColumns) == 0 {
return
}
fmt.Printf("Running syncUsers()..\n")
users, userMap, userNameMap := syncer.getUserMap()
users, _, _ := syncer.getUserMap()
oUsers, oUserMap, err := syncer.getOriginalUserMap()
if err != nil {
fmt.Printf(err.Error())
timestamp := time.Now().Format("2006-01-02 15:04:05")
line := fmt.Sprintf("[%s] %s\n", timestamp, err.Error())
updateSyncerErrorText(syncer, line)
_, err = updateSyncerErrorText(syncer, line)
if err != nil {
panic(err)
}
return
}
@ -40,48 +48,68 @@ func (syncer *Syncer) syncUsers() {
_, affiliationMap, err = syncer.getAffiliationMap()
}
key := syncer.getKey()
myUsers := map[string]*User{}
for _, m := range users {
myUsers[syncer.getUserValue(m, key)] = m
}
newUsers := []*User{}
for _, oUser := range oUsers {
id := oUser.Id
if _, ok := userMap[id]; !ok {
if _, ok := userNameMap[oUser.Name]; !ok {
newUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
fmt.Printf("New user: %v\n", newUser)
newUsers = append(newUsers, newUser)
}
} else {
user := userMap[id]
oHash := syncer.calculateHash(oUser)
primary := syncer.getUserValue(oUser, key)
if _, ok := myUsers[primary]; !ok {
newUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
fmt.Printf("New user: %v\n", newUser)
newUsers = append(newUsers, newUser)
} else {
user := myUsers[primary]
oHash := syncer.calculateHash(oUser)
if user.Hash == user.PreHash {
if user.Hash != oHash {
updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
updatedUser.Hash = oHash
updatedUser.PreHash = oHash
syncer.updateUserForOriginalFields(updatedUser)
_, err = syncer.updateUserForOriginalByFields(updatedUser, key)
if err != nil {
panic(err)
}
fmt.Printf("Update from oUser to user: %v\n", updatedUser)
}
} else {
if user.PreHash == oHash {
if !syncer.IsReadOnly {
updatedOUser := syncer.createOriginalUserFromUser(user)
syncer.updateUser(updatedOUser)
_, err = syncer.updateUser(updatedOUser)
if err != nil {
panic(err)
}
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
}
// update preHash
user.PreHash = user.Hash
SetUserField(user, "pre_hash", user.PreHash)
_, err = SetUserField(user, "pre_hash", user.PreHash)
if err != nil {
panic(err)
}
} else {
if user.Hash == oHash {
// update preHash
user.PreHash = user.Hash
SetUserField(user, "pre_hash", user.PreHash)
_, err = SetUserField(user, "pre_hash", user.PreHash)
if err != nil {
panic(err)
}
} else {
updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
updatedUser.Hash = oHash
updatedUser.PreHash = oHash
syncer.updateUserForOriginalFields(updatedUser)
_, err = syncer.updateUserForOriginalByFields(updatedUser, key)
if err != nil {
panic(err)
}
fmt.Printf("Update from oUser to user (2nd condition): %v\n", updatedUser)
}
}

View File

@ -80,16 +80,6 @@ func (syncer *Syncer) addUser(user *OriginalUser) (bool, error) {
return affected != 0, nil
}
/*func (syncer *Syncer) getOriginalColumns() []string {
res := []string{}
for _, tableColumn := range syncer.TableColumns {
if tableColumn.CasdoorName != "Id" {
res = append(res, tableColumn.Name)
}
}
return res
}*/
func (syncer *Syncer) getCasdoorColumns() []string {
res := []string{}
for _, tableColumn := range syncer.TableColumns {
@ -102,12 +92,14 @@ func (syncer *Syncer) getCasdoorColumns() []string {
}
func (syncer *Syncer) updateUser(user *OriginalUser) (bool, error) {
key := syncer.getKey()
m := syncer.getMapFromOriginalUser(user)
pkValue := m[syncer.TablePrimaryKey]
delete(m, syncer.TablePrimaryKey)
pkValue := m[key]
delete(m, key)
setString := syncer.getSqlSetStringFromMap(m)
sql := fmt.Sprintf("update %s set %s where %s = %s", syncer.getTable(), setString, syncer.TablePrimaryKey, pkValue)
sql := fmt.Sprintf("update %s set %s where %s = %s", syncer.getTable(), setString, key, pkValue)
res, err := syncer.Ormer.Engine.Exec(sql)
if err != nil {
return false, err
@ -142,6 +134,34 @@ func (syncer *Syncer) updateUserForOriginalFields(user *User) (bool, error) {
if err != nil {
return false, err
}
return affected != 0, nil
}
func (syncer *Syncer) updateUserForOriginalByFields(user *User, key string) (bool, error) {
var err error
oldUser := User{}
existed, err := ormer.Engine.Where(key+" = ? and owner = ?", syncer.getUserValue(user, key), user.Owner).Get(&oldUser)
if err != nil {
return false, err
}
if !existed {
return false, nil
}
if user.Avatar != oldUser.Avatar && user.Avatar != "" {
user.PermanentAvatar, err = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, true)
if err != nil {
return false, err
}
}
columns := syncer.getCasdoorColumns()
columns = append(columns, "affiliation", "hash", "pre_hash")
affected, err := ormer.Engine.Where(key+" = ? and owner = ?", syncer.getUserValue(&oldUser, key), oldUser.Owner).Cols(columns...).Update(user)
if err != nil {
return false, err
}
return affected != 0, nil
}

View File

@ -17,6 +17,7 @@ package object
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"time"
@ -164,6 +165,33 @@ func (syncer *Syncer) setUserByKeyValue(user *User, key string, value string) {
}
}
func (syncer *Syncer) getUserValue(user *User, key string) string {
jsonData, _ := json.Marshal(user)
var mapData map[string]interface{}
if err := json.Unmarshal(jsonData, &mapData); err != nil {
fmt.Println("conversion failed:", err)
return user.Id
}
value := mapData[util.SnakeToCamel(key)]
if str, ok := value.(string); ok {
return str
} else {
if value != nil {
valType := reflect.TypeOf(value)
typeName := valType.Name()
switch typeName {
case "bool":
return strconv.FormatBool(value.(bool))
case "int":
return strconv.Itoa(value.(int))
}
}
return user.Id
}
}
func (syncer *Syncer) getOriginalUsersFromMap(results []map[string]string) []*OriginalUser {
users := []*OriginalUser{}
for _, result := range results {

View File

@ -29,6 +29,19 @@ const (
UserPropertiesWechatOpenId = "wechatOpenId"
)
const UserEnforcerId = "built-in/user-enforcer-built-in"
var userEnforcer *UserGroupEnforcer
func InitUserManager() {
enforcer, err := GetInitializedEnforcer(UserEnforcerId)
if err != nil {
panic(err)
}
userEnforcer = NewUserGroupEnforcer(enforcer.Enforcer)
}
type User struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
@ -157,6 +170,7 @@ type User struct {
Yandex string `xorm:"yandex varchar(100)" json:"yandex"`
Zoom string `xorm:"zoom varchar(100)" json:"zoom"`
MetaMask string `xorm:"metamask varchar(100)" json:"metamask"`
Web3Onboard string `xorm:"web3onboard varchar(100)" json:"web3onboard"`
Custom string `xorm:"custom varchar(100)" json:"custom"`
WebauthnCredentials []webauthn.Credential `xorm:"webauthnCredentials blob" json:"webauthnCredentials"`
@ -531,6 +545,13 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
columns = append(columns, "name", "email", "phone", "country_code")
}
if util.ContainsString(columns, "groups") {
_, err := userEnforcer.UpdateGroupsForUser(user.GetId(), user.Groups)
if err != nil {
return false, err
}
}
affected, err := updateUser(id, user, columns)
if err != nil {
return false, err
@ -778,6 +799,10 @@ func ExtendUserWithRolesAndPermissions(user *User) (err error) {
return
}
func DeleteGroupForUser(user string, group string) (bool, error) {
return userEnforcer.DeleteGroupForUser(user, group)
}
func userChangeTrigger(oldName string, newName string) error {
session := ormer.Engine.NewSession()
defer session.Close()

95
object/user_enforcer.go Normal file
View File

@ -0,0 +1,95 @@
package object
import (
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/errors"
"github.com/casdoor/casdoor/util"
)
type UserGroupEnforcer struct {
// use rbac model implement use group, the enforcer can also implement user role
enforcer *casbin.Enforcer
}
func NewUserGroupEnforcer(enforcer *casbin.Enforcer) *UserGroupEnforcer {
return &UserGroupEnforcer{
enforcer: enforcer,
}
}
func (e *UserGroupEnforcer) AddGroupForUser(user string, group string) (bool, error) {
return e.enforcer.AddRoleForUser(user, GetGroupWithPrefix(group))
}
func (e *UserGroupEnforcer) AddGroupsForUser(user string, groups []string) (bool, error) {
g := make([]string, len(groups))
for i, group := range groups {
g[i] = GetGroupWithPrefix(group)
}
return e.enforcer.AddRolesForUser(user, g)
}
func (e *UserGroupEnforcer) DeleteGroupForUser(user string, group string) (bool, error) {
return e.enforcer.DeleteRoleForUser(user, GetGroupWithPrefix(group))
}
func (e *UserGroupEnforcer) DeleteGroupsForUser(user string) (bool, error) {
return e.enforcer.DeleteRolesForUser(user)
}
func (e *UserGroupEnforcer) GetGroupsForUser(user string) ([]string, error) {
groups, err := e.enforcer.GetRolesForUser(user)
for i, group := range groups {
groups[i] = GetGroupWithoutPrefix(group)
}
return groups, err
}
func (e *UserGroupEnforcer) GetAllUsersByGroup(group string) ([]string, error) {
users, err := e.enforcer.GetUsersForRole(GetGroupWithPrefix(group))
if err != nil {
if err == errors.ERR_NAME_NOT_FOUND {
return []string{}, nil
}
return nil, err
}
return users, nil
}
func GetGroupWithPrefix(group string) string {
return "group:" + group
}
func GetGroupWithoutPrefix(group string) string {
return group[len("group:"):]
}
func (e *UserGroupEnforcer) GetUserNamesByGroupName(groupName string) ([]string, error) {
var names []string
userIds, err := e.GetAllUsersByGroup(groupName)
if err != nil {
return nil, err
}
for _, userId := range userIds {
_, name := util.GetOwnerAndNameFromIdNoCheck(userId)
names = append(names, name)
}
return names, nil
}
func (e *UserGroupEnforcer) UpdateGroupsForUser(user string, groups []string) (bool, error) {
_, err := e.DeleteGroupsForUser(user)
if err != nil {
return false, err
}
affected, err := e.AddGroupsForUser(user, groups)
if err != nil {
return false, err
}
return affected, nil
}

View File

@ -90,7 +90,7 @@ func (pp *AlipayPaymentProvider) Notify(request *http.Request, body []byte, auth
ProductName: productName,
ProductDisplayName: productDisplayName,
ProviderName: providerName,
OutOrderId: orderId,
OrderId: orderId,
PaymentStatus: PaymentStatePaid,
Price: price,
PaymentName: paymentName,

View File

@ -255,7 +255,7 @@ func (pp *GcPaymentProvider) Notify(request *http.Request, body []byte, authorit
ProductName: productName,
ProductDisplayName: productDisplayName,
ProviderName: providerName,
OutOrderId: orderId,
OrderId: orderId,
Price: price,
PaymentStatus: PaymentStatePaid,
PaymentName: paymentName,

View File

@ -52,13 +52,12 @@ func NewPaypalPaymentProvider(clientID string, secret string) (*PaypalPaymentPro
func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
// https://github.com/go-pay/gopay/blob/main/doc/paypal.md
priceStr := strconv.FormatFloat(price, 'f', 2, 64)
units := make([]*paypal.PurchaseUnit, 0, 1)
unit := &paypal.PurchaseUnit{
ReferenceId: util.GetRandomString(16),
Amount: &paypal.Amount{
CurrencyCode: currency, // e.g."USD"
Value: priceStr, // e.g."100.00"
CurrencyCode: currency, // e.g."USD"
Value: priceFloat64ToString(price), // e.g."100.00"
},
Description: joinAttachString([]string{productDisplayName, productName, providerName}),
}
@ -147,16 +146,15 @@ func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, auth
paymentStatus = PaymentStateError
}
notifyResult = &NotifyResult{
PaymentStatus: paymentStatus,
PaymentName: paymentName,
PaymentStatus: paymentStatus,
PaymentName: paymentName,
ProductName: productName,
ProductDisplayName: productDisplayName,
ProviderName: providerName,
Price: price,
Currency: currency,
OutOrderId: orderId,
OrderId: orderId,
}
return notifyResult, nil
}

View File

@ -24,6 +24,7 @@ const (
PaymentStatePaid PaymentState = "Paid"
PaymentStateCreated PaymentState = "Created"
PaymentStateCanceled PaymentState = "Canceled"
PaymentStateTimeout PaymentState = "Timeout"
PaymentStateError PaymentState = "Error"
)
@ -32,13 +33,13 @@ type NotifyResult struct {
PaymentStatus PaymentState
NotifyMessage string
ProviderName string
ProductName string
ProductDisplayName string
ProviderName string
Price float64
Currency string
OutOrderId string
OrderId string
}
type PaymentProvider interface {
@ -75,6 +76,12 @@ func GetPaymentProvider(typ string, clientId string, clientSecret string, host s
return nil, err
}
return pp, nil
} else if typ == "Stripe" {
pp, err := NewStripePaymentProvider(clientId, clientSecret)
if err != nil {
return nil, err
}
return pp, nil
}
return nil, nil

166
pp/stripe.go Normal file
View File

@ -0,0 +1,166 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pp
import (
"fmt"
"net/http"
"time"
"github.com/casdoor/casdoor/conf"
"github.com/stripe/stripe-go/v74"
stripeCheckout "github.com/stripe/stripe-go/v74/checkout/session"
stripeIntent "github.com/stripe/stripe-go/v74/paymentintent"
stripePrice "github.com/stripe/stripe-go/v74/price"
stripeProduct "github.com/stripe/stripe-go/v74/product"
)
type StripePaymentProvider struct {
PublishableKey string
SecretKey string
isProd bool
}
func NewStripePaymentProvider(PublishableKey, SecretKey string) (*StripePaymentProvider, error) {
isProd := false
if conf.GetConfigString("runmode") == "prod" {
isProd = true
}
pp := &StripePaymentProvider{
PublishableKey: PublishableKey,
SecretKey: SecretKey,
isProd: isProd,
}
stripe.Key = pp.SecretKey
return pp, nil
}
func (pp *StripePaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (payUrl string, orderId string, err error) {
// Create a temp product
description := joinAttachString([]string{productName, productDisplayName, providerName})
productParams := &stripe.ProductParams{
Name: stripe.String(productDisplayName),
Description: stripe.String(description),
DefaultPriceData: &stripe.ProductDefaultPriceDataParams{
UnitAmount: stripe.Int64(priceFloat64ToInt64(price)),
Currency: stripe.String(currency),
},
}
sProduct, err := stripeProduct.New(productParams)
if err != nil {
return "", "", err
}
// Create a price for an existing product
priceParams := &stripe.PriceParams{
Currency: stripe.String(currency),
UnitAmount: stripe.Int64(priceFloat64ToInt64(price)),
Product: stripe.String(sProduct.ID),
}
sPrice, err := stripePrice.New(priceParams)
if err != nil {
return "", "", err
}
// Create a Checkout Session
checkoutParams := &stripe.CheckoutSessionParams{
LineItems: []*stripe.CheckoutSessionLineItemParams{
{
Price: stripe.String(sPrice.ID),
Quantity: stripe.Int64(1),
},
},
Mode: stripe.String(string(stripe.CheckoutSessionModePayment)),
SuccessURL: stripe.String(returnUrl),
CancelURL: stripe.String(returnUrl),
ClientReferenceID: stripe.String(paymentName),
ExpiresAt: stripe.Int64(time.Now().Add(30 * time.Minute).Unix()),
}
checkoutParams.AddMetadata("product_description", description)
sCheckout, err := stripeCheckout.New(checkoutParams)
if err != nil {
return "", "", err
}
return sCheckout.URL, sCheckout.ID, nil
}
func (pp *StripePaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
notifyResult := &NotifyResult{}
sCheckout, err := stripeCheckout.Get(orderId, nil)
if err != nil {
return nil, err
}
switch sCheckout.Status {
case "open":
// The checkout session is still in progress. Payment processing has not started
notifyResult.PaymentStatus = PaymentStateCreated
return notifyResult, nil
case "complete":
// The checkout session is complete. Payment processing may still be in progress
case "expired":
// The checkout session has expired. No further processing will occur
notifyResult.PaymentStatus = PaymentStateTimeout
return notifyResult, nil
default:
notifyResult.PaymentStatus = PaymentStateError
notifyResult.NotifyMessage = fmt.Sprintf("unexpected stripe checkout status: %v", sCheckout.Status)
return notifyResult, nil
}
switch sCheckout.PaymentStatus {
case "paid":
// Skip
case "unpaid":
notifyResult.PaymentStatus = PaymentStateCreated
return notifyResult, nil
default:
notifyResult.PaymentStatus = PaymentStateError
notifyResult.NotifyMessage = fmt.Sprintf("unexpected stripe checkout payment status: %v", sCheckout.PaymentStatus)
return notifyResult, nil
}
// Once payment is successful, the Checkout Session will contain a reference to the successful `PaymentIntent`
sIntent, err := stripeIntent.Get(sCheckout.PaymentIntent.ID, nil)
var (
productName string
productDisplayName string
providerName string
)
if description, ok := sCheckout.Metadata["product_description"]; ok {
productName, productDisplayName, providerName, _ = parseAttachString(description)
}
notifyResult = &NotifyResult{
PaymentName: sCheckout.ClientReferenceID,
PaymentStatus: PaymentStatePaid,
ProductName: productName,
ProductDisplayName: productDisplayName,
ProviderName: providerName,
Price: priceInt64ToFloat64(sIntent.Amount),
Currency: string(sIntent.Currency),
OrderId: orderId,
}
return notifyResult, nil
}
func (pp *StripePaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
return "", nil
}
func (pp *StripePaymentProvider) GetResponseError(err error) string {
if err == nil {
return "success"
} else {
return "fail"
}
}

View File

@ -16,6 +16,8 @@ package pp
import (
"fmt"
"math"
"strconv"
"strings"
)
@ -35,3 +37,15 @@ func parseAttachString(s string) (string, string, string, error) {
}
return tokens[0], tokens[1], tokens[2], nil
}
func priceInt64ToFloat64(price int64) float64 {
return float64(price) / 100
}
func priceFloat64ToInt64(price float64) int64 {
return int64(math.Round(price * 100))
}
func priceFloat64ToString(price float64) string {
return strconv.FormatFloat(price, 'f', 2, 64)
}

View File

@ -113,7 +113,7 @@ func (pp *WechatPaymentProvider) Notify(request *http.Request, body []byte, auth
ProductName: productName,
ProductDisplayName: productDisplayName,
ProviderName: providerName,
OutOrderId: orderId,
OrderId: orderId,
Price: price,
PaymentStatus: PaymentStatePaid,
PaymentName: paymentName,

View File

@ -51,6 +51,7 @@ func initAPI() {
beego.Router("/api/signup", &controllers.ApiController{}, "POST:Signup")
beego.Router("/api/login", &controllers.ApiController{}, "POST:Login")
beego.Router("/api/get-app-login", &controllers.ApiController{}, "GET:GetApplicationLogin")
beego.Router("/api/get-dashboard", &controllers.ApiController{}, "GET:GetDashboard")
beego.Router("/api/logout", &controllers.ApiController{}, "GET,POST:Logout")
beego.Router("/api/get-account", &controllers.ApiController{}, "GET:GetAccount")
beego.Router("/api/userinfo", &controllers.ApiController{}, "GET:GetUserinfo")

31
storage/google_cloud.go Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package storage
import (
"github.com/casdoor/oss"
"github.com/casdoor/oss/googlecloud"
)
func NewGoogleCloudStorageProvider(clientId string, clientSecret string, bucket string, endpoint string) oss.StorageInterface {
sp, _ := googlecloud.New(&googlecloud.Config{
AccessID: clientId,
AccessKey: clientSecret,
Bucket: bucket,
Endpoint: endpoint,
})
return sp
}

32
storage/qiniu_cloud.go Normal file
View File

@ -0,0 +1,32 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package storage
import (
"github.com/casdoor/oss"
"github.com/casdoor/oss/qiniu"
)
func NewQiniuCloudKodoStorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
sp := qiniu.New(&qiniu.Config{
AccessID: clientId,
AccessKey: clientSecret,
Region: region,
Bucket: bucket,
Endpoint: endpoint,
})
return sp
}

View File

@ -30,6 +30,10 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
return NewTencentCloudCosStorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "Azure Blob":
return NewAzureBlobStorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "Qiniu Cloud Kodo":
return NewQiniuCloudKodoStorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "Google Cloud Storage":
return NewGoogleCloudStorageProvider(clientId, clientSecret, bucket, endpoint)
}
return nil

63
util/casbin.go Normal file
View File

@ -0,0 +1,63 @@
// Copyright 2022 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 xormadapter "github.com/casdoor/xorm-adapter/v3"
func CasbinToSlice(casbinRule xormadapter.CasbinRule) []string {
s := []string{
casbinRule.V0,
casbinRule.V1,
casbinRule.V2,
casbinRule.V3,
casbinRule.V4,
casbinRule.V5,
}
// remove empty strings from end, for update model policy map
for i := len(s) - 1; i >= 0; i-- {
if s[i] != "" {
s = s[:i+1]
break
}
}
return s
}
func safeReturn(policy []string, i int) string {
if len(policy) > i {
return policy[i]
} else {
return ""
}
}
func MatrixToCasbinRules(Ptype string, policies [][]string) []*xormadapter.CasbinRule {
res := []*xormadapter.CasbinRule{}
for _, policy := range policies {
line := xormadapter.CasbinRule{
Ptype: Ptype,
V0: safeReturn(policy, 0),
V1: safeReturn(policy, 1),
V2: safeReturn(policy, 2),
V3: safeReturn(policy, 3),
V4: safeReturn(policy, 4),
V5: safeReturn(policy, 5),
}
res = append(res, &line)
}
return res
}

View File

@ -88,6 +88,17 @@ func CamelToSnakeCase(camel string) string {
return strings.ReplaceAll(buf.String(), " ", "")
}
func SnakeToCamel(snake string) string {
words := strings.Split(snake, "_")
for i := range words {
words[i] = strings.ToLower(words[i])
if i > 0 {
words[i] = strings.Title(words[i])
}
}
return strings.Join(words, "")
}
func GetOwnerAndNameFromId(id string) (string, string) {
tokens := strings.Split(id, "/")
if len(tokens) != 2 {

View File

@ -51,31 +51,46 @@ module.exports = {
},
],
webpack: {
// use polyfill Buffer with Webpack 5
// https://viglucci.io/articles/how-to-polyfill-buffer-with-webpack-5
// https://craco.js.org/docs/configuration/webpack/
configure: (webpackConfig, { env, paths }) => {
webpackConfig.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,
};
return webpackConfig;
configure: {
// ignore webpack warnings by source-map-loader
// https://github.com/facebook/create-react-app/pull/11752#issuecomment-1345231546
ignoreWarnings: [
function ignoreSourcemapsloaderWarnings(warning) {
return (
warning.module &&
warning.module.resource.includes('node_modules') &&
warning.details &&
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,
},
}
},
}
};

View File

@ -13,6 +13,16 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"@web3-onboard/coinbase": "^2.2.5",
"@web3-onboard/core": "^2.20.5",
"@web3-onboard/frontier": "^2.0.4",
"@web3-onboard/gnosis": "^2.1.10",
"@web3-onboard/infinity-wallet": "^2.0.4",
"@web3-onboard/injected-wallets": "^2.10.4",
"@web3-onboard/react": "^2.8.10",
"@web3-onboard/sequence": "^2.0.8",
"@web3-onboard/taho": "^2.0.5",
"@web3-onboard/trust": "^2.0.4",
"antd": "5.2.3",
"antd-token-previewer": "^1.1.0-22",
"buffer": "^6.0.3",
@ -20,7 +30,9 @@
"copy-to-clipboard": "^3.3.1",
"core-js": "^3.25.0",
"craco-less": "^2.0.0",
"echarts": "^5.4.3",
"eslint-plugin-unused-imports": "^2.0.0",
"ethers": "5.6.9",
"file-saver": "^2.0.5",
"i18n-iso-countries": "^7.0.0",
"i18next": "^19.8.9",
@ -44,7 +56,7 @@
},
"scripts": {
"start": "cross-env PORT=7001 craco start",
"build": "craco build",
"build": "craco --max_old_space_size=4096 build",
"test": "craco test",
"eject": "craco eject",
"crowdin:sync": "crowdin upload && crowdin download",

View File

@ -13,17 +13,12 @@
// limitations under the License.
import React from "react";
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
import {Button, Card, Col, Input, InputNumber, Row, Select} from "antd";
import * as AdapterBackend from "./backend/AdapterBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as Setting from "./Setting";
import i18next from "i18next";
import "codemirror/lib/codemirror.css";
import PolicyTable from "./table/PoliciyTable";
require("codemirror/theme/material-darker.css");
require("codemirror/mode/javascript/javascript");
const {Option} = Select;
class AdapterEditPage extends React.Component {
@ -232,20 +227,23 @@ class AdapterEditPage extends React.Component {
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("adapter:Policies"), i18next.t("adapter:Policies - Tooltip"))} :
{Setting.getLabel(i18next.t("provider:DB Test"), i18next.t("provider:DB Test - Tooltip"))} :
</Col>
<Col span={22}>
<PolicyTable owner={this.state.organizationName} name={this.state.adapterName} mode={this.state.mode} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("general:Is enabled"), i18next.t("general:Is enabled - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.adapter.isEnabled} onChange={checked => {
this.updateAdapterField("isEnabled", checked);
}} />
<Col span={2} >
<Button type={"primary"} onClick={() => {
AdapterBackend.getPolicies("", "", `${this.state.organizationName}/${this.state.adapterName}`)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("syncer:Connect successfully"));
} else {
Setting.showMessage("error", i18next.t("syncer:Failed to connect") + ": " + res.msg);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
}>{i18next.t("syncer:Test DB Connection")}</Button>
</Col>
</Row>
</Card>

View File

@ -14,7 +14,7 @@
import React from "react";
import {Link} from "react-router-dom";
import {Button, Switch, Table} from "antd";
import {Button, Table} from "antd";
import moment from "moment";
import * as Setting from "./Setting";
import * as AdapterBackend from "./backend/AdapterBackend";
@ -38,7 +38,6 @@ class AdapterListPage extends BaseListPage {
databaseType: "mysql",
database: "dbName",
table: "tableName",
isEnabled: false,
};
}
@ -183,18 +182,6 @@ class AdapterListPage extends BaseListPage {
width: "120px",
sorter: true,
},
{
title: i18next.t("general:Is enabled"),
dataIndex: "isEnabled",
key: "isEnabled",
width: "120px",
sorter: true,
render: (text, record, index) => {
return (
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />
);
},
},
{
title: i18next.t("general:Action"),
dataIndex: "",

View File

@ -153,10 +153,10 @@ class App extends Component {
this.setState({selectedMenuKey: "/"});
} else if (uri.includes("/organizations") || uri.includes("/trees") || uri.includes("/users") || uri.includes("/groups")) {
this.setState({selectedMenuKey: "/orgs"});
} else if (uri.includes("/roles") || uri.includes("/permissions") || uri.includes("/models") || uri.includes("/adapters") || uri.includes("/enforcers")) {
this.setState({selectedMenuKey: "/auth"});
} else if (uri.includes("/applications") || uri.includes("/providers") || uri.includes("/resources") || uri.includes("/certs")) {
this.setState({selectedMenuKey: "/identity"});
} else if (uri.includes("/roles") || uri.includes("/permissions") || uri.includes("/models") || uri.includes("/adapters") || uri.includes("/enforcers")) {
this.setState({selectedMenuKey: "/auth"});
} else if (uri.includes("/records") || uri.includes("/tokens") || uri.includes("/sessions")) {
this.setState({selectedMenuKey: "/logs"});
} else if (uri.includes("/products") || uri.includes("/payments") || uri.includes("/plans") || uri.includes("/pricings") || uri.includes("/subscriptions")) {
@ -351,7 +351,7 @@ class App extends Component {
}
&nbsp;
&nbsp;
{Setting.isMobile() ? null : Setting.getNameAtLeast(this.state.account.displayName)} &nbsp; <DownOutlined />
{Setting.isMobile() ? null : Setting.getShortText(Setting.getNameAtLeast(this.state.account.displayName), 30)} &nbsp; <DownOutlined />
&nbsp;
&nbsp;
&nbsp;
@ -418,6 +418,13 @@ class App extends Component {
Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>, "/users"),
]));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/applications">{i18next.t("general:Identity")}</Link>, "/identity", <LockTwoTone />, [
Setting.getItem(<Link to="/applications">{i18next.t("general:Applications")}</Link>, "/applications"),
Setting.getItem(<Link to="/providers">{i18next.t("general:Providers")}</Link>, "/providers"),
Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>, "/resources"),
Setting.getItem(<Link to="/certs">{i18next.t("general:Certs")}</Link>, "/certs"),
]));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/roles">{i18next.t("general:Authorization")}</Link>, "/auth", <SafetyCertificateTwoTone />, [
Setting.getItem(<Link to="/roles">{i18next.t("general:Roles")}</Link>, "/roles"),
Setting.getItem(<Link to="/permissions">{i18next.t("general:Permissions")}</Link>, "/permissions"),
@ -431,15 +438,6 @@ class App extends Component {
return true;
}
})));
}
if (Setting.isLocalAdminUser(this.state.account)) {
res.push(Setting.getItem(<Link style={{color: "black"}} to="/applications">{i18next.t("general:Identity")}</Link>, "/identity", <LockTwoTone />, [
Setting.getItem(<Link to="/applications">{i18next.t("general:Applications")}</Link>, "/applications"),
Setting.getItem(<Link to="/providers">{i18next.t("general:Providers")}</Link>, "/providers"),
Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>, "/resources"),
Setting.getItem(<Link to="/certs">{i18next.t("general:Certs")}</Link>, "/certs"),
]));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/records">{i18next.t("general:Logging & Auditing")}</Link>, "/logs", <WalletTwoTone />, [
Setting.getItem(<Link to="/records">{i18next.t("general:Records")}</Link>, "/records"),

View File

@ -13,11 +13,12 @@
// limitations under the License.
import React from "react";
import {Button, Card, Col, Input, Row, Select, Switch} from "antd";
import {Button, Card, Col, Input, Row, Select} from "antd";
import * as AdapterBackend from "./backend/AdapterBackend";
import * as EnforcerBackend from "./backend/EnforcerBackend";
import * as ModelBackend from "./backend/ModelBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
import PolicyTable from "./table/PolicyTable";
import * as Setting from "./Setting";
import i18next from "i18next";
@ -42,7 +43,7 @@ class EnforcerEditPage extends React.Component {
}
getEnforcer() {
EnforcerBackend.getEnforcer(this.state.organizationName, this.state.enforcerName)
EnforcerBackend.getEnforcer(this.state.organizationName, this.state.enforcerName, true)
.then((res) => {
if (res.data === null) {
this.props.history.push("/404");
@ -169,7 +170,7 @@ class EnforcerEditPage extends React.Component {
<Select virtual={false} disabled={Setting.builtInObject(this.state.enforcer)} style={{width: "100%"}} value={this.state.enforcer.model} onChange={(model => {
this.updateEnforcerField("model", model);
})}
options={this.state.models.map((model) => Setting.getOption(model.displayName, `${model.owner}/${model.name}`))
options={this.state.models.map((model) => Setting.getOption(`${model.owner}/${model.name}`, `${model.owner}/${model.name}`))
} />
</Col>
</Row>
@ -181,18 +182,16 @@ class EnforcerEditPage extends React.Component {
<Select virtual={false} disabled={Setting.builtInObject(this.state.enforcer)} style={{width: "100%"}} value={this.state.enforcer.adapter} onChange={(adapter => {
this.updateEnforcerField("adapter", adapter);
})}
options={this.state.adapters.map((adapter) => Setting.getOption(adapter.name, `${adapter.owner}/${adapter.name}`))
options={this.state.adapters.map((adapter) => Setting.getOption(`${adapter.owner}/${adapter.name}`, `${adapter.owner}/${adapter.name}`))
} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("general:Is enabled"), i18next.t("general:Is enabled - Tooltip"))} :
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("adapter:Policies"), i18next.t("adapter:Policies - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.enforcer.isEnabled} onChange={checked => {
this.updateEnforcerField("isEnabled", checked);
}} />
<Col span={22}>
<PolicyTable enforcer={this.state.enforcer} modelCfg={this.state.enforcer?.modelCfg} mode={this.state.mode} />
</Col>
</Row>
</Card>

View File

@ -14,7 +14,7 @@
import React from "react";
import {Link} from "react-router-dom";
import {Button, Switch, Table} from "antd";
import {Button, Table} from "antd";
import moment from "moment";
import * as Setting from "./Setting";
import * as EnforcerBackend from "./backend/EnforcerBackend";
@ -31,7 +31,6 @@ class EnforcerListPage extends BaseListPage {
name: `enforcer_${randomName}`,
createdTime: moment().format(),
displayName: `New Enforcer - ${randomName}`,
isEnabled: true,
};
}
@ -75,7 +74,7 @@ class EnforcerListPage extends BaseListPage {
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "150px",
width: "200px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("name"),
@ -116,19 +115,39 @@ class EnforcerListPage extends BaseListPage {
title: i18next.t("general:Display name"),
dataIndex: "displayName",
key: "displayName",
width: "200px",
// width: "200px",
sorter: true,
...this.getColumnSearchProps("displayName"),
},
{
title: i18next.t("general:Is enabled"),
dataIndex: "isEnabled",
key: "isEnabled",
width: "120px",
title: i18next.t("general:Model"),
dataIndex: "model",
key: "model",
width: "250px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("name"),
render: (text, record, index) => {
return (
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />
<Link to={`/models/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Adapter"),
dataIndex: "adapter",
key: "adapter",
width: "250px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("name"),
render: (text, record, index) => {
return (
<Link to={`/adapters/${text}`}>
{text}
</Link>
);
},
},
@ -136,7 +155,7 @@ class EnforcerListPage extends BaseListPage {
title: i18next.t("general:Action"),
dataIndex: "",
key: "op",
width: "170px",
width: "180px",
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
return (

View File

@ -103,7 +103,7 @@ class GroupListPage extends BaseListPage {
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "120px",
width: "150px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("name"),
@ -119,7 +119,7 @@ class GroupListPage extends BaseListPage {
title: i18next.t("general:Organization"),
dataIndex: "owner",
key: "owner",
width: "120px",
width: "140px",
sorter: true,
...this.getColumnSearchProps("owner"),
render: (text, record, index) => {
@ -134,7 +134,7 @@ class GroupListPage extends BaseListPage {
title: i18next.t("general:Created time"),
dataIndex: "createdTime",
key: "createdTime",
width: "150px",
width: "180px",
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
@ -144,7 +144,7 @@ class GroupListPage extends BaseListPage {
title: i18next.t("general:Updated time"),
dataIndex: "updatedTime",
key: "updatedTime",
width: "150px",
width: "180px",
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
@ -154,7 +154,7 @@ class GroupListPage extends BaseListPage {
title: i18next.t("general:Display name"),
dataIndex: "displayName",
key: "displayName",
width: "100px",
// width: "200px",
sorter: true,
...this.getColumnSearchProps("displayName"),
},
@ -162,7 +162,7 @@ class GroupListPage extends BaseListPage {
title: i18next.t("general:Type"),
dataIndex: "type",
key: "type",
width: "110px",
width: "140px",
sorter: true,
filterMultiple: false,
filters: [
@ -177,7 +177,7 @@ class GroupListPage extends BaseListPage {
title: i18next.t("group:Parent group"),
dataIndex: "parentId",
key: "parentId",
width: "110px",
width: "220px",
sorter: true,
...this.getColumnSearchProps("parentId"),
render: (text, record, index) => {
@ -199,7 +199,7 @@ class GroupListPage extends BaseListPage {
title: i18next.t("general:Action"),
dataIndex: "",
key: "op",
width: "170px",
width: "180px",
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
const haveChildren = this.state.groups.find((group) => group.parentId === record.id) !== undefined;

View File

@ -221,6 +221,7 @@ class GroupTreePage extends React.Component {
onChange={(value) => {
this.setState({
organizationName: value,
groupName: "",
});
this.props.history.push(`/trees/${value}`);
}}

View File

@ -191,8 +191,8 @@ class LdapSyncPage extends React.Component {
},
{
title: i18next.t("general:Phone"),
dataIndex: "phone",
key: "phone",
dataIndex: "mobile",
key: "mobile",
sorter: (a, b) => a.phone.localeCompare(b.phone),
},
{

View File

@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {Button, Card, Col, Input, Row, Select, Switch} from "antd";
import {Button, Card, Col, Input, Row, Select} from "antd";
import * as ModelBackend from "./backend/ModelBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as Setting from "./Setting";
@ -161,16 +161,6 @@ class ModelEditPage extends React.Component {
</div>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("general:Is enabled"), i18next.t("general:Is enabled - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.model.isEnabled} onChange={checked => {
this.updateModelField("isEnabled", checked);
}} />
</Col>
</Row>
</Card>
);
}

View File

@ -13,8 +13,9 @@
// limitations under the License.
import React from "react";
import {Controlled as CodeMirror} from "react-codemirror2";
import {Link} from "react-router-dom";
import {Button, Switch, Table} from "antd";
import {Button, Popover, Table} from "antd";
import moment from "moment";
import * as Setting from "./Setting";
import * as ModelBackend from "./backend/ModelBackend";
@ -47,7 +48,6 @@ class ModelListPage extends BaseListPage {
createdTime: moment().format(),
displayName: `New Model - ${randomName}`,
modelText: rbacModel,
isEnabled: true,
};
}
@ -91,7 +91,7 @@ class ModelListPage extends BaseListPage {
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "150px",
width: "180px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("name"),
@ -107,7 +107,7 @@ class ModelListPage extends BaseListPage {
title: i18next.t("general:Organization"),
dataIndex: "owner",
key: "owner",
width: "120px",
width: "180px",
sorter: true,
...this.getColumnSearchProps("owner"),
render: (text, record, index) => {
@ -122,7 +122,7 @@ class ModelListPage extends BaseListPage {
title: i18next.t("general:Created time"),
dataIndex: "createdTime",
key: "createdTime",
width: "160px",
width: "180px",
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
@ -137,14 +137,26 @@ class ModelListPage extends BaseListPage {
...this.getColumnSearchProps("displayName"),
},
{
title: i18next.t("general:Is enabled"),
dataIndex: "isEnabled",
key: "isEnabled",
width: "120px",
title: i18next.t("model:Model text"),
dataIndex: "modelText",
key: "modelText",
// width: "180px",
sorter: true,
render: (text, record, index) => {
return (
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />
<Popover placement="topRight" content={() => {
return (
<CodeMirror
value={text}
options={{mode: "properties", theme: "default"}}
onBeforeChange={(editor, data, value) => {}}
/>
);
}} title="" trigger="hover">
{
Setting.getShortText(text, 100)
}
</Popover>
);
},
},
@ -152,7 +164,7 @@ class ModelListPage extends BaseListPage {
title: i18next.t("general:Action"),
dataIndex: "",
key: "op",
width: "170px",
width: "180px",
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
return (

View File

@ -182,7 +182,7 @@ class OrganizationListPage extends BaseListPage {
title: i18next.t("organization:Website URL"),
dataIndex: "websiteUrl",
key: "websiteUrl",
width: "300px",
width: "200px",
sorter: true,
...this.getColumnSearchProps("websiteUrl"),
render: (text, record, index) => {
@ -243,7 +243,7 @@ class OrganizationListPage extends BaseListPage {
title: i18next.t("general:Action"),
dataIndex: "",
key: "op",
width: "320px",
width: "350px",
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
return (

View File

@ -48,7 +48,7 @@ class PaymentResultPage extends React.Component {
});
// window.console.log("payment=", res.data);
if (res.data.state === "Created") {
if (res.data.type === "PayPal") {
if (["PayPal", "Stripe"].includes(res.data.type)) {
this.setState({
timeout: setTimeout(() => {
PaymentBackend.notifyPayment(this.state.organizationName, this.state.paymentName)
@ -135,6 +135,26 @@ class PaymentResultPage extends React.Component {
/>
</div>
);
} else if (payment.state === "Timeout") {
return (
<div>
{
Setting.renderHelmet(payment)
}
<Result
status="warning"
title={`${i18next.t("payment:The payment has time out")}: ${payment.productDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}`}
subTitle={i18next.t("payment:Please click the below button to return to the original website")}
extra={[
<Button type="primary" key="returnUrl" onClick={() => {
this.goToPaymentUrl(payment);
}}>
{i18next.t("payment:Return to Website")}
</Button>,
]}
/>
</div>
);
} else {
return (
<div>

View File

@ -163,6 +163,8 @@ class ProductBuyPage extends React.Component {
text = i18next.t("product:WeChat Pay");
} else if (provider.type === "PayPal") {
text = i18next.t("product:PayPal");
} else if (provider.type === "Stripe") {
text = i18next.t("product:Stripe");
}
return (

View File

@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
import {Button, Card, Checkbox, Col, Input, InputNumber, Row, Select, Switch} from "antd";
import {LinkOutlined} from "@ant-design/icons";
import * as ProviderBackend from "./backend/ProviderBackend";
import * as Setting from "./Setting";
@ -25,6 +25,7 @@ import copy from "copy-to-clipboard";
import {CaptchaPreview} from "./common/CaptchaPreview";
import * as OrganizationBackend from "./backend/OrganizationBackend";
import {CountryCodeSelect} from "./common/select/CountryCodeSelect";
import * as Web3Auth from "./auth/Web3Auth";
const {Option} = Select;
const {TextArea} = Input;
@ -356,7 +357,7 @@ class ProviderEditPage extends React.Component {
<Select virtual={false} style={{width: "100%"}} value={this.state.provider.category} onChange={(value => {
this.updateProviderField("category", value);
if (value === "OAuth") {
this.updateProviderField("type", "GitHub");
this.updateProviderField("type", "Google");
} else if (value === "Email") {
this.updateProviderField("type", "Default");
this.updateProviderField("host", "smtp.example.com");
@ -366,12 +367,12 @@ class ProviderEditPage extends React.Component {
this.updateProviderField("content", "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes.");
this.updateProviderField("receiver", this.props.account.email);
} else if (value === "SMS") {
this.updateProviderField("type", "Aliyun SMS");
this.updateProviderField("type", "Twilio SMS");
} else if (value === "Storage") {
this.updateProviderField("type", "Local File System");
this.updateProviderField("type", "AWS S3");
this.updateProviderField("domain", Setting.getFullServerUrl());
} else if (value === "SAML") {
this.updateProviderField("type", "Aliyun IDaaS");
this.updateProviderField("type", "Keycloak");
} else if (value === "Payment") {
this.updateProviderField("type", "PayPal");
} else if (value === "Captcha") {
@ -406,12 +407,16 @@ class ProviderEditPage extends React.Component {
this.updateProviderField("type", value);
if (value === "Local File System") {
this.updateProviderField("domain", Setting.getFullServerUrl());
}
if (value === "Custom") {
} else if (value === "Custom") {
this.updateProviderField("customAuthUrl", "https://door.casdoor.com/login/oauth/authorize");
this.updateProviderField("scopes", "openid profile email");
this.updateProviderField("customTokenUrl", "https://door.casdoor.com/api/login/oauth/access_token");
this.updateProviderField("customUserInfoUrl", "https://door.casdoor.com/api/userinfo");
} else if (value === "Custom HTTP SMS") {
this.updateProviderField("endpoint", "https://door.casdoor.com/api/get-account");
this.updateProviderField("method", "GET");
this.updateProviderField("clientId", "param1");
this.updateProviderField("title", "");
}
})}>
{
@ -547,30 +552,33 @@ class ProviderEditPage extends React.Component {
)
}
{
(this.state.provider.category === "Captcha" && this.state.provider.type === "Default") || (this.state.provider.category === "Web3") || (this.state.provider.category === "Storage" && this.state.provider.type === "Local File System") ? null : (
<React.Fragment>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{this.getClientIdLabel(this.state.provider)} :
</Col>
<Col span={22} >
<Input value={this.state.provider.clientId} onChange={e => {
this.updateProviderField("clientId", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{this.getClientSecretLabel(this.state.provider)} :
</Col>
<Col span={22} >
<Input value={this.state.provider.clientSecret} onChange={e => {
this.updateProviderField("clientSecret", e.target.value);
}} />
</Col>
</Row>
</React.Fragment>
)
(this.state.provider.category === "Captcha" && this.state.provider.type === "Default") ||
(this.state.provider.category === "SMS" && this.state.provider.type === "Custom HTTP SMS") ||
(this.state.provider.category === "Web3") ||
(this.state.provider.category === "Storage" && this.state.provider.type === "Local File System") ? null : (
<React.Fragment>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{this.getClientIdLabel(this.state.provider)} :
</Col>
<Col span={22} >
<Input value={this.state.provider.clientId} onChange={e => {
this.updateProviderField("clientId", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{this.getClientSecretLabel(this.state.provider)} :
</Col>
<Col span={22} >
<Input value={this.state.provider.clientSecret} onChange={e => {
this.updateProviderField("clientSecret", e.target.value);
}} />
</Col>
</Row>
</React.Fragment>
)
}
{
this.state.provider.category !== "Email" && this.state.provider.type !== "WeChat" && this.state.provider.type !== "Aliyun Captcha" && this.state.provider.type !== "WeChat Pay" ? null : (
@ -623,14 +631,14 @@ class ProviderEditPage extends React.Component {
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.domain} onChange={e => {
<Input prefix={<LinkOutlined />} value={this.state.provider.domain} onChange={e => {
this.updateProviderField("domain", e.target.value);
}} />
</Col>
</Row>
)
}
{this.state.provider.category === "Storage" ? (
{this.state.provider.category === "Storage" || this.state.provider.type === "Custom HTTP SMS" ? (
<div>
{["Local File System"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
@ -638,25 +646,25 @@ class ProviderEditPage extends React.Component {
{Setting.getLabel(i18next.t("provider:Endpoint"), i18next.t("provider:Region endpoint for Internet"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.endpoint} onChange={e => {
<Input prefix={<LinkOutlined />} value={this.state.provider.endpoint} onChange={e => {
this.updateProviderField("endpoint", e.target.value);
}} />
</Col>
</Row>
)}
{["Local File System", "MinIO", "Tencent Cloud COS"].includes(this.state.provider.type) ? null : (
{["Custom HTTP SMS", "Local File System", "MinIO", "Tencent Cloud COS", "Google Cloud Storage", "Qiniu Cloud Kodo"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Endpoint (Intranet)"), i18next.t("provider:Region endpoint for Intranet"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.intranetEndpoint} onChange={e => {
<Input prefix={<LinkOutlined />} value={this.state.provider.intranetEndpoint} onChange={e => {
this.updateProviderField("intranetEndpoint", e.target.value);
}} />
</Col>
</Row>
)}
{["Local File System"].includes(this.state.provider.type) ? null : (
{["Custom HTTP SMS", "Local File System"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Bucket"), i18next.t("provider:Bucket - Tooltip"))} :
@ -668,29 +676,31 @@ class ProviderEditPage extends React.Component {
</Col>
</Row>
)}
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Path prefix"), i18next.t("provider:Path prefix - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.pathPrefix} onChange={e => {
this.updateProviderField("pathPrefix", e.target.value);
}} />
</Col>
</Row>
{["MinIO"].includes(this.state.provider.type) ? null : (
{["Custom HTTP SMS"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Path prefix"), i18next.t("provider:Path prefix - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.pathPrefix} onChange={e => {
this.updateProviderField("pathPrefix", e.target.value);
}} />
</Col>
</Row>
)}
{["Custom HTTP SMS", "MinIO", "Google Cloud Storage", "Qiniu Cloud Kodo"].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"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.domain} disabled={this.state.provider.type === "Local File System"} onChange={e => {
<Input prefix={<LinkOutlined />} value={this.state.provider.domain} disabled={this.state.provider.type === "Local File System"} onChange={e => {
this.updateProviderField("domain", e.target.value);
}} />
</Col>
</Row>
)}
{["AWS S3", "Tencent Cloud COS"].includes(this.state.provider.type) ? (
{["AWS S3", "Tencent Cloud COS", "Qiniu Cloud Kodo"].includes(this.state.provider.type) ? (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Region ID"), i18next.t("provider:Region ID - Tooltip"))} :
@ -704,6 +714,49 @@ class ProviderEditPage extends React.Component {
) : null}
</div>
) : null}
{
this.state.provider.type !== "Custom HTTP SMS" ? null : (
<React.Fragment>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("general:Method"), i18next.t("provider:Method - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.provider.method} onChange={value => {
this.updateProviderField("method", value);
}}>
{
[
{id: "GET", name: "GET"},
{id: "POST", name: "POST"},
].map((method, index) => <Option key={index} value={method.id}>{method.name}</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Parameter name"), i18next.t("provider:Parameter name - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.clientId} onChange={e => {
this.updateProviderField("clientId", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Content"), i18next.t("provider:Content - Tooltip"))} :
</Col>
<Col span={22} >
<TextArea autoSize={{minRows: 3, maxRows: 100}} value={this.state.provider.title} onChange={e => {
this.updateProviderField("title", e.target.value);
}} />
</Col>
</Row>
</React.Fragment>
)
}
{this.getAppIdRow(this.state.provider)}
{
this.state.provider.category === "Email" ? (
@ -779,7 +832,7 @@ class ProviderEditPage extends React.Component {
</React.Fragment>
) : this.state.provider.category === "SMS" ? (
<React.Fragment>
{["Twilio SMS", "Amazon SNS", "Azure ACS", "Msg91 SMS", "Infobip SMS"].includes(this.state.provider.type) ?
{["Custom HTTP SMS", "Twilio SMS", "Amazon SNS", "Azure ACS", "Msg91 SMS", "Infobip SMS"].includes(this.state.provider.type) ?
null :
(<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
@ -793,7 +846,7 @@ class ProviderEditPage extends React.Component {
</Row>
)
}
{["Infobip SMS"].includes(this.state.provider.type) ?
{["Custom HTTP SMS", "Infobip SMS"].includes(this.state.provider.type) ?
null :
(<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
@ -811,27 +864,32 @@ class ProviderEditPage extends React.Component {
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:SMS Test"), i18next.t("provider:SMS Test - Tooltip"))} :
</Col>
<Col span={4} >
<Input.Group compact>
<CountryCodeSelect
style={{width: "30%"}}
value={this.state.provider.content}
onChange={(value) => {
this.updateProviderField("content", value);
}}
countryCodes={this.props.account.organization.countryCodes}
/>
<Input value={this.state.provider.receiver}
style={{width: "70%"}}
placeholder = {i18next.t("user:Input your phone number")}
onChange={e => {
this.updateProviderField("receiver", e.target.value);
}} />
</Input.Group>
</Col>
{["Custom HTTP SMS"].includes(this.state.provider.type) ?
null :
(
<Col span={4} >
<Input.Group compact>
<CountryCodeSelect
style={{width: "30%"}}
value={this.state.provider.content}
onChange={(value) => {
this.updateProviderField("content", value);
}}
countryCodes={this.props.account.organization.countryCodes}
/>
<Input value={this.state.provider.receiver}
style={{width: "70%"}}
placeholder = {i18next.t("user:Input your phone number")}
onChange={e => {
this.updateProviderField("receiver", e.target.value);
}} />
</Input.Group>
</Col>
)
}
<Col span={2} >
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary"
disabled={!Setting.isValidPhone(this.state.provider.receiver)}
disabled={!Setting.isValidPhone(this.state.provider.receiver) && (this.state.provider.type !== "Custom HTTP SMS" || this.state.provider.endpoint === "")}
onClick={() => ProviderEditTestSms.sendTestSms(this.state.provider, "+" + Setting.getCountryCode(this.state.provider.content) + this.state.provider.receiver)} >
{i18next.t("provider:Send Testing SMS")}
</Button>
@ -954,6 +1012,30 @@ class ProviderEditPage extends React.Component {
</Row>
) : null
}
{
this.state.provider.type === "Web3Onboard" ? (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Wallets"), i18next.t("provider:Wallets - Tooltip"))} :
</Col>
<Col span={22}>
<Checkbox.Group
options={Web3Auth.getWeb3OnboardWalletsOptions()}
value={() => {
try {
return JSON.parse(this.state.provider.metadata);
} catch {
return ["injected"];
}
}}
onChange={options => {
this.updateProviderField("metadata", JSON.stringify(options));
}}
/>
</Col>
</Row>
) : null
}
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Provider URL"), i18next.t("provider:Provider URL - Tooltip"))} :

View File

@ -46,6 +46,9 @@ export const Countries = [{label: "English", key: "en", country: "US", alt: "Eng
{label: "Itariano", key: "it", country: "IT", alt: "Itariano"},
{label: "Marley", key: "ms", country: "MY", alt: "Marley"},
{label: "Tkiš", key: "tr", country: "TR", alt: "Tkiš"},
{label: "لغة عربية", key: "ar", country: "DZ", alt: "لغة عربية"},
{label: "עִבְרִית", key: "he", country: "IL", alt: "עִבְרִית"},
{label: "Filipino", key: "fi", country: "PH", alt: "Filipino"},
];
export function getThemeData(organization, application) {
@ -93,6 +96,10 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/social_azure.png`,
url: "https://azure.microsoft.com/en-us/products/communication-services",
},
"Custom HTTP SMS": {
logo: `${StaticBaseUrl}/img/email_default.png`,
url: "https://casdoor.org/docs/provider/sms/overview",
},
"Infobip SMS": {
logo: `${StaticBaseUrl}/img/social_infobip.png`,
url: "https://portal.infobip.com/homepage/",
@ -177,6 +184,14 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/social_azure.png`,
url: "https://azure.microsoft.com/en-us/services/storage/blobs/",
},
"Qiniu Cloud Kodo": {
logo: `${StaticBaseUrl}/img/social_qiniu_cloud.png`,
url: "https://www.qiniu.com/solutions/storage",
},
"Google Cloud Storage": {
logo: `${StaticBaseUrl}/img/social_google_cloud.png`,
url: "https://cloud.google.com/storage",
},
},
SAML: {
"Aliyun IDaaS": {
@ -205,6 +220,10 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/payment_paypal.png`,
url: "https://www.paypal.com/",
},
"Stripe": {
logo: `${StaticBaseUrl}/img/social_stripe.png`,
url: "https://stripe.com/",
},
"GC": {
logo: `${StaticBaseUrl}/img/payment_gc.png`,
url: "https://gc.org",
@ -247,6 +266,10 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/social_metamask.svg`,
url: "https://metamask.io/",
},
"Web3Onboard": {
logo: `${StaticBaseUrl}/img/social_web3onboard.svg`,
url: "https://onboard.blocknative.com/",
},
},
};
@ -880,6 +903,7 @@ export function getProviderTypeOptions(category) {
{id: "Aliyun SMS", name: "Alibaba Cloud SMS"},
{id: "Amazon SNS", name: "Amazon SNS"},
{id: "Azure ACS", name: "Azure ACS"},
{id: "Custom HTTP SMS", name: "Custom HTTP SMS"},
{id: "Infobip SMS", name: "Infobip SMS"},
{id: "Tencent Cloud SMS", name: "Tencent Cloud SMS"},
{id: "Baidu Cloud SMS", name: "Baidu Cloud SMS"},
@ -901,6 +925,8 @@ export function getProviderTypeOptions(category) {
{id: "Aliyun OSS", name: "Aliyun OSS"},
{id: "Tencent Cloud COS", name: "Tencent Cloud COS"},
{id: "Azure Blob", name: "Azure Blob"},
{id: "Qiniu Cloud Kodo", name: "Qiniu Cloud Kodo"},
{id: "Google Cloud Storage", name: "Google Cloud Storage"},
]
);
} else if (category === "SAML") {
@ -914,6 +940,7 @@ export function getProviderTypeOptions(category) {
{id: "Alipay", name: "Alipay"},
{id: "WeChat Pay", name: "WeChat Pay"},
{id: "PayPal", name: "PayPal"},
{id: "Stripe", name: "Stripe"},
{id: "GC", name: "GC"},
]);
} else if (category === "Captcha") {
@ -928,6 +955,7 @@ export function getProviderTypeOptions(category) {
} else if (category === "Web3") {
return ([
{id: "MetaMask", name: "MetaMask"},
{id: "Web3Onboard", name: "Web3-Onboard"},
]);
} else {
return [];

View File

@ -313,16 +313,6 @@ class SyncerEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:Table primary key"), i18next.t("syncer:Table primary key - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.syncer.tablePrimaryKey} onChange={e => {
this.updateSyncerField("tablePrimaryKey", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:Table columns"), i18next.t("syncer:Table columns - Tooltip"))} :

View File

@ -15,9 +15,11 @@
import React from "react";
import {Button, Card, Col, Input, InputNumber, List, Result, Row, Select, Space, Spin, Switch, Tag} from "antd";
import {withRouter} from "react-router-dom";
import {TotpMfaType} from "./auth/MfaSetupPage";
import * as GroupBackend from "./backend/GroupBackend";
import * as UserBackend from "./backend/UserBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
import EnableMfaModal from "./common/modal/EnableMfaModal";
import * as Setting from "./Setting";
import i18next from "i18next";
import CropperDivModal from "./common/modal/CropperDivModal.js";
@ -133,13 +135,29 @@ class UserEditPage extends React.Component {
this.setState({
application: res.data,
isGroupsVisible: res.data?.organizationObj.accountItems?.some((item) => item.name === "Groups" && item.visible),
});
});
}
getUserOrganization() {
if (this.state.user === null || this.state.organizations.length === 0) {
return null;
}
return this.state.organizations.filter(organization => organization.name === this.state.user.owner)[0];
}
isGroupsVisible() {
const organization = this.getUserOrganization();
if (!organization) {
return false;
} else {
return organization.accountItems?.some((item) => item.name === "Groups" && item.visible);
}
}
getGroups(organizationName) {
if (this.state.isGroupsVisible) {
if (this.isGroupsVisible()) {
GroupBackend.getGroups(organizationName)
.then((res) => {
if (res.status === "ok") {
@ -194,23 +212,6 @@ class UserEditPage extends React.Component {
return this.props.account.countryCode;
}
loadMore = (table, type) => {
return <div
style={{
textAlign: "center",
marginTop: 12,
height: 32,
lineHeight: "32px",
}}
>
<Button onClick={() => {
this.setState({
multiFactorAuths: Setting.addRow(table, {"type": type}),
});
}}>{i18next.t("general:Add")}</Button>
</div>;
};
deleteMfa = () => {
this.setState({
RemoveMfaLoading: true,
@ -401,7 +402,7 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"))} :
</Col>
<Col span={22} >
<PasswordModal user={this.state.user} organization={this.state.application?.organizationObj} account={this.props.account} disabled={disabled} />
<PasswordModal user={this.state.user} organization={this.getUserOrganization()} account={this.props.account} disabled={disabled} />
</Col>
</Row>
);
@ -442,7 +443,7 @@ class UserEditPage extends React.Component {
onChange={(value) => {
this.updateUserField("countryCode", value);
}}
countryCodes={this.state.application?.organizationObj.countryCodes}
countryCodes={this.getUserOrganization()?.countryCodes}
/>
<Input value={this.state.user.phone}
style={{width: "70%"}}
@ -558,7 +559,7 @@ class UserEditPage extends React.Component {
{name: "ID card back", value: "idCardBack"},
{name: "ID card with person", value: "idCardWithPerson"},
].map((entry) => {
return this.renderImage(this.state.user.properties[entry.value] || "", this.getIdCardType(entry.name), this.getIdCardText(entry.name), entry.value, disabled);
return this.renderImage(this.state.user.properties === null ? "" : (this.state.user.properties[entry.value] || ""), this.getIdCardType(entry.name), this.getIdCardText(entry.name), entry.value, disabled);
})
}
</Row>
@ -599,10 +600,10 @@ class UserEditPage extends React.Component {
</Col>
<Col span={22} >
{
this.state.application?.organizationObj.tags?.length > 0 ? (
this.getUserOrganization()?.tags?.length > 0 ? (
<Select virtual={false} style={{width: "100%"}} value={this.state.user.tag}
onChange={(value => {this.updateUserField("tag", value);})}
options={this.state.application.organizationObj.tags?.map((tag) => {
options={this.getUserOrganization()?.tags?.map((tag) => {
const tokens = tag.split("|");
const value = tokens[0];
const displayValue = Setting.getLanguage() !== "zh" ? tokens[0] : tokens[1];
@ -888,7 +889,7 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("mfa:Multi-factor authentication"), i18next.t("mfa:Multi-factor authentication - Tooltip "))} :
</Col>
<Col span={22} >
<Card title={i18next.t("mfa:Multi-factor methods")}
<Card size="small" title={i18next.t("mfa:Multi-factor methods")}
extra={this.state.multiFactorAuths?.some(mfaProps => mfaProps.enabled) ?
<PopconfirmModal
text={i18next.t("general:Disable")}
@ -935,11 +936,18 @@ class UserEditPage extends React.Component {
</Button>
}
</Space>
) : <Button type={"default"} onClick={() => {
this.props.history.push(`/mfa/setup?mfaType=${item.mfaType}`);
}}>
{i18next.t("mfa:Setup")}
</Button>}
) :
<Space>
{item.mfaType !== TotpMfaType && Setting.isAdminUser(this.props.account) && window.location.href.indexOf("/users") !== -1 ?
<EnableMfaModal user={this.state.user} mfaType={item.mfaType} onSuccess={() => {
this.getUser();
}} /> : null}
<Button type={"default"} onClick={() => {
this.props.history.push(`/mfa/setup?mfaType=${item.mfaType}`);
}}>
{i18next.t("mfa:Setup")}
</Button>
</Space>}
</List.Item>
)}
/>
@ -983,11 +991,13 @@ class UserEditPage extends React.Component {
<Col span={4} style={{textAlign: "center", margin: "auto"}} key={tag}>
{
imgUrl ?
<a target="_blank" rel="noreferrer" href={imgUrl} style={{marginBottom: "10px"}}>
<AccountAvatar src={imgUrl} alt={imgUrl} size={90} style={{marginBottom: "20px"}} />
</a>
<div style={{marginBottom: "10px"}}>
<a target="_blank" rel="noreferrer" href={imgUrl} style={{marginBottom: "10px"}}>
<AccountAvatar src={imgUrl} alt={imgUrl} height={150} />
</a>
</div>
:
<Col style={{height: "78%", border: "1px dotted grey", borderRadius: 3, marginBottom: 5}}>
<Col style={{height: "78%", border: "1px dotted grey", borderRadius: 3, marginBottom: "10px"}}>
<div style={{fontSize: 30, margin: 10}}>+</div>
<div style={{verticalAlign: "middle", marginBottom: 10}}>{`Upload ${title}...`}</div>
</Col>
@ -1008,7 +1018,7 @@ class UserEditPage extends React.Component {
</div>
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
{
this.state.application?.organizationObj.accountItems?.map(accountItem => {
this.getUserOrganization()?.accountItems?.map(accountItem => {
return (
<React.Fragment key={accountItem.name}>
{

View File

@ -17,7 +17,7 @@ import {MetaMaskAvatar} from "react-metamask-avatar";
class AccountAvatar extends React.Component {
render() {
const {src, size} = this.props;
const {src, size, width, height} = this.props;
// The avatar for Metamask account is directly generated by an algorithm based on the address
// src = "metamask:0xC304b2cC0Be8E9ce10fF3Afd34820Ed306A23600";
const matchMetaMask = src.match(/^metamask:(\w+)$/);
@ -27,9 +27,19 @@ class AccountAvatar extends React.Component {
<MetaMaskAvatar address={address} size={size} />
);
}
return (
<img width={size} height={size} src={src} />
);
if (size !== undefined) {
return (
<img width={size} height={size} src={src} />
);
} else if (width === undefined) {
return (
<img height={height} src={src} />
);
} else if (height === undefined) {
return (
<img width={width} src={src} />
);
}
}
}

View File

@ -95,7 +95,7 @@ class AuthCallback extends React.Component {
if (code === null) {
code = params.get("authCode");
}
// The code for Metamask is the JSON-serialized string of Web3AuthToken
// The code for Web3 is the JSON-serialized string of Web3AuthToken
// Due to the limited length of URLs, we only pass the web3AuthTokenKey
if (code === null) {
code = params.get("web3AuthTokenKey");

View File

@ -321,6 +321,10 @@ const authInfo = {
scope: "",
endpoint: "",
},
Web3Onboard: {
scope: "",
endpoint: "",
},
};
export function getProviderUrl(provider) {
@ -465,5 +469,7 @@ export function getAuthUrl(application, provider, method) {
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}&code_challenge=${codeChallenge}&code_challenge_method=S256`;
} else if (provider.type === "MetaMask") {
return `${redirectUri}?state=${state}`;
} else if (provider.type === "Web3Onboard") {
return `${redirectUri}?state=${state}`;
}
}

View File

@ -17,7 +17,7 @@ import i18next from "i18next";
import * as Provider from "./Provider";
import {getProviderLogoURL} from "../Setting";
import {GithubLoginButton, GoogleLoginButton} from "react-social-login-buttons";
import {authViaMetaMask} from "./Web3Auth";
import {authViaMetaMask, authViaWeb3Onboard} from "./Web3Auth";
import QqLoginButton from "./QqLoginButton";
import FacebookLoginButton from "./FacebookLoginButton";
import WeiboLoginButton from "./WeiboLoginButton";
@ -46,7 +46,7 @@ import {getEvent} from "./Util";
import {Modal} from "antd";
function getSigninButton(provider) {
const text = i18next.t("login:Sign in with {type}").replace("{type}", provider.type);
const text = i18next.t("login:Sign in with {type}").replace("{type}", provider.displayName !== "" ? provider.displayName : provider.type);
if (provider.type === "GitHub") {
return <GithubLoginButton text={text} align={"center"} />;
} else if (provider.type === "Google") {
@ -121,6 +121,8 @@ function goToSamlUrl(provider, location) {
export function goToWeb3Url(application, provider, method) {
if (provider.type === "MetaMask") {
authViaMetaMask(application, provider, method);
} else if (provider.type === "Web3Onboard") {
authViaWeb3Onboard(application, provider, method);
}
}

View File

@ -18,7 +18,25 @@ import {v4 as uuidv4} from "uuid";
import {SignTypedDataVersion, recoverTypedSignature} from "@metamask/eth-sig-util";
import {getAuthUrl} from "./Provider";
import {Buffer} from "buffer";
// import {toChecksumAddress} from "ethereumjs-util";
import Onboard from "@web3-onboard/core";
import injectedModule from "@web3-onboard/injected-wallets";
import infinityWalletModule from "@web3-onboard/infinity-wallet";
import sequenceModule from "@web3-onboard/sequence";
import trustModule from "@web3-onboard/trust";
import frontierModule from "@web3-onboard/frontier";
import tahoModule from "@web3-onboard/taho";
import coinbaseModule from "@web3-onboard/coinbase";
import gnosisModule from "@web3-onboard/gnosis";
// import keystoneModule from "@web3-onboard/keystone";
// import keepkeyModule from "@web3-onboard/keepkey";
// import dcentModule from "@web3-onboard/dcent";
// import ledgerModule from "@web3-onboard/ledger";
// import trezorModule from "@web3-onboard/trezor";
// import walletConnectModule from "@web3-onboard/walletconnect";
// import fortmaticModule from "@web3-onboard/fortmatic";
// import portisModule from "@web3-onboard/portis";
// import magicModule from "@web3-onboard/magic";
global.Buffer = Buffer;
export function generateNonce() {
@ -147,3 +165,174 @@ export async function authViaMetaMask(application, provider, method) {
showMessage("error", `${i18next.t("login:Failed to obtain MetaMask authorization")}: ${err.message}`);
}
}
const web3Wallets = {
// injected wallets
injected: {
label: "Injected",
wallet: injectedModule(),
},
// sdk wallets
coinbase: {
label: "Coinbase",
wallet: coinbaseModule(),
},
trust: {
label: "Trust",
wallet: trustModule(),
},
gnosis: {
label: "Gnosis",
wallet: gnosisModule(),
},
sequence: {
label: "Sequence",
wallet: sequenceModule(),
},
taho: {
label: "Taho",
wallet: tahoModule(),
},
frontier: {
label: "Frontier",
wallet: frontierModule(),
},
infinityWallet: {
label: "Infinity Wallet",
wallet: infinityWalletModule(),
},
// hardware wallets
// keystone: {
// label: "Keystone",
// wallet: keystoneModule(),
// },
// keepkey: {
// label: "KeepKey",
// wallet: keepkeyModule(),
// },
// dcent: {
// label: "D'CENT",
// wallet: dcentModule(),
// },
// some wallet need custome `apiKey` or `projectId` configure item
// const magic = magicModule({
// apiKey: "magicApiKey",
// });
// const fortmatic = fortmaticModule({
// apiKey: "fortmaticApiKey",
// });
// const portis = portisModule({
// apiKey: "portisApiKey",
// });
// const ledger = ledgerModule({
// projectId: "ledgerProjectId"
// });
// const walletConnect = walletConnectModule({
// projectId: "walletConnectProjectId",
// });
};
export function getWeb3OnboardWalletsOptions() {
return Object.entries(web3Wallets).map(([key, value]) => ({
label: value.label,
value: key,
}));
}
function getWeb3OnboardWallets(options) {
if (options === null || options === undefined || !Array.isArray(options)) {
return [];
}
return options.map(walletType => {
if (walletType && web3Wallets[walletType]?.wallet) {
return web3Wallets[walletType]?.wallet;
}
});
}
export function initWeb3Onboard(application, provider) {
// init wallet
// options = ["injected","coinbase",...]
const options = JSON.parse(provider.metadata);
const wallets = getWeb3OnboardWallets(options);
// init chain
// const InfuraKey = "2fa45cbe531e4e65be4fcbf408e651a8";
const chains = [
// {
// id: "0x1",
// token: "ETH",
// label: "Ethereum Mainnet",
// rpcUrl: `https://mainnet.infura.io/v3/${InfuraKey}`,
// },
// {
// id: "0x5",
// token: "ETH",
// label: "Goerli",
// rpcUrl: `https://goerli.infura.io/v3/${InfuraKey}`,
// },
{
id: "0x13881",
token: "MATIC",
label: "Polygon - Mumbai",
rpcUrl: "https://matic-mumbai.chainstacklabs.com",
},
{
id: "0x38",
token: "BNB",
label: "Binance",
rpcUrl: "https://bsc-dataseed.binance.org/",
},
{
id: "0xA",
token: "OETH",
label: "Optimism",
rpcUrl: "https://mainnet.optimism.io",
},
{
id: "0xA4B1",
token: "ARB-ETH",
label: "Arbitrum",
rpcUrl: "https://rpc.ankr.com/arbitrum",
},
];
const appMetadata = {
name: "Casdoor",
description: "Connect a wallet using Casdoor",
recommendedInjectedWallets: [
{name: "MetaMask", url: "https://metamask.io"},
{name: "Coinbase", url: "https://wallet.coinbase.com/"},
],
};
const web3Onboard = Onboard({
wallets,
chains,
appMetadata,
});
return web3Onboard;
}
export async function authViaWeb3Onboard(application, provider, method) {
try {
const onboard = initWeb3Onboard(application, provider);
const connectedWallets = await onboard.connectWallet();
if (connectedWallets.length > 0) {
const wallet = connectedWallets[0];
const account = wallet.accounts[0];
const address = account.address;
const token = {
address: address, // e.g."0xbd5444d31fe4139ee36bea29e43d4ac67ae276de"
walletType: wallet.label, // e.g."MetaMask"
createAt: Math.floor(new Date().getTime() / 1000),
};
setWeb3AuthToken(token);
const redirectUri = `${getAuthUrl(application, provider, method)}&web3AuthTokenKey=${getWeb3AuthTokenKey(address)}`;
goToLink(redirectUri);
}
} catch (err) {
showMessage("error", `${i18next.t("login:Failed to obtain Web3-Onboard authorization")}: ${err}`);
}
}

View File

@ -103,8 +103,8 @@ export function RemovePolicy(owner, name, policy) {
}).then(res => res.json());
}
export function getPolicies(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-policies?id=${owner}/${encodeURIComponent(name)}`, {
export function getPolicies(owner, name, adapterId = "") {
return fetch(`${Setting.ServerUrl}/api/get-policies?id=${owner}/${encodeURIComponent(name)}&adapterId=${adapterId}`, {
method: "GET",
credentials: "include",
headers: {

View File

@ -1,4 +1,4 @@
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -12,18 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package util
import * as Setting from "../Setting";
import xormadapter "github.com/casdoor/xorm-adapter/v3"
func CasbinToSlice(casbinRule xormadapter.CasbinRule) []string {
s := []string{
casbinRule.V0,
casbinRule.V1,
casbinRule.V2,
casbinRule.V3,
casbinRule.V4,
casbinRule.V5,
}
return s
export function getDashboard(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-dashboard`, {
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}

View File

@ -24,8 +24,8 @@ export function getEnforcers(owner, page = "", pageSize = "", field = "", value
}).then(res => res.json());
}
export function getEnforcer(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-enforcer?id=${owner}/${encodeURIComponent(name)}`, {
export function getEnforcer(owner, name, loadModelCfg = false) {
return fetch(`${Setting.ServerUrl}/api/get-enforcer?id=${owner}/${encodeURIComponent(name)}&loadModelCfg=${loadModelCfg}`, {
method: "GET",
credentials: "include",
headers: {

View File

@ -13,8 +13,11 @@
// limitations under the License.
import React from "react";
import {Card, Col, Row} from "antd";
import {Card, Col, Row, Spin, Statistic} from "antd";
import {ArrowUpOutlined} from "@ant-design/icons";
import * as ApplicationBackend from "../backend/ApplicationBackend";
import * as DashboardBackend from "../backend/DashboardBackend";
import * as echarts from "echarts";
import * as Setting from "../Setting";
import SingleCard from "./SingleCard";
import i18next from "i18next";
@ -25,11 +28,13 @@ class HomePage extends React.Component {
this.state = {
classes: props,
applications: null,
dashboardData: null,
};
}
UNSAFE_componentWillMount() {
this.getApplicationsByOrganization(this.props.account.owner);
this.getDashboard();
}
getApplicationsByOrganization(organizationName) {
@ -41,6 +46,21 @@ class HomePage extends React.Component {
});
}
getDashboard() {
DashboardBackend.getDashboard()
.then((res) => {
if (res.status === "ok") {
this.setState({
dashboardData: res.data,
}, () => {
this.renderEChart();
});
} else {
Setting.showMessage("error", res.msg);
}
});
}
getItems() {
let items = [];
if (Setting.isAdminUser(this.props.account)) {
@ -75,9 +95,53 @@ class HomePage extends React.Component {
return items;
}
renderEChart() {
const data = this.state.dashboardData;
const chartDom = document.getElementById("echarts-chart");
const myChart = echarts.init(chartDom);
const currentDate = new Date();
const dateArray = [];
for (let i = 30; i >= 0; i--) {
const date = new Date(currentDate);
date.setDate(date.getDate() - i);
const month = parseInt(date.getMonth()) + 1;
const day = parseInt(date.getDate());
const formattedDate = `${month}-${day}`;
dateArray.push(formattedDate);
}
const option = {
title: {text: i18next.t("home:Past 30 Days")},
tooltip: {trigger: "axis"},
legend: {data: [
i18next.t("general:Users"),
i18next.t("general:Providers"),
i18next.t("general:Applications"),
i18next.t("general:Organizations"),
i18next.t("general:Subscriptions"),
]},
grid: {left: "3%", right: "4%", bottom: "3%", containLabel: true},
xAxis: {type: "category", boundaryGap: false, data: dateArray},
yAxis: {type: "value"},
series: [
{name: i18next.t("general:Organizations"), type: "line", data: data?.organizationCounts},
{name: i18next.t("general:Users"), type: "line", data: data?.userCounts},
{name: i18next.t("general:Providers"), type: "line", data: data?.providerCounts},
{name: i18next.t("general:Applications"), type: "line", data: data?.applicationCounts},
{name: i18next.t("general:Subscriptions"), type: "line", data: data?.subscriptionCounts},
],
};
myChart.setOption(option);
}
renderCards() {
if (this.state.applications === null) {
return null;
const data = this.state.dashboardData;
if (data === null) {
return (
<div style={{display: "flex", justifyContent: "center", alignItems: "center", marginTop: "10%"}}>
<Spin size="large" tip={i18next.t("login:Loading")} style={{paddingTop: "10%"}} />
</div>
);
}
const items = this.getItems();
@ -96,24 +160,35 @@ class HomePage extends React.Component {
);
} else {
return (
<div style={{marginRight: "15px", marginLeft: "15px"}}>
<Row style={{marginLeft: "-20px", marginRight: "-20px", marginTop: "20px"}} gutter={24}>
{
items.map(item => {
return (
<SingleCard logo={item.logo} link={item.link} title={item.name} desc={item.organizer} time={item.createdTime} isSingle={items.length === 1} key={item.name} />
);
})
}
</Row>
</div>
<Row gutter={80}>
<Col span={50}>
<Card bordered={false} bodyStyle={{width: "100%", height: "150px", display: "flex", alignItems: "center", justifyContent: "center"}}>
<Statistic title={i18next.t("home:Total users")} fontSize="100px" value={data?.userCounts[30]} valueStyle={{fontSize: "30px"}} style={{width: "200px", paddingLeft: "10px"}} />
</Card>
</Col>
<Col span={50}>
<Card bordered={false} bodyStyle={{width: "100%", height: "150px", display: "flex", alignItems: "center", justifyContent: "center"}}>
<Statistic title={i18next.t("home:New users today")} fontSize="100px" value={data?.userCounts[30] - data?.userCounts[30 - 1]} valueStyle={{fontSize: "30px"}} prefix={<ArrowUpOutlined />} style={{width: "200px", paddingLeft: "10px"}} />
</Card>
</Col>
<Col span={50}>
<Card bordered={false} bodyStyle={{width: "100%", height: "150px", display: "flex", alignItems: "center", justifyContent: "center"}}>
<Statistic title={i18next.t("home:New users past 7 days")} value={data?.userCounts[30] - data?.userCounts[30 - 7]} valueStyle={{fontSize: "30px"}} prefix={<ArrowUpOutlined />} style={{width: "200px", paddingLeft: "10px"}} />
</Card>
</Col>
<Col span={50}>
<Card bordered={false} bodyStyle={{width: "100%", height: "150px", display: "flex", alignItems: "center", justifyContent: "center"}}>
<Statistic title={i18next.t("home:New users past 30 days")} value={data?.userCounts[30] - data?.userCounts[30 - 30]} valueStyle={{fontSize: "30px"}} prefix={<ArrowUpOutlined />} style={{width: "200px", paddingLeft: "10px"}} />
</Card>
</Col>
</Row>
);
}
}
render() {
return (
<div>
<div style={{display: "flex", justifyContent: "center", flexDirection: "column", alignItems: "center"}}>
<Row style={{width: "100%"}}>
<Col span={24} style={{display: "flex", justifyContent: "center"}} >
{
@ -121,6 +196,8 @@ class HomePage extends React.Component {
}
</Col>
</Row>
<div id="echarts-chart"
style={{width: "80%", height: "400px", textAlign: "center", marginTop: "20px"}}></div>
</div>
);
}

View File

@ -97,7 +97,7 @@ class OAuthWidget extends React.Component {
// should add the unlink user's info, cause the user may not be logged in, but a admin want to unlink the user.
user: this.props.user,
};
if (providerType === "MetaMask") {
if (providerType === "MetaMask" || providerType === "Web3Onboard") {
delWeb3AuthToken(linkedValue);
}
AuthBackend.unlink(body)
@ -158,7 +158,12 @@ class OAuthWidget extends React.Component {
</Col>
<Col span={24 - this.props.labelSpan} >
<AccountAvatar style={{marginRight: "10px"}} size={30} src={avatarUrl} alt={name} referrerPolicy="no-referrer" />
<span style={{width: this.props.labelSpan === 3 ? "300px" : "200px", display: (Setting.isMobile()) ? "inline" : "inline-block"}}>
<span style={{
width: this.props.labelSpan === 3 ? "300px" : "200px",
display: (Setting.isMobile()) ? "inline" : "inline-block",
overflow: "hidden",
textOverflow: "ellipsis",
}} title={name}>
{
linkedValue === "" ? (
`(${i18next.t("general:empty")})`

View File

@ -0,0 +1,113 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {Button, Modal} from "antd";
import i18next from "i18next";
import React from "react";
import {useEffect, useState} from "react";
import {EmailMfaType} from "../../auth/MfaSetupPage";
import * as MfaBackend from "../../backend/MfaBackend";
import * as Setting from "../../Setting";
const EnableMfaModal = ({user, mfaType, onSuccess}) => {
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
useEffect(() => {
if (!open || !user) {
return;
}
MfaBackend.MfaSetupInitiate({
mfaType,
...user,
}).then((res) => {
if (res.status === "error") {
Setting.showMessage("error", i18next.t("mfa:Failed to initiate MFA"));
}
});
}, [open]);
const handleOk = () => {
setLoading(true);
MfaBackend.MfaSetupEnable({
mfaType,
...user,
}).then(res => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Enabled successfully"));
setOpen(false);
onSuccess();
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to enable")}: ${res.msg}`);
}
}
).finally(() => {
setLoading(false);
});
};
const handleCancel = () => {
setOpen(false);
};
const showModal = () => {
if (!isValid()) {
if (mfaType === EmailMfaType) {
Setting.showMessage("error", i18next.t("signup:Please input your Email!"));
} else {
Setting.showMessage("error", i18next.t("signup:Please input your phone number!"));
}
return;
}
setOpen(true);
};
const renderText = () => {
return (
<p>{i18next.t("mfa:Please confirm the information below")}<br />
<b>{i18next.t("general:User")}</b>: {`${user.owner}/${user.name}`}<br />
{mfaType === EmailMfaType ?
<><b>{i18next.t("general:Email")}</b> : {user.email}</> :
<><b>{i18next.t("general:Phone")}</b> : {user.phone}</>}
</p>
);
};
const isValid = () => {
if (mfaType === EmailMfaType) {
return user.email !== "";
} else {
return user.phone !== "";
}
};
return (
<React.Fragment>
<Button type="primary" onClick={showModal}>
{i18next.t("general:Enable")}
</Button>
<Modal
title={i18next.t("mfa:Enable multi-factor authentication")}
open={open}
onOk={handleOk}
onCancel={handleCancel}
confirmLoading={loading}
>
{renderText()}
</Modal>
</React.Fragment>
);
};
export default EnableMfaModal;

View File

@ -27,6 +27,9 @@ import pt from "./locales/pt/data.json";
import it from "./locales/it/data.json";
import ms from "./locales/ms/data.json";
import tr from "./locales/tr/data.json";
import ar from "./locales/ar/data.json";
import he from "./locales/he/data.json";
import fi from "./locales/fi/data.json";
import * as Conf from "./Conf";
import {initReactI18next} from "react-i18next";
@ -45,6 +48,9 @@ const resources = {
it: it,
ms: ms,
tr: tr,
ar: ar,
he: he,
fi: fi,
};
function initLanguage() {
@ -103,6 +109,15 @@ function initLanguage() {
case "tr":
language = "tr";
break;
case "ar":
language = "ar";
break;
case "he":
language = "he";
break;
case "fi":
language = "fi";
break;
default:
language = Conf.DefaultLanguage;
}

1008
web/src/locales/ar/data.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
"New Adapter": "Neuer Adapter",
"Policies": "Richtlinien",
"Policies - Tooltip": "Casbin Richtlinienregeln",
"Rule type": "Rule type",
"Sync policies successfully": "Richtlinien synchronisiert"
},
"application": {
@ -165,16 +166,19 @@
"Adapter - Tooltip": "Tabellenname des Policy Stores",
"Adapters": "Adapter",
"Add": "Hinzufügen",
"Admin": "Admin",
"Affiliation URL": "Affiliation-URL",
"Affiliation URL - Tooltip": "Die Homepage-URL für die Zugehörigkeit",
"Application": "Applikation",
"Application - Tooltip": "Application - Tooltip",
"Applications": "Anwendungen",
"Applications that require authentication": "Anwendungen, die eine Authentifizierung erfordern",
"Authorization": "Authorization",
"Avatar": "Avatar",
"Avatar - Tooltip": "Öffentliches Avatarbild für den Benutzer",
"Back": "Back",
"Back Home": "Zurück nach Hause",
"Business & Payments": "Business & Payments",
"Cancel": "Abbrechen",
"Captcha": "Captcha",
"Cert": "Zertifikat",
@ -226,6 +230,7 @@
"Home - Tooltip": "Homepage der Anwendung",
"ID": "ID",
"ID - Tooltip": "Einzigartiger Zufallsstring",
"Identity": "Identity",
"Is enabled": "Ist aktiviert",
"Is enabled - Tooltip": "Festlegen, ob es verwendet werden kann",
"LDAPs": "LDAPs",
@ -234,6 +239,7 @@
"Languages - Tooltip": "Verfügbare Sprachen",
"Last name": "Nachname",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Symbole, die die Anwendung der Außenwelt präsentiert",
"MFA items": "MFA items",
@ -327,6 +333,7 @@
"Updated time": "Updated time",
"User": "Nutzer",
"User - Tooltip": "Stellen Sie sicher, dass der Benutzername korrekt ist",
"User Management": "User Management",
"User containers": "Nutzerpools",
"User type": "Benutzertyp",
"User type - Tooltip": "Tags, denen der Benutzer angehört, standardmäßig auf \"normaler Benutzer\" festgelegt",
@ -347,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN oder ID des LDAP-Serveradministrators",
@ -382,6 +396,7 @@
"Continue with": "Weitermachen mit",
"Email or phone": "E-Mail oder Telefon",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Passwort vergessen?",
"Loading": "Laden",
"Logging out...": "Ausloggen...",
@ -425,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -481,6 +497,7 @@
"Currency - Tooltip": "Wie USD, CNY usw.",
"Download Invoice": "Rechnung herunterladen",
"Edit Payment": "Zahlung bearbeiten",
"Failed reason": "Failed reason",
"Individual": "individuell",
"Invoice URL": "Rechnungs-URL",
"Invoice URL - Tooltip": "URL für den Download der Rechnung",
@ -514,7 +531,9 @@
"Product - Tooltip": "Produktname",
"Result": "Ergebnis",
"Return to Website": "Zurück zur Website",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "Die Zahlung ist fehlgeschlagen",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "Die Zahlung wird immer noch bearbeitet",
"Type - Tooltip": "Zahlungsmethode, die beim Kauf des Produkts verwendet wurde",
"You have successfully completed the payment": "Sie haben die Zahlung erfolgreich abgeschlossen",
@ -596,6 +615,7 @@
"SKU": "SKU",
"Sold": "Verkauft",
"Sold - Tooltip": "Menge verkauft",
"Stripe": "Stripe",
"Tag - Tooltip": "Tag des Produkts",
"Test buy page..": "Testkaufseite.",
"There is no payment channel for this product.": "Es gibt keinen Zahlungskanal für dieses Produkt.",
@ -608,14 +628,20 @@
"Access key - Tooltip": "Zugriffsschlüssel",
"Agent ID": "Agenten-ID",
"Agent ID - Tooltip": "Agenten-ID",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "App ID",
"App ID - Tooltip": "App-ID",
"App key": "App-Key",
"App key - Tooltip": "App-Schlüssel",
"App secret": "App-Secret",
"AppSecret - Tooltip": "App-Geheimnis",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "Auth-URL",
"Auth URL - Tooltip": "Auth-URL",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "Bucket",
"Bucket - Tooltip": "Name des Buckets",
"Can not parse metadata": "Kann Metadaten nicht durchsuchen / auswerten",
@ -634,6 +660,8 @@
"Client secret - Tooltip": "Client-Geheimnis",
"Client secret 2": "Client-Secret 2",
"Client secret 2 - Tooltip": "Der zweite Client-Secret-Key",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "Kopieren",
"Disable SSL": "SSL deaktivieren",
"Disable SSL - Tooltip": "Ob die Deaktivierung des SSL-Protokolls bei der Kommunikation mit dem STMP-Server erfolgen soll",
@ -649,6 +677,7 @@
"Enable QR code - Tooltip": "Ob das Scannen von QR-Codes zum Einloggen aktiviert werden soll",
"Endpoint": "Endpoint",
"Endpoint (Intranet)": "Endpoint (Intranet)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "From address - Tooltip",
"From name": "From name",
@ -667,6 +696,8 @@
"Method - Tooltip": "Anmeldeverfahren, QR-Code oder Silent-Login",
"New Provider": "Neuer Provider",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "parsen",
"Parse metadata successfully": "Metadaten erfolgreich analysiert",
"Path prefix": "Pfadpräfix",
@ -674,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "Bitte verwenden Sie WeChat und scanne den QR-Code ein, um dich anzumelden",
"Port": "Port",
"Port - Tooltip": "Stellen Sie sicher, dass der Port offen ist",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "ausgelöst",
"Provider URL": "Provider-URL",
"Provider URL - Tooltip": "URL zur Konfiguration des Dienstanbieters, dieses Feld dient nur als Referenz und wird in Casdoor nicht verwendet",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "Regions-ID",
"Region ID - Tooltip": "Regions-ID für den Dienstleister",
"Region endpoint for Internet": "Regionsendpunkt für das Internet",
@ -701,6 +740,8 @@
"Secret key - Tooltip": "Vom Server verwendet, um die API des Verifizierungscodes-Providers für die Verifizierung aufzurufen",
"Send Testing Email": "Senden Sie eine Test-E-Mail",
"Send Testing SMS": "Sende Test-SMS",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "Signatur Namen",
@ -733,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "UserInfo-URL",
"UserInfo URL - Tooltip": "UserInfo-URL",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
@ -815,6 +858,7 @@
"Error text": "Fehlermeldung",
"Error text - Tooltip": "Fehler Text",
"Is hashed": "ist gehasht",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "Neuer Syncer",
@ -823,9 +867,7 @@
"Table": "Tabelle",
"Table - Tooltip": "Name der Datenbanktabelle",
"Table columns": "Tabellenspalten",
"Table columns - Tooltip": "Spalten in der Tabelle, die an der Datensynchronisierung beteiligt sind. Spalten, die nicht an der Synchronisierung beteiligt sind, müssen nicht hinzugefügt werden",
"Table primary key": "Primärschlüssel der Tabelle",
"Table primary key - Tooltip": "Primärschlüssel in einer Tabelle, wie beispielsweise eine ID"
"Table columns - Tooltip": "Spalten in der Tabelle, die an der Datensynchronisierung beteiligt sind. Spalten, die nicht an der Synchronisierung beteiligt sind, müssen nicht hinzugefügt werden"
},
"system": {
"API Latency": "API Latenz",

View File

@ -11,6 +11,7 @@
"New Adapter": "New Adapter",
"Policies": "Policies",
"Policies - Tooltip": "Casbin policy rules",
"Rule type": "Rule type",
"Sync policies successfully": "Sync policies successfully"
},
"application": {
@ -165,16 +166,19 @@
"Adapter - Tooltip": "Table name of the policy store",
"Adapters": "Adapters",
"Add": "Add",
"Admin": "Admin",
"Affiliation URL": "Affiliation URL",
"Affiliation URL - Tooltip": "The homepage URL for the affiliation",
"Application": "Application",
"Application - Tooltip": "Application - Tooltip",
"Applications": "Applications",
"Applications that require authentication": "Applications that require authentication",
"Authorization": "Authorization",
"Avatar": "Avatar",
"Avatar - Tooltip": "Public avatar image for the user",
"Back": "Back",
"Back Home": "Back Home",
"Business & Payments": "Business & Payments",
"Cancel": "Cancel",
"Captcha": "Captcha",
"Cert": "Cert",
@ -226,6 +230,7 @@
"Home - Tooltip": "Home page of the application",
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -234,6 +239,7 @@
"Languages - Tooltip": "Available languages",
"Last name": "Last name",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"MFA items": "MFA items",
@ -327,6 +333,7 @@
"Updated time": "Updated time",
"User": "User",
"User - Tooltip": "Make sure the username is correct",
"User Management": "User Management",
"User containers": "User pools",
"User type": "User type",
"User type - Tooltip": "Tags that the user belongs to, defaulting to \"normal-user\"",
@ -347,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -382,6 +396,7 @@
"Continue with": "Continue with",
"Email or phone": "Email or phone",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"Loading": "Loading",
"Logging out...": "Logging out...",
@ -425,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -481,6 +497,7 @@
"Currency - Tooltip": "Like USD, CNY, etc.",
"Download Invoice": "Download Invoice",
"Edit Payment": "Edit Payment",
"Failed reason": "Failed reason",
"Individual": "Individual",
"Invoice URL": "Invoice URL",
"Invoice URL - Tooltip": "URL for downloading the invoice",
@ -514,7 +531,9 @@
"Product - Tooltip": "Product Name",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "The payment has failed",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
@ -596,6 +615,7 @@
"SKU": "SKU",
"Sold": "Sold",
"Sold - Tooltip": "Quantity sold",
"Stripe": "Stripe",
"Tag - Tooltip": "Tag of product",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
@ -608,14 +628,20 @@
"Access key - Tooltip": "Access key",
"Agent ID": "Agent ID",
"Agent ID - Tooltip": "Agent ID",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "App ID",
"App ID - Tooltip": "App ID",
"App key": "App key",
"App key - Tooltip": "App key",
"App secret": "App secret",
"AppSecret - Tooltip": "App secret",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "Auth URL",
"Auth URL - Tooltip": "Auth URL",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "Bucket",
"Bucket - Tooltip": "Name of bucket",
"Can not parse metadata": "Can not parse metadata",
@ -634,6 +660,8 @@
"Client secret - Tooltip": "Client secret",
"Client secret 2": "Client secret 2",
"Client secret 2 - Tooltip": "The second client secret key",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "Copy",
"Disable SSL": "Disable SSL",
"Disable SSL - Tooltip": "Whether to disable SSL protocol when communicating with STMP server",
@ -649,6 +677,7 @@
"Enable QR code - Tooltip": "Whether to allow scanning QR code to login",
"Endpoint": "Endpoint",
"Endpoint (Intranet)": "Endpoint (Intranet)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "Email address of \"From\"",
"From name": "From name",
@ -667,6 +696,8 @@
"Method - Tooltip": "Login method, QR code or silent login",
"New Provider": "New Provider",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "Parse",
"Parse metadata successfully": "Parse metadata successfully",
"Path prefix": "Path prefix",
@ -674,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "Please use WeChat and scan the QR code to sign in",
"Port": "Port",
"Port - Tooltip": "Make sure the port is open",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "Prompted",
"Provider URL": "Provider URL",
"Provider URL - Tooltip": "URL for configuring the service provider, this field is only used for reference and is not used in Casdoor",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "Region ID",
"Region ID - Tooltip": "Region ID for the service provider",
"Region endpoint for Internet": "Region endpoint for Internet",
@ -701,6 +740,8 @@
"Secret key - Tooltip": "Used by the server to call the verification code provider API for verification",
"Send Testing Email": "Send Testing Email",
"Send Testing SMS": "Send Testing SMS",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "Sign Name",
@ -733,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "UserInfo URL",
"UserInfo URL - Tooltip": "UserInfo URL",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
@ -815,6 +858,7 @@
"Error text": "Error text",
"Error text - Tooltip": "Error text",
"Is hashed": "Is hashed",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
@ -823,9 +867,7 @@
"Table": "Table",
"Table - Tooltip": "Name of database table",
"Table columns": "Table columns",
"Table columns - Tooltip": "Columns in the table involved in data synchronization. Columns that are not involved in synchronization do not need to be added",
"Table primary key": "Table primary key",
"Table primary key - Tooltip": "Table primary key, such as id"
"Table columns - Tooltip": "Columns in the table involved in data synchronization. Columns that are not involved in synchronization do not need to be added"
},
"system": {
"API Latency": "API Latency",

View File

@ -11,6 +11,7 @@
"New Adapter": "Nuevo adaptador",
"Policies": "Políticas",
"Policies - Tooltip": "Reglas de política de Casbin",
"Rule type": "Rule type",
"Sync policies successfully": "Sincronizar políticas correctamente"
},
"application": {
@ -165,16 +166,19 @@
"Adapter - Tooltip": "Nombre de la tabla de la tienda de políticas",
"Adapters": "Adaptadores",
"Add": "Añadir",
"Admin": "Admin",
"Affiliation URL": "URL de afiliación",
"Affiliation URL - Tooltip": "La URL de la página de inicio para la afiliación",
"Application": "Aplicación",
"Application - Tooltip": "Application - Tooltip",
"Applications": "Aplicaciones",
"Applications that require authentication": "Aplicaciones que requieren autenticación",
"Authorization": "Authorization",
"Avatar": "Avatar",
"Avatar - Tooltip": "Imagen de avatar pública para el usuario",
"Back": "Back",
"Back Home": "Regreso a casa",
"Business & Payments": "Business & Payments",
"Cancel": "Cancelar",
"Captcha": "Captcha",
"Cert": "ificado",
@ -226,6 +230,7 @@
"Home - Tooltip": "Página de inicio de la aplicación",
"ID": "identificación",
"ID - Tooltip": "Cadena aleatoria única",
"Identity": "Identity",
"Is enabled": "Está habilitado",
"Is enabled - Tooltip": "Establecer si se puede usar",
"LDAPs": "LDAPs",
@ -234,6 +239,7 @@
"Languages - Tooltip": "Idiomas disponibles",
"Last name": "Apellido",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logotipo",
"Logo - Tooltip": "Iconos que la aplicación presenta al mundo exterior",
"MFA items": "MFA items",
@ -327,6 +333,7 @@
"Updated time": "Updated time",
"User": "Usuario",
"User - Tooltip": "Asegúrate de que el nombre de usuario sea correcto",
"User Management": "User Management",
"User containers": "Piscinas de usuarios",
"User type": "Tipo de usuario",
"User type - Tooltip": "Etiquetas a las que el usuario pertenece, con una configuración predeterminada en \"usuario-normal\"",
@ -347,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Administrador",
"Admin - Tooltip": "CN o ID del administrador del servidor LDAP",
@ -382,6 +396,7 @@
"Continue with": "Continúe con",
"Email or phone": "Correo electrónico o teléfono",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "¿Olvidaste tu contraseña?",
"Loading": "Cargando",
"Logging out...": "Cerrando sesión...",
@ -425,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -481,6 +497,7 @@
"Currency - Tooltip": "Como USD, CNY, etc.",
"Download Invoice": "Descargar factura",
"Edit Payment": "Editar pago",
"Failed reason": "Failed reason",
"Individual": "Individuo",
"Invoice URL": "URL de factura",
"Invoice URL - Tooltip": "URL para descargar la factura",
@ -514,7 +531,9 @@
"Product - Tooltip": "Nombre del producto",
"Result": "Resultado",
"Return to Website": "Regresar al sitio web",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "El pago ha fallado",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "El pago aún está en proceso",
"Type - Tooltip": "Método de pago utilizado al comprar el producto",
"You have successfully completed the payment": "Has completado el pago exitosamente",
@ -596,6 +615,7 @@
"SKU": "SKU",
"Sold": "Vendido",
"Sold - Tooltip": "Cantidad vendida",
"Stripe": "Stripe",
"Tag - Tooltip": "Etiqueta de producto",
"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.",
@ -608,14 +628,20 @@
"Access key - Tooltip": "Clave de acceso",
"Agent ID": "Identificador de agente",
"Agent ID - Tooltip": "Identificador de agente",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "ID de aplicación",
"App ID - Tooltip": "Identificador de la aplicación",
"App key": "Clave de aplicación",
"App key - Tooltip": "Clave de aplicación",
"App secret": "Secreto de la aplicación",
"AppSecret - Tooltip": "Secreto de aplicación",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "URL de autenticación",
"Auth URL - Tooltip": "URL de autenticación",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "Cubo",
"Bucket - Tooltip": "Nombre del balde",
"Can not parse metadata": "No se puede analizar los metadatos",
@ -634,6 +660,8 @@
"Client secret - Tooltip": "Secreto del cliente",
"Client secret 2": "Secreto del cliente 2",
"Client secret 2 - Tooltip": "La segunda clave secreta del cliente",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "Copiar",
"Disable SSL": "Desactivar SSL",
"Disable SSL - Tooltip": "¿Hay que desactivar el protocolo SSL al comunicarse con el servidor STMP?",
@ -649,6 +677,7 @@
"Enable QR code - Tooltip": "Si permitir el escaneo de códigos QR para acceder",
"Endpoint": "Punto final",
"Endpoint (Intranet)": "Punto final (intranet)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "From address - Tooltip",
"From name": "From name",
@ -667,6 +696,8 @@
"Method - Tooltip": "Método de inicio de sesión, código QR o inicio de sesión silencioso",
"New Provider": "Nuevo proveedor",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "Analizar",
"Parse metadata successfully": "Analizar los metadatos con éxito",
"Path prefix": "Prefijo de ruta",
@ -674,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "Por favor, utiliza WeChat y escanea el código QR para iniciar sesión",
"Port": "Puerto",
"Port - Tooltip": "Asegúrate de que el puerto esté abierto",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "Estimulado",
"Provider URL": "URL del proveedor",
"Provider URL - Tooltip": "Dirección URL para configurar el proveedor de servicios, este campo sólo se utiliza como referencia y no se utiliza en Casdoor",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "ID de región",
"Region ID - Tooltip": "Identificación de región para el proveedor de servicios",
"Region endpoint for Internet": "Punto final de la región para Internet",
@ -701,6 +740,8 @@
"Secret key - Tooltip": "Utilizado por el servidor para llamar a la API del proveedor de códigos de verificación para verificar",
"Send Testing Email": "Enviar correo electrónico de prueba",
"Send Testing SMS": "Enviar SMS de prueba",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "Firma de Nombre",
@ -733,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "URL de información del usuario",
"UserInfo URL - Tooltip": "URL de información de usuario",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "administrador (compartido)"
},
"record": {
@ -815,6 +858,7 @@
"Error text": "Texto de error",
"Error text - Tooltip": "Texto de error",
"Is hashed": "Está encriptado",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "Nuevo Syncer",
@ -823,9 +867,7 @@
"Table": "Mesa",
"Table - Tooltip": "Nombre de la tabla de la base de datos",
"Table columns": "Columnas de tabla",
"Table columns - Tooltip": "Columnas en la tabla involucradas en la sincronización de datos. Las columnas que no estén involucradas en la sincronización no necesitan ser añadidas",
"Table primary key": "Clave primaria de la tabla",
"Table primary key - Tooltip": "Clave primaria de la tabla, como id"
"Table columns - Tooltip": "Columnas en la tabla involucradas en la sincronización de datos. Las columnas que no estén involucradas en la sincronización no necesitan ser añadidas"
},
"system": {
"API Latency": "Retraso API",

1008
web/src/locales/fi/data.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
"New Adapter": "Nouvel adaptateur",
"Policies": "Politiques",
"Policies - Tooltip": "Règles de politique Casbin",
"Rule type": "Rule type",
"Sync policies successfully": "Synchronisation des politiques réussie"
},
"application": {
@ -165,16 +166,19 @@
"Adapter - Tooltip": "Nom de la table du magasin de politique",
"Adapters": "Adaptateurs",
"Add": "Ajouter",
"Admin": "Admin",
"Affiliation URL": "URL d'affiliation",
"Affiliation URL - Tooltip": "La URL de la page d'accueil pour l'affiliation",
"Application": "Application",
"Application - Tooltip": "Application - Tooltip",
"Applications": "Applications",
"Applications that require authentication": "Applications qui nécessitent une authentification",
"Authorization": "Authorization",
"Avatar": "Avatar",
"Avatar - Tooltip": "Image d'avatar public pour l'utilisateur",
"Back": "Back",
"Back Home": "Retour à la maison",
"Business & Payments": "Business & Payments",
"Cancel": "Annuler",
"Captcha": "Captcha (prononcé « kæptʃə » en anglais) est un acronyme qui signifie « Completely Automated Public Turing test to tell Computers and Humans Apart ». En français, le terme est souvent traduit par « test de Turing complètement automatisé et public pour différencier les ordinateurs et les humains ». Le système est utilisé pour empêcher les spammeurs, les robots et les logiciels malveillants d'accéder aux sites Web en leur demandant de résoudre une tâche de reconnaissance de caractères ou d'images, qui peut être difficile pour les machines à comprendre mais facile pour les humains",
"Cert": "ainement",
@ -226,6 +230,7 @@
"Home - Tooltip": "Page d'accueil de l'application",
"ID": "Identité",
"ID - Tooltip": "Chaîne unique aléatoire",
"Identity": "Identity",
"Is enabled": "Est activé",
"Is enabled - Tooltip": "Définir s'il peut être utilisé",
"LDAPs": "LDAPs",
@ -234,6 +239,7 @@
"Languages - Tooltip": "Langues disponibles",
"Last name": "Nom de famille",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icônes que l'application présente au monde extérieur",
"MFA items": "MFA items",
@ -327,6 +333,7 @@
"Updated time": "Updated time",
"User": "Utilisateur",
"User - Tooltip": "Assurez-vous que le nom d'utilisateur est correct",
"User Management": "User Management",
"User containers": "Piscines d'utilisateurs",
"User type": "Type d'utilisateur",
"User type - Tooltip": "Balises auxquelles l'utilisateur appartient, avec une valeur par défaut \"utilisateur-normal\"",
@ -347,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN ou ID de l'administrateur du serveur LDAP",
@ -382,6 +396,7 @@
"Continue with": "Continuer avec",
"Email or phone": "Email ou téléphone",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Mot de passe oublié ?",
"Loading": "Chargement",
"Logging out...": "Déconnexion...",
@ -425,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -481,6 +497,7 @@
"Currency - Tooltip": "Comme USD, CNY, etc.",
"Download Invoice": "Télécharger la facture",
"Edit Payment": "Modifier le paiement",
"Failed reason": "Failed reason",
"Individual": "Individu",
"Invoice URL": "URL de facture",
"Invoice URL - Tooltip": "URL pour télécharger la facture",
@ -514,7 +531,9 @@
"Product - Tooltip": "Nom du produit",
"Result": "Résultat",
"Return to Website": "Retourner sur le site web",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "Le paiement a échoué",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "Le paiement est encore en cours de traitement",
"Type - Tooltip": "Méthode de paiement utilisée lors de l'achat du produit",
"You have successfully completed the payment": "Vous avez effectué le paiement avec succès",
@ -596,6 +615,7 @@
"SKU": "SKU",
"Sold": "Vendu",
"Sold - Tooltip": "Quantité vendue",
"Stripe": "Stripe",
"Tag - Tooltip": "Étiquette de produit",
"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.",
@ -608,14 +628,20 @@
"Access key - Tooltip": "Clé d'accès",
"Agent ID": "Identifiant d'agent",
"Agent ID - Tooltip": "Identifiant d'agent",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "Identifiant d'application",
"App ID - Tooltip": "Identifiant d'application",
"App key": "Clé d'application",
"App key - Tooltip": "Clé d'application",
"App secret": "Secret d'application",
"AppSecret - Tooltip": "Secret de l'application",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "URL d'authentification",
"Auth URL - Tooltip": "URL d'authentification",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "seau",
"Bucket - Tooltip": "Nom du seau",
"Can not parse metadata": "Impossible d'analyser les métadonnées",
@ -634,6 +660,8 @@
"Client secret - Tooltip": "Secret client",
"Client secret 2": "Secret client 2",
"Client secret 2 - Tooltip": "La deuxième clé secrète du client",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "Copie",
"Disable SSL": "Désactiver SSL",
"Disable SSL - Tooltip": "Doit-on désactiver le protocole SSL lors de la communication avec le serveur STMP ?",
@ -649,6 +677,7 @@
"Enable QR code - Tooltip": "Doit-on autoriser la numérisation de QR code pour se connecter ?",
"Endpoint": "Point final",
"Endpoint (Intranet)": "Point final (intranet)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "From address - Tooltip",
"From name": "From name",
@ -667,6 +696,8 @@
"Method - Tooltip": "Méthode de connexion, code QR ou connexion silencieuse",
"New Provider": "Nouveau fournisseur",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "Parser",
"Parse metadata successfully": "Parcourir les métadonnées avec succès",
"Path prefix": "Préfixe de chemin",
@ -674,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "Veuillez utiliser WeChat et scanner le code QR pour vous connecter",
"Port": "Port",
"Port - Tooltip": "Assurez-vous que le port est ouvert",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "Incité",
"Provider URL": "URL du fournisseur",
"Provider URL - Tooltip": "URL pour configurer le fournisseur de services, ce champ est uniquement utilisé à titre de référence et n'est pas utilisé dans Casdoor",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "Identifiant de région",
"Region ID - Tooltip": "Identifiant de région pour le fournisseur de services",
"Region endpoint for Internet": "Point de terminaison de région pour Internet",
@ -701,6 +740,8 @@
"Secret key - Tooltip": "Utilisé par le serveur pour appeler l'API du fournisseur de code de vérification pour vérifier",
"Send Testing Email": "Envoyer un e-mail de test",
"Send Testing SMS": "Envoyer des messages SMS de tests",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "Nom de signature",
@ -733,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "URL d'informations utilisateur",
"UserInfo URL - Tooltip": "URL d'informations sur l'utilisateur",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Partagé)"
},
"record": {
@ -815,6 +858,7 @@
"Error text": "Texte d'erreur",
"Error text - Tooltip": "Texte d'erreur",
"Is hashed": "Est-haché",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "Nouveau synchroniseur",
@ -823,9 +867,7 @@
"Table": "Table",
"Table - Tooltip": "Nom de la table de base de données",
"Table columns": "Colonnes de table",
"Table columns - Tooltip": "Colonnes dans la table impliquées dans la synchronisation des données. Les colonnes qui ne sont pas impliquées dans la synchronisation n'ont pas besoin d'être ajoutées",
"Table primary key": "Clé primaire de table",
"Table primary key - Tooltip": "Clé primaire de la table, telle que l'id"
"Table columns - Tooltip": "Colonnes dans la table impliquées dans la synchronisation des données. Les colonnes qui ne sont pas impliquées dans la synchronisation n'ont pas besoin d'être ajoutées"
},
"system": {
"API Latency": "Retard API",

1008
web/src/locales/he/data.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
"New Adapter": "Adapter Baru",
"Policies": "Kebijakan",
"Policies - Tooltip": "Kebijakan aturan Casbin",
"Rule type": "Rule type",
"Sync policies successfully": "Sinkronisasi kebijakan berhasil dilakukan"
},
"application": {
@ -165,16 +166,19 @@
"Adapter - Tooltip": "Nama tabel dari penyimpanan kebijakan",
"Adapters": "Adaptor",
"Add": "Tambahkan",
"Admin": "Admin",
"Affiliation URL": "URL Afiliasi",
"Affiliation URL - Tooltip": "URL halaman depan untuk afiliasi",
"Application": "Aplikasi",
"Application - Tooltip": "Application - Tooltip",
"Applications": "Aplikasi",
"Applications that require authentication": "Aplikasi yang memerlukan autentikasi",
"Authorization": "Authorization",
"Avatar": "Avatar",
"Avatar - Tooltip": "Gambar avatar publik untuk pengguna",
"Back": "Back",
"Back Home": "Kembali ke Rumah",
"Business & Payments": "Business & Payments",
"Cancel": "Membatalkan",
"Captcha": "Captcha",
"Cert": "Sertifikat",
@ -226,6 +230,7 @@
"Home - Tooltip": "Halaman utama aplikasi",
"ID": "ID",
"ID - Tooltip": "Karakter acak unik",
"Identity": "Identity",
"Is enabled": "Diaktifkan",
"Is enabled - Tooltip": "Atur apakah itu dapat digunakan",
"LDAPs": "LDAPs",
@ -234,6 +239,7 @@
"Languages - Tooltip": "Bahasa yang tersedia",
"Last name": "Nama belakang",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Ikon-ikon yang disajikan aplikasi ke dunia luar",
"MFA items": "MFA items",
@ -327,6 +333,7 @@
"Updated time": "Updated time",
"User": "Pengguna",
"User - Tooltip": "Pastikan username-nya benar",
"User Management": "User Management",
"User containers": "User pools",
"User type": "Jenis pengguna",
"User type - Tooltip": "Tag yang dimiliki oleh pengguna, defaultnya adalah \"normal-user\"",
@ -347,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN atau ID dari administrator server LDAP",
@ -382,6 +396,7 @@
"Continue with": "Lanjutkan dengan",
"Email or phone": "Email atau telepon",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Lupa kata sandi?",
"Loading": "Memuat",
"Logging out...": "Keluar...",
@ -425,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -481,6 +497,7 @@
"Currency - Tooltip": "Seperti USD, CNY, dll.",
"Download Invoice": "Unduh Faktur",
"Edit Payment": "Edit Pembayaran",
"Failed reason": "Failed reason",
"Individual": "Individu",
"Invoice URL": "URL Faktur",
"Invoice URL - Tooltip": "URL untuk mengunduh faktur",
@ -514,7 +531,9 @@
"Product - Tooltip": "Nama Produk",
"Result": "Hasil",
"Return to Website": "Kembali ke Situs Web",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "Pembayaran gagal",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "Pembayaran masih dalam proses",
"Type - Tooltip": "Metode pembayaran yang digunakan saat membeli produk",
"You have successfully completed the payment": "Anda telah berhasil menyelesaikan pembayaran",
@ -596,6 +615,7 @@
"SKU": "SKU",
"Sold": "Terjual",
"Sold - Tooltip": "Jumlah terjual",
"Stripe": "Stripe",
"Tag - Tooltip": "Tag produk",
"Test buy page..": "Halaman pembelian uji coba.",
"There is no payment channel for this product.": "Tidak ada saluran pembayaran untuk produk ini.",
@ -608,14 +628,20 @@
"Access key - Tooltip": "Kunci akses",
"Agent ID": "ID agen",
"Agent ID - Tooltip": "ID Agen",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "ID Aplikasi",
"App ID - Tooltip": "ID Aplikasi",
"App key": "Kunci aplikasi",
"App key - Tooltip": "Kunci aplikasi",
"App secret": "Rahasia aplikasi",
"AppSecret - Tooltip": "Rahasia aplikasi",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "URL Otorisasi",
"Auth URL - Tooltip": "URL terautentikasi",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "Ember",
"Bucket - Tooltip": "Nama ember",
"Can not parse metadata": "Tidak dapat mengurai metadata",
@ -634,6 +660,8 @@
"Client secret - Tooltip": "Rahasia klien",
"Client secret 2": "Rahasia klien 2",
"Client secret 2 - Tooltip": "Kunci rahasia klien kedua",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "Salin",
"Disable SSL": "Menonaktifkan SSL",
"Disable SSL - Tooltip": "Apakah perlu menonaktifkan protokol SSL saat berkomunikasi dengan server STMP?",
@ -649,6 +677,7 @@
"Enable QR code - Tooltip": "Apakah diizinkan untuk memindai kode QR untuk masuk?",
"Endpoint": "Titik akhir",
"Endpoint (Intranet)": "Titik Akhir (Intranet)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "From address - Tooltip",
"From name": "From name",
@ -667,6 +696,8 @@
"Method - Tooltip": "Metode login, kode QR atau login tanpa suara",
"New Provider": "Penyedia Baru",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "Parse: Memecah atau mengurai data atau teks menjadi bagian-bagian yang lebih kecil dan lebih mudah dipahami atau dimanipulasi",
"Parse metadata successfully": "Berhasil mem-parse metadata",
"Path prefix": "Awalan jalur",
@ -674,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "Silakan gunakan WeChat dan pindai kode QR untuk masuk",
"Port": "Pelabuhan",
"Port - Tooltip": "Pastikan port terbuka",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "Mendorong",
"Provider URL": "URL penyedia",
"Provider URL - Tooltip": "URL untuk melakukan konfigurasi service provider, kolom ini hanya digunakan sebagai referensi dan tidak digunakan dalam Casdoor",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "Daerah ID",
"Region ID - Tooltip": "Daerah ID untuk penyedia layanan",
"Region endpoint for Internet": "Titik akhir wilayah untuk Internet",
@ -701,6 +740,8 @@
"Secret key - Tooltip": "Digunakan oleh server untuk memanggil API penyedia kode verifikasi untuk melakukan verifikasi",
"Send Testing Email": "Kirim Email Uji Coba",
"Send Testing SMS": "Kirim SMS Uji Coba",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "Tanda Tangan",
@ -733,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "URL UserInfo",
"UserInfo URL - Tooltip": "URL Informasi Pengguna",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "Admin (Berbagi)"
},
"record": {
@ -815,6 +858,7 @@
"Error text": "Teks kesalahan",
"Error text - Tooltip": "Teks kesalahan",
"Is hashed": "Apakah di-hash?",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "Sinkronisasi Baru",
@ -823,9 +867,7 @@
"Table": "Tabel",
"Table - Tooltip": "Nama tabel database",
"Table columns": "Kolom tabel",
"Table columns - Tooltip": "Kolom pada tabel yang terlibat dalam sinkronisasi data. Kolom yang tidak terlibat dalam sinkronisasi tidak perlu ditambahkan",
"Table primary key": "Kunci utama tabel",
"Table primary key - Tooltip": "Kunci primer tabel, seperti id"
"Table columns - Tooltip": "Kolom pada tabel yang terlibat dalam sinkronisasi data. Kolom yang tidak terlibat dalam sinkronisasi tidak perlu ditambahkan"
},
"system": {
"API Latency": "API Latency",

View File

@ -1,6 +1,5 @@
{
"account": {
"Chats & Messages": "Chats & Messages",
"Logout": "Logout",
"My Account": "My Account",
"Sign Up": "Sign Up"
@ -12,6 +11,7 @@
"New Adapter": "New Adapter",
"Policies": "Policies",
"Policies - Tooltip": "Casbin policy rules",
"Rule type": "Rule type",
"Sync policies successfully": "Sync policies successfully"
},
"application": {
@ -127,19 +127,6 @@
"Scope - Tooltip": "Usage scenarios of the certificate",
"Type - Tooltip": "Type of certificate"
},
"chat": {
"AI": "AI",
"Edit Chat": "Edit Chat",
"Group": "Group",
"Message count": "Message count",
"New Chat": "New Chat",
"Single": "Single",
"User1": "User1",
"User1 - Tooltip": "User1 - Tooltip",
"User2": "User2",
"User2 - Tooltip": "User2 - Tooltip",
"Users - Tooltip": "Users - Tooltip"
},
"code": {
"Code you received": "Code you received",
"Email code": "Email code",
@ -179,22 +166,24 @@
"Adapter - Tooltip": "Table name of the policy store",
"Adapters": "Adapters",
"Add": "Add",
"Admin": "Admin",
"Affiliation URL": "Affiliation URL",
"Affiliation URL - Tooltip": "The homepage URL for the affiliation",
"Application": "Application",
"Application - Tooltip": "Application - Tooltip",
"Applications": "Applications",
"Applications that require authentication": "Applications that require authentication",
"Authorization": "Authorization",
"Avatar": "Avatar",
"Avatar - Tooltip": "Public avatar image for the user",
"Back": "Back",
"Back Home": "Back Home",
"Business & Payments": "Business & Payments",
"Cancel": "Cancel",
"Captcha": "Captcha",
"Cert": "Cert",
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Chats": "Chats",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
@ -224,7 +213,6 @@
"Failed to connect to server": "Failed to connect to server",
"Failed to delete": "Failed to delete",
"Failed to enable": "Failed to enable",
"Failed to get answer": "Failed to get answer",
"Failed to remove": "Failed to remove",
"Failed to save": "Failed to save",
"Failed to verify": "Failed to verify",
@ -242,6 +230,7 @@
"Home - Tooltip": "Home page of the application",
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -250,6 +239,7 @@
"Languages - Tooltip": "Available languages",
"Last name": "Last name",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"MFA items": "MFA items",
@ -257,7 +247,6 @@
"Master password": "Master password",
"Master password - Tooltip": "Can be used to log in to all users under this organization, making it convenient for administrators to log in as this user to solve technical issues",
"Menu": "Menu",
"Messages": "Messages",
"Method": "Method",
"Model": "Model",
"Model - Tooltip": "Casbin access control model",
@ -344,6 +333,7 @@
"Updated time": "Updated time",
"User": "User",
"User - Tooltip": "Make sure the username is correct",
"User Management": "User Management",
"User containers": "User pools",
"User type": "User type",
"User type - Tooltip": "Tags that the user belongs to, defaulting to \"normal-user\"",
@ -364,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -399,6 +396,7 @@
"Continue with": "Continue with",
"Email or phone": "Email or phone",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"Loading": "Loading",
"Logging out...": "Logging out...",
@ -425,16 +423,6 @@
"sign up now": "sign up now",
"username, Email or phone": "username, Email or phone"
},
"message": {
"Author": "Author",
"Author - Tooltip": "Author - Tooltip",
"Chat": "Chat",
"Chat - Tooltip": "Chat - Tooltip",
"Edit Message": "Edit Message",
"New Message": "New Message",
"Text": "Text",
"Text - Tooltip": "Text - Tooltip"
},
"mfa": {
"Each time you sign in to your Account, you'll need your password and a authentication code": "Each time you sign in to your Account, you'll need your password and a authentication code",
"Enable multi-factor authentication": "Enable multi-factor authentication",
@ -452,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -508,6 +497,7 @@
"Currency - Tooltip": "Like USD, CNY, etc.",
"Download Invoice": "Download Invoice",
"Edit Payment": "Edit Payment",
"Failed reason": "Failed reason",
"Individual": "Individual",
"Invoice URL": "Invoice URL",
"Invoice URL - Tooltip": "URL for downloading the invoice",
@ -541,7 +531,9 @@
"Product - Tooltip": "Product Name",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "The payment has failed",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
@ -623,6 +615,7 @@
"SKU": "SKU",
"Sold": "Sold",
"Sold - Tooltip": "Quantity sold",
"Stripe": "Stripe",
"Tag - Tooltip": "Tag of product",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
@ -635,14 +628,20 @@
"Access key - Tooltip": "Access key",
"Agent ID": "Agent ID",
"Agent ID - Tooltip": "Agent ID",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "App ID",
"App ID - Tooltip": "App ID",
"App key": "App key",
"App key - Tooltip": "App key",
"App secret": "App secret",
"AppSecret - Tooltip": "App secret",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "Auth URL",
"Auth URL - Tooltip": "Auth URL",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "Bucket",
"Bucket - Tooltip": "Name of bucket",
"Can not parse metadata": "Can not parse metadata",
@ -661,6 +660,8 @@
"Client secret - Tooltip": "Client secret",
"Client secret 2": "Client secret 2",
"Client secret 2 - Tooltip": "The second client secret key",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "Copy",
"Disable SSL": "Disable SSL",
"Disable SSL - Tooltip": "Whether to disable SSL protocol when communicating with STMP server",
@ -676,6 +677,7 @@
"Enable QR code - Tooltip": "Whether to allow scanning QR code to login",
"Endpoint": "Endpoint",
"Endpoint (Intranet)": "Endpoint (Intranet)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "Email address of \"From\"",
"From name": "From name",
@ -694,6 +696,8 @@
"Method - Tooltip": "Login method, QR code or silent login",
"New Provider": "New Provider",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "Parse",
"Parse metadata successfully": "Parse metadata successfully",
"Path prefix": "Path prefix",
@ -701,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "Please use WeChat and scan the QR code to sign in",
"Port": "Port",
"Port - Tooltip": "Make sure the port is open",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "Prompted",
"Provider URL": "Provider URL",
"Provider URL - Tooltip": "URL for configuring the service provider, this field is only used for reference and is not used in Casdoor",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "Region ID",
"Region ID - Tooltip": "Region ID for the service provider",
"Region endpoint for Internet": "Region endpoint for Internet",
@ -728,6 +740,8 @@
"Secret key - Tooltip": "Used by the server to call the verification code provider API for verification",
"Send Testing Email": "Send Testing Email",
"Send Testing SMS": "Send Testing SMS",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "Sign Name",
@ -760,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "UserInfo URL",
"UserInfo URL - Tooltip": "UserInfo URL",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
@ -842,6 +858,7 @@
"Error text": "Error text",
"Error text - Tooltip": "Error text",
"Is hashed": "Is hashed",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
@ -850,9 +867,7 @@
"Table": "Table",
"Table - Tooltip": "Name of database table",
"Table columns": "Table columns",
"Table columns - Tooltip": "Columns in the table involved in data synchronization. Columns that are not involved in synchronization do not need to be added",
"Table primary key": "Table primary key",
"Table primary key - Tooltip": "Table primary key, such as id"
"Table columns - Tooltip": "Columns in the table involved in data synchronization. Columns that are not involved in synchronization do not need to be added"
},
"system": {
"API Latency": "API Latency",

View File

@ -11,6 +11,7 @@
"New Adapter": "新しいアダプター",
"Policies": "政策",
"Policies - Tooltip": "Casbinのポリシールール",
"Rule type": "Rule type",
"Sync policies successfully": "ポリシーを同期できました"
},
"application": {
@ -165,16 +166,19 @@
"Adapter - Tooltip": "ポリシー・ストアのテーブル名",
"Adapters": "アダプター",
"Add": "追加",
"Admin": "Admin",
"Affiliation URL": "所属するURL",
"Affiliation URL - Tooltip": "所属先のホームページURL",
"Application": "アプリケーション",
"Application - Tooltip": "Application - Tooltip",
"Applications": "アプリケーション",
"Applications that require authentication": "認証が必要なアプリケーション",
"Authorization": "Authorization",
"Avatar": "アバター",
"Avatar - Tooltip": "ユーザーのパブリックアバター画像",
"Back": "Back",
"Back Home": "帰宅",
"Business & Payments": "Business & Payments",
"Cancel": "キャンセルします",
"Captcha": "キャプチャ",
"Cert": "証明書",
@ -226,6 +230,7 @@
"Home - Tooltip": "アプリケーションのホームページ",
"ID": "ID",
"ID - Tooltip": "ユニークなランダム文字列",
"Identity": "Identity",
"Is enabled": "可能になっています",
"Is enabled - Tooltip": "使用可能かどうかを設定してください",
"LDAPs": "LDAPs",
@ -234,6 +239,7 @@
"Languages - Tooltip": "利用可能な言語",
"Last name": "苗字",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "ロゴ",
"Logo - Tooltip": "アプリケーションが外部世界に示すアイコン",
"MFA items": "MFA items",
@ -327,6 +333,7 @@
"Updated time": "Updated time",
"User": "ユーザー",
"User - Tooltip": "ユーザー名が正しいことを確認してください",
"User Management": "User Management",
"User containers": "ユーザープール",
"User type": "ユーザータイプ",
"User type - Tooltip": "ユーザーが属するタグは、デフォルトでは「通常ユーザー」となります",
@ -347,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "LDAPサーバー管理者のCNまたはID",
@ -382,6 +396,7 @@
"Continue with": "続ける",
"Email or phone": "メールまたは電話",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "パスワードを忘れましたか?",
"Loading": "ローディング",
"Logging out...": "ログアウト中...",
@ -425,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -481,6 +497,7 @@
"Currency - Tooltip": "米ドル、人民元など。",
"Download Invoice": "請求書のダウンロード",
"Edit Payment": "支払い編集",
"Failed reason": "Failed reason",
"Individual": "個人",
"Invoice URL": "請求書のURL",
"Invoice URL - Tooltip": "請求書のダウンロード用のURL",
@ -514,7 +531,9 @@
"Product - Tooltip": "製品名",
"Result": "結果",
"Return to Website": "ウェブサイトに戻る",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "支払いに失敗しました",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "支払いはまだ処理中です",
"Type - Tooltip": "製品を購入する際に使用される支払方法",
"You have successfully completed the payment": "あなたは支払いを正常に完了しました",
@ -596,6 +615,7 @@
"SKU": "SKU",
"Sold": "売れました",
"Sold - Tooltip": "販売数量",
"Stripe": "Stripe",
"Tag - Tooltip": "製品のタグ",
"Test buy page..": "テスト購入ページ。",
"There is no payment channel for this product.": "この製品には支払いチャネルがありません。",
@ -608,14 +628,20 @@
"Access key - Tooltip": "アクセスキー",
"Agent ID": "エージェントID",
"Agent ID - Tooltip": "エージェントID",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "アプリID",
"App ID - Tooltip": "アプリID",
"App key": "アプリキー",
"App key - Tooltip": "アプリキー",
"App secret": "アプリの秘密鍵",
"AppSecret - Tooltip": "アプリの秘密鍵",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "認証URL",
"Auth URL - Tooltip": "認証URL",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "バケツ",
"Bucket - Tooltip": "バケットの名前",
"Can not parse metadata": "メタデータを解析できません",
@ -634,6 +660,8 @@
"Client secret - Tooltip": "クライアント秘密鍵",
"Client secret 2": "クライアントシークレット2",
"Client secret 2 - Tooltip": "第二クライアント秘密鍵",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "コピー",
"Disable SSL": "SSLを無効にする",
"Disable SSL - Tooltip": "SMTPサーバーと通信する場合にSSLプロトコルを無効にするかどうか",
@ -649,6 +677,7 @@
"Enable QR code - Tooltip": "ログインするためにQRコードをスキャンすることを許可するかどうか",
"Endpoint": "エンドポイント",
"Endpoint (Intranet)": "エンドポイント(イントラネット)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "From address - Tooltip",
"From name": "From name",
@ -667,6 +696,8 @@
"Method - Tooltip": "ログイン方法、QRコードまたはサイレントログイン",
"New Provider": "新しい提供者",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "パースする",
"Parse metadata successfully": "メタデータを正常に解析しました",
"Path prefix": "パスプレフィックス",
@ -674,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "WeChatを使用し、QRコードをスキャンしてサインインしてください",
"Port": "ポート",
"Port - Tooltip": "ポートが開いていることを確認してください",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "促された",
"Provider URL": "プロバイダーURL",
"Provider URL - Tooltip": "サービスプロバイダーの設定用URL。このフィールドは参照用にのみ使用され、Casdoorでは使用されません",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "地域ID",
"Region ID - Tooltip": "サービスプロバイダの地域ID",
"Region endpoint for Internet": "インターネットのリージョンエンドポイント",
@ -701,6 +740,8 @@
"Secret key - Tooltip": "認証のためにサーバーによって使用され、認証コードプロバイダAPIを呼び出すためのもの",
"Send Testing Email": "テスト用メールを送信する",
"Send Testing SMS": "テストSMSを送信してください",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "署名",
@ -733,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "UserInfo URLを日本語に翻訳すると、「ユーザー情報のURL」となります",
"UserInfo URL - Tooltip": "ユーザー情報URL",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "管理者(共有)"
},
"record": {
@ -815,6 +858,7 @@
"Error text": "エラーテキスト",
"Error text - Tooltip": "エラーテキスト",
"Is hashed": "ハッシュ化されました",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "新しいシンクロナイザー",
@ -823,9 +867,7 @@
"Table": "テーブル",
"Table - Tooltip": "データベーステーブル名",
"Table columns": "テーブルの列",
"Table columns - Tooltip": "テーブルで同期データに関与する列。同期に関係のない列は追加する必要はありません",
"Table primary key": "テーブルの主キー",
"Table primary key - Tooltip": "テーブルのプライマリキー、例えばid"
"Table columns - Tooltip": "テーブルで同期データに関与する列。同期に関係のない列は追加する必要はありません"
},
"system": {
"API Latency": "API遅延",

View File

@ -11,6 +11,7 @@
"New Adapter": "새로운 어댑터",
"Policies": "정책",
"Policies - Tooltip": "Casbin 정책 규칙",
"Rule type": "Rule type",
"Sync policies successfully": "정책을 성공적으로 동기화했습니다"
},
"application": {
@ -165,16 +166,19 @@
"Adapter - Tooltip": "정책 저장소의 테이블 이름",
"Adapters": "어댑터",
"Add": "추가하다",
"Admin": "Admin",
"Affiliation URL": "소속 URL",
"Affiliation URL - Tooltip": "소속 홈페이지 URL",
"Application": "응용 프로그램",
"Application - Tooltip": "Application - Tooltip",
"Applications": "응용 프로그램",
"Applications that require authentication": "인증이 필요한 애플리케이션들",
"Authorization": "Authorization",
"Avatar": "아바타",
"Avatar - Tooltip": "사용자를 위한 공개 아바타 이미지",
"Back": "Back",
"Back Home": "집으로 돌아오기",
"Business & Payments": "Business & Payments",
"Cancel": "취소",
"Captcha": "캡차",
"Cert": "인증서",
@ -226,6 +230,7 @@
"Home - Tooltip": "어플리케이션 홈 페이지",
"ID": "ID",
"ID - Tooltip": "유일한 랜덤 문자열",
"Identity": "Identity",
"Is enabled": "활성화됩니다",
"Is enabled - Tooltip": "사용 가능한 지 여부를 설정하세요",
"LDAPs": "LDAPs",
@ -234,6 +239,7 @@
"Languages - Tooltip": "사용 가능한 언어",
"Last name": "성",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "로고",
"Logo - Tooltip": "애플리케이션이 외부 세계에 제시하는 아이콘들",
"MFA items": "MFA items",
@ -327,6 +333,7 @@
"Updated time": "Updated time",
"User": "사용자",
"User - Tooltip": "사용자 이름이 정확한지 확인하세요",
"User Management": "User Management",
"User containers": "사용자 풀",
"User type": "사용자 유형",
"User type - Tooltip": "사용자가 속한 태그는 기본적으로 \"보통 사용자\"로 설정됩니다",
@ -347,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "LDAP 서버 관리자의 CN 또는 ID",
@ -382,6 +396,7 @@
"Continue with": "계속하다",
"Email or phone": "이메일 또는 전화",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "비밀번호를 잊으셨나요?",
"Loading": "로딩 중입니다",
"Logging out...": "로그아웃 중...",
@ -425,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -481,6 +497,7 @@
"Currency - Tooltip": "USD, CNY 등과 같이.",
"Download Invoice": "인보이스 다운로드",
"Edit Payment": "결제 수정",
"Failed reason": "Failed reason",
"Individual": "개인",
"Invoice URL": "송장 URL",
"Invoice URL - Tooltip": "송장 다운로드를 위한 URL",
@ -514,7 +531,9 @@
"Product - Tooltip": "제품 이름",
"Result": "결과",
"Return to Website": "웹 사이트로 돌아가기",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "결제가 실패했습니다",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "지불은 아직 처리 중입니다",
"Type - Tooltip": "제품을 구매할 때 사용되는 결제 방법",
"You have successfully completed the payment": "당신은 결제를 성공적으로 완료하셨습니다",
@ -596,6 +615,7 @@
"SKU": "SKU",
"Sold": "팔렸습니다",
"Sold - Tooltip": "판매량",
"Stripe": "Stripe",
"Tag - Tooltip": "제품 태그",
"Test buy page..": "시험 구매 페이지.",
"There is no payment channel for this product.": "이 제품에 대한 결제 채널이 없습니다.",
@ -608,14 +628,20 @@
"Access key - Tooltip": "액세스 키",
"Agent ID": "에이전트 ID",
"Agent ID - Tooltip": "에이전트 ID",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "앱 ID",
"App ID - Tooltip": "앱 식별자",
"App key": "앱 키",
"App key - Tooltip": "앱 키",
"App secret": "앱 비밀키",
"AppSecret - Tooltip": "앱 비밀번호",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "인증 URL",
"Auth URL - Tooltip": "인증 URL",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "양동이",
"Bucket - Tooltip": "양동이의 이름",
"Can not parse metadata": "메타데이터를 구문 분석할 수 없습니다",
@ -634,6 +660,8 @@
"Client secret - Tooltip": "클라이언트 비밀키",
"Client secret 2": "클라이언트 비밀번호 2",
"Client secret 2 - Tooltip": "두 번째 클라이언트 비밀 키",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "복사하다",
"Disable SSL": "SSL을 사용하지 않도록 설정하십시오",
"Disable SSL - Tooltip": "STMP 서버와 통신할 때 SSL 프로토콜을 비활성화할지 여부",
@ -649,6 +677,7 @@
"Enable QR code - Tooltip": "QR 코드를 스캔해서 로그인할 수 있는지 여부",
"Endpoint": "엔드포인트",
"Endpoint (Intranet)": "엔드포인트 (Intranet)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "From address - Tooltip",
"From name": "From name",
@ -667,6 +696,8 @@
"Method - Tooltip": "로그인 방법, QR 코드 또는 음성 로그인",
"New Provider": "새로운 공급 업체",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "파싱",
"Parse metadata successfully": "메타데이터를 성공적으로 분석했습니다",
"Path prefix": "경로 접두어",
@ -674,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "WeChat를 사용하시고 QR 코드를 스캔하여 로그인해주세요",
"Port": "포트",
"Port - Tooltip": "포트가 열려 있는지 확인하세요",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "자극 받은",
"Provider URL": "제공자 URL",
"Provider URL - Tooltip": "서비스 제공 업체 구성을 위한 URL이며, 이 필드는 참조 용도로만 사용되며 Casdoor에서 사용되지 않습니다",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "지역 ID",
"Region ID - Tooltip": "서비스 제공업체의 지역 ID",
"Region endpoint for Internet": "인터넷 지역 엔드포인트",
@ -701,6 +740,8 @@
"Secret key - Tooltip": "검증을 위해 서버에서 인증 코드 공급자 API를 호출하는 데 사용됩니다",
"Send Testing Email": "테스트 이메일을 보내기",
"Send Testing SMS": "테스트 SMS를 보내세요",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "신명서",
@ -733,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "사용자 정보 URL",
"UserInfo URL - Tooltip": "UserInfo URL: 사용자 정보 URL",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "관리자 (공유)"
},
"record": {
@ -815,6 +858,7 @@
"Error text": "오류 메시지",
"Error text - Tooltip": "에러 텍스트",
"Is hashed": "해시화 되었습니다",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "신규 싱크어",
@ -823,9 +867,7 @@
"Table": "테이블",
"Table - Tooltip": "데이터베이스 테이블 이름",
"Table columns": "테이블 열",
"Table columns - Tooltip": "데이터 동기화에 관련된 테이블의 열들입니다. 동기화에 관련되지 않은 열은 추가할 필요가 없습니다",
"Table primary key": "테이블 기본키",
"Table primary key - Tooltip": "테이블의 기본 키, 예를 들어 id"
"Table columns - Tooltip": "데이터 동기화에 관련된 테이블의 열들입니다. 동기화에 관련되지 않은 열은 추가할 필요가 없습니다"
},
"system": {
"API Latency": "API 지연",

View File

@ -1,6 +1,5 @@
{
"account": {
"Chats & Messages": "Chats & Messages",
"Logout": "Logout",
"My Account": "My Account",
"Sign Up": "Sign Up"
@ -12,6 +11,7 @@
"New Adapter": "New Adapter",
"Policies": "Policies",
"Policies - Tooltip": "Casbin policy rules",
"Rule type": "Rule type",
"Sync policies successfully": "Sync policies successfully"
},
"application": {
@ -127,19 +127,6 @@
"Scope - Tooltip": "Usage scenarios of the certificate",
"Type - Tooltip": "Type of certificate"
},
"chat": {
"AI": "AI",
"Edit Chat": "Edit Chat",
"Group": "Group",
"Message count": "Message count",
"New Chat": "New Chat",
"Single": "Single",
"User1": "User1",
"User1 - Tooltip": "User1 - Tooltip",
"User2": "User2",
"User2 - Tooltip": "User2 - Tooltip",
"Users - Tooltip": "Users - Tooltip"
},
"code": {
"Code you received": "Code you received",
"Email code": "Email code",
@ -179,22 +166,24 @@
"Adapter - Tooltip": "Table name of the policy store",
"Adapters": "Adapters",
"Add": "Add",
"Admin": "Admin",
"Affiliation URL": "Affiliation URL",
"Affiliation URL - Tooltip": "The homepage URL for the affiliation",
"Application": "Application",
"Application - Tooltip": "Application - Tooltip",
"Applications": "Applications",
"Applications that require authentication": "Applications that require authentication",
"Authorization": "Authorization",
"Avatar": "Avatar",
"Avatar - Tooltip": "Public avatar image for the user",
"Back": "Back",
"Back Home": "Back Home",
"Business & Payments": "Business & Payments",
"Cancel": "Cancel",
"Captcha": "Captcha",
"Cert": "Cert",
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Chats": "Chats",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
@ -224,7 +213,6 @@
"Failed to connect to server": "Failed to connect to server",
"Failed to delete": "Failed to delete",
"Failed to enable": "Failed to enable",
"Failed to get answer": "Failed to get answer",
"Failed to remove": "Failed to remove",
"Failed to save": "Failed to save",
"Failed to verify": "Failed to verify",
@ -242,6 +230,7 @@
"Home - Tooltip": "Home page of the application",
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -250,6 +239,7 @@
"Languages - Tooltip": "Available languages",
"Last name": "Last name",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"MFA items": "MFA items",
@ -257,7 +247,6 @@
"Master password": "Master password",
"Master password - Tooltip": "Can be used to log in to all users under this organization, making it convenient for administrators to log in as this user to solve technical issues",
"Menu": "Menu",
"Messages": "Messages",
"Method": "Method",
"Model": "Model",
"Model - Tooltip": "Casbin access control model",
@ -344,6 +333,7 @@
"Updated time": "Updated time",
"User": "User",
"User - Tooltip": "Make sure the username is correct",
"User Management": "User Management",
"User containers": "User pools",
"User type": "User type",
"User type - Tooltip": "Tags that the user belongs to, defaulting to \"normal-user\"",
@ -364,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -399,6 +396,7 @@
"Continue with": "Continue with",
"Email or phone": "Email or phone",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"Loading": "Loading",
"Logging out...": "Logging out...",
@ -425,16 +423,6 @@
"sign up now": "sign up now",
"username, Email or phone": "username, Email or phone"
},
"message": {
"Author": "Author",
"Author - Tooltip": "Author - Tooltip",
"Chat": "Chat",
"Chat - Tooltip": "Chat - Tooltip",
"Edit Message": "Edit Message",
"New Message": "New Message",
"Text": "Text",
"Text - Tooltip": "Text - Tooltip"
},
"mfa": {
"Each time you sign in to your Account, you'll need your password and a authentication code": "Each time you sign in to your Account, you'll need your password and a authentication code",
"Enable multi-factor authentication": "Enable multi-factor authentication",
@ -452,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -508,6 +497,7 @@
"Currency - Tooltip": "Like USD, CNY, etc.",
"Download Invoice": "Download Invoice",
"Edit Payment": "Edit Payment",
"Failed reason": "Failed reason",
"Individual": "Individual",
"Invoice URL": "Invoice URL",
"Invoice URL - Tooltip": "URL for downloading the invoice",
@ -541,7 +531,9 @@
"Product - Tooltip": "Product Name",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "The payment has failed",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
@ -623,6 +615,7 @@
"SKU": "SKU",
"Sold": "Sold",
"Sold - Tooltip": "Quantity sold",
"Stripe": "Stripe",
"Tag - Tooltip": "Tag of product",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
@ -635,14 +628,20 @@
"Access key - Tooltip": "Access key",
"Agent ID": "Agent ID",
"Agent ID - Tooltip": "Agent ID",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "App ID",
"App ID - Tooltip": "App ID",
"App key": "App key",
"App key - Tooltip": "App key",
"App secret": "App secret",
"AppSecret - Tooltip": "App secret",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "Auth URL",
"Auth URL - Tooltip": "Auth URL",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "Bucket",
"Bucket - Tooltip": "Name of bucket",
"Can not parse metadata": "Can not parse metadata",
@ -661,6 +660,8 @@
"Client secret - Tooltip": "Client secret",
"Client secret 2": "Client secret 2",
"Client secret 2 - Tooltip": "The second client secret key",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "Copy",
"Disable SSL": "Disable SSL",
"Disable SSL - Tooltip": "Whether to disable SSL protocol when communicating with STMP server",
@ -676,6 +677,7 @@
"Enable QR code - Tooltip": "Whether to allow scanning QR code to login",
"Endpoint": "Endpoint",
"Endpoint (Intranet)": "Endpoint (Intranet)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "Email address of \"From\"",
"From name": "From name",
@ -694,6 +696,8 @@
"Method - Tooltip": "Login method, QR code or silent login",
"New Provider": "New Provider",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "Parse",
"Parse metadata successfully": "Parse metadata successfully",
"Path prefix": "Path prefix",
@ -701,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "Please use WeChat and scan the QR code to sign in",
"Port": "Port",
"Port - Tooltip": "Make sure the port is open",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "Prompted",
"Provider URL": "Provider URL",
"Provider URL - Tooltip": "URL for configuring the service provider, this field is only used for reference and is not used in Casdoor",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "Region ID",
"Region ID - Tooltip": "Region ID for the service provider",
"Region endpoint for Internet": "Region endpoint for Internet",
@ -728,6 +740,8 @@
"Secret key - Tooltip": "Used by the server to call the verification code provider API for verification",
"Send Testing Email": "Send Testing Email",
"Send Testing SMS": "Send Testing SMS",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "Sign Name",
@ -760,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "UserInfo URL",
"UserInfo URL - Tooltip": "UserInfo URL",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
@ -842,6 +858,7 @@
"Error text": "Error text",
"Error text - Tooltip": "Error text",
"Is hashed": "Is hashed",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
@ -850,9 +867,7 @@
"Table": "Table",
"Table - Tooltip": "Name of database table",
"Table columns": "Table columns",
"Table columns - Tooltip": "Columns in the table involved in data synchronization. Columns that are not involved in synchronization do not need to be added",
"Table primary key": "Table primary key",
"Table primary key - Tooltip": "Table primary key, such as id"
"Table columns - Tooltip": "Columns in the table involved in data synchronization. Columns that are not involved in synchronization do not need to be added"
},
"system": {
"API Latency": "API Latency",

View File

@ -11,6 +11,7 @@
"New Adapter": "Novo Adaptador",
"Policies": "Políticas",
"Policies - Tooltip": "Regras de política do Casbin",
"Rule type": "Rule type",
"Sync policies successfully": "Políticas sincronizadas com sucesso"
},
"application": {
@ -165,16 +166,19 @@
"Adapter - Tooltip": "Nome da tabela do armazenamento de políticas",
"Adapters": "Adaptadores",
"Add": "Adicionar",
"Admin": "Admin",
"Affiliation URL": "URL da Afiliação",
"Affiliation URL - Tooltip": "A URL da página inicial para a afiliação",
"Application": "Aplicação",
"Application - Tooltip": "Application - Tooltip",
"Applications": "Aplicações",
"Applications that require authentication": "Aplicações que requerem autenticação",
"Authorization": "Authorization",
"Avatar": "Avatar",
"Avatar - Tooltip": "Imagem de avatar pública do usuário",
"Back": "Voltar",
"Back Home": "Voltar para a Página Inicial",
"Business & Payments": "Business & Payments",
"Cancel": "Cancelar",
"Captcha": "Captcha",
"Cert": "Certificado",
@ -226,6 +230,7 @@
"Home - Tooltip": "Página inicial do aplicativo",
"ID": "ID",
"ID - Tooltip": "String única aleatória",
"Identity": "Identity",
"Is enabled": "Está habilitado",
"Is enabled - Tooltip": "Define se está habilitado",
"LDAPs": "LDAPs",
@ -234,6 +239,7 @@
"Languages - Tooltip": "Idiomas disponíveis",
"Last name": "Sobrenome",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Ícones que o aplicativo apresenta para o mundo externo",
"MFA items": "MFA items",
@ -327,6 +333,7 @@
"Updated time": "Hora de Atualização",
"User": "Usuário",
"User - Tooltip": "Certifique-se de que o nome de usuário esteja correto",
"User Management": "User Management",
"User containers": "Pools de Usuários",
"User type": "Tipo de Usuário",
"User type - Tooltip": "Tags às quais o usuário pertence, com valor padrão de \"usuário-normal\"",
@ -347,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Administrador",
"Admin - Tooltip": "CN ou ID do administrador do servidor LDAP",
@ -382,6 +396,7 @@
"Continue with": "Continuar com",
"Email or phone": "Email ou telefone",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Esqueceu a senha?",
"Loading": "Carregando",
"Logging out...": "Saindo...",
@ -425,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -481,6 +497,7 @@
"Currency - Tooltip": "Como USD, CNY, etc.",
"Download Invoice": "Baixar Fatura",
"Edit Payment": "Editar Pagamento",
"Failed reason": "Failed reason",
"Individual": "Individual",
"Invoice URL": "URL da Fatura",
"Invoice URL - Tooltip": "URL para baixar a fatura",
@ -514,7 +531,9 @@
"Product - Tooltip": "Nome do Produto",
"Result": "Resultado",
"Return to Website": "Retornar ao Website",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "O pagamento falhou",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "O pagamento ainda está sendo processado",
"Type - Tooltip": "Método de pagamento utilizado ao comprar o produto",
"You have successfully completed the payment": "Você concluiu o pagamento com sucesso",
@ -596,6 +615,7 @@
"SKU": "SKU",
"Sold": "Vendido",
"Sold - Tooltip": "Quantidade vendida",
"Stripe": "Stripe",
"Tag - Tooltip": "Tag do produto",
"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.",
@ -608,14 +628,20 @@
"Access key - Tooltip": "Chave de acesso",
"Agent ID": "ID do Agente",
"Agent ID - Tooltip": "ID do Agente",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "ID do aplicativo",
"App ID - Tooltip": "ID do aplicativo",
"App key": "Chave do aplicativo",
"App key - Tooltip": "Chave do aplicativo",
"App secret": "Segredo do aplicativo",
"AppSecret - Tooltip": "Segredo do aplicativo",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "URL de autenticação",
"Auth URL - Tooltip": "URL de autenticação",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "Bucket",
"Bucket - Tooltip": "Nome do bucket",
"Can not parse metadata": "Não é possível analisar metadados",
@ -634,6 +660,8 @@
"Client secret - Tooltip": "Segredo do cliente",
"Client secret 2": "Segredo do cliente 2",
"Client secret 2 - Tooltip": "A segunda chave secreta do cliente",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "Copiar",
"Disable SSL": "Desabilitar SSL",
"Disable SSL - Tooltip": "Se deve desabilitar o protocolo SSL ao comunicar com o servidor SMTP",
@ -649,6 +677,7 @@
"Enable QR code - Tooltip": "Se permite escanear código QR para fazer login",
"Endpoint": "Endpoint",
"Endpoint (Intranet)": "Endpoint (Intranet)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "Endereço do remetente",
"From address - Tooltip": "Endereço de e-mail do remetente",
"From name": "Nome do remetente",
@ -667,6 +696,8 @@
"Method - Tooltip": "Método de login, código QR ou login silencioso",
"New Provider": "Novo Provedor",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "Analisar",
"Parse metadata successfully": "Metadados analisados com sucesso",
"Path prefix": "Prefixo do caminho",
@ -674,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "Por favor, use o WeChat e escaneie o código QR para fazer login",
"Port": "Porta",
"Port - Tooltip": "Certifique-se de que a porta esteja aberta",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "Solicitado",
"Provider URL": "URL do Provedor",
"Provider URL - Tooltip": "URL para configurar o provedor de serviço, este campo é apenas usado para referência e não é usado no Casdoor",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "ID da Região",
"Region ID - Tooltip": "ID da região para o provedor de serviços",
"Region endpoint for Internet": "Endpoint da região para a Internet",
@ -701,6 +740,8 @@
"Secret key - Tooltip": "Usada pelo servidor para chamar a API do fornecedor de código de verificação para verificação",
"Send Testing Email": "Enviar E-mail de Teste",
"Send Testing SMS": "Enviar SMS de Teste",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "Nome do Sinal",
@ -733,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "URL do UserInfo",
"UserInfo URL - Tooltip": "URL do UserInfo",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Compartilhado)"
},
"record": {
@ -815,6 +858,7 @@
"Error text": "Texto de erro",
"Error text - Tooltip": "Texto de erro",
"Is hashed": "Está criptografado",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "Novo Syncer",
@ -823,9 +867,7 @@
"Table": "Tabela",
"Table - Tooltip": "Nome da tabela no banco de dados",
"Table columns": "Colunas da tabela",
"Table columns - Tooltip": "Colunas na tabela envolvidas na sincronização de dados. Colunas que não estão envolvidas na sincronização não precisam ser adicionadas",
"Table primary key": "Chave primária da tabela",
"Table primary key - Tooltip": "Chave primária da tabela, como id"
"Table columns - Tooltip": "Colunas na tabela envolvidas na sincronização de dados. Colunas que não estão envolvidas na sincronização não precisam ser adicionadas"
},
"system": {
"API Latency": "Latência da API",

View File

@ -11,6 +11,7 @@
"New Adapter": "Новый адаптер",
"Policies": "Политика",
"Policies - Tooltip": "Правила политики Casbin",
"Rule type": "Rule type",
"Sync policies successfully": "Успешно синхронизированы политики"
},
"application": {
@ -165,16 +166,19 @@
"Adapter - Tooltip": "Имя таблицы хранилища политик",
"Adapters": "Адаптеры",
"Add": "Добавить",
"Admin": "Admin",
"Affiliation URL": "URL принадлежности",
"Affiliation URL - Tooltip": "URL домашней страницы для аффилированности",
"Application": "Приложение",
"Application - Tooltip": "Application - Tooltip",
"Applications": "Приложения",
"Applications that require authentication": "Приложения, которые требуют аутентификации",
"Authorization": "Authorization",
"Avatar": "Аватар",
"Avatar - Tooltip": "Публичное изображение аватара пользователя",
"Back": "Back",
"Back Home": "Домой",
"Business & Payments": "Business & Payments",
"Cancel": "Отменить",
"Captcha": "Капча",
"Cert": "Сертификат",
@ -226,6 +230,7 @@
"Home - Tooltip": "Главная страница приложения",
"ID": "ID",
"ID - Tooltip": "Уникальная случайная строка",
"Identity": "Identity",
"Is enabled": "Включен",
"Is enabled - Tooltip": "Установить, может ли использоваться",
"LDAPs": "LDAPs",
@ -234,6 +239,7 @@
"Languages - Tooltip": "Доступные языки",
"Last name": "Фамилия",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "Логотип",
"Logo - Tooltip": "Иконки, которые приложение представляет во внешний мир",
"MFA items": "MFA items",
@ -327,6 +333,7 @@
"Updated time": "Updated time",
"User": "Пользователь",
"User - Tooltip": "Убедитесь, что имя пользователя правильное",
"User Management": "User Management",
"User containers": "Пользовательские пулы",
"User type": "Тип пользователя",
"User type - Tooltip": "Теги, к которым принадлежит пользователь, по умолчанию \"обычный пользователь\"",
@ -347,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN или ID администратора сервера LDAP",
@ -382,6 +396,7 @@
"Continue with": "Продолжайте с",
"Email or phone": "Электронная почта или телефон",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Забыли пароль?",
"Loading": "Загрузка",
"Logging out...": "Выход...",
@ -425,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -481,6 +497,7 @@
"Currency - Tooltip": "Как и USD, CNY и т.д.",
"Download Invoice": "Загрузить счет-фактуру",
"Edit Payment": "Редактировать оплату",
"Failed reason": "Failed reason",
"Individual": "Индивидуальный",
"Invoice URL": "URL счета-фактуры",
"Invoice URL - Tooltip": "URL для загрузки счета-фактуры",
@ -514,7 +531,9 @@
"Product - Tooltip": "Название продукта",
"Result": "Результат",
"Return to Website": "Вернуться на веб-сайт",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "Оплата не удалась",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "Оплата все еще обрабатывается",
"Type - Tooltip": "Способ оплаты, используемый при покупке товара",
"You have successfully completed the payment": "Вы успешно произвели платеж",
@ -596,6 +615,7 @@
"SKU": "SKU",
"Sold": "Продано",
"Sold - Tooltip": "Количество проданных",
"Stripe": "Stripe",
"Tag - Tooltip": "Метка продукта",
"Test buy page..": "Страница для тестовой покупки.",
"There is no payment channel for this product.": "Для этого продукта нет канала оплаты.",
@ -608,14 +628,20 @@
"Access key - Tooltip": "Ключ доступа",
"Agent ID": "Идентификатор агента",
"Agent ID - Tooltip": "Агент ID",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "Идентификатор приложения",
"App ID - Tooltip": "Идентификатор приложения",
"App key": "Ключ приложения",
"App key - Tooltip": "Ключ приложения",
"App secret": "Секрет приложения",
"AppSecret - Tooltip": "Секрет приложения",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "Адрес авторизации",
"Auth URL - Tooltip": "URL авторизации",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "Ведро",
"Bucket - Tooltip": "Название ведра",
"Can not parse metadata": "Невозможно проанализировать метаданные",
@ -634,6 +660,8 @@
"Client secret - Tooltip": "Клиентский секрет",
"Client secret 2": "Секрет клиента 2",
"Client secret 2 - Tooltip": "Второй секретный ключ клиента",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "Копировать",
"Disable SSL": "Отключить SSL",
"Disable SSL - Tooltip": "Нужно ли отключать протокол SSL при общении с SMTP сервером?",
@ -649,6 +677,7 @@
"Enable QR code - Tooltip": "Разрешить ли сканирование QR-кода для входа в систему",
"Endpoint": "Конечная точка",
"Endpoint (Intranet)": "Конечная точка (интранет)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "From address - Tooltip",
"From name": "From name",
@ -667,6 +696,8 @@
"Method - Tooltip": "Метод входа, QR-код или беззвучный вход",
"New Provider": "Новый провайдер",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "Спарсить",
"Parse metadata successfully": "Успешно обработана метаданные",
"Path prefix": "Префикс пути",
@ -674,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "Пожалуйста, используйте WeChat и отсканируйте QR-код для входа в систему",
"Port": "Порт",
"Port - Tooltip": "Убедитесь, что порт открыт",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "Побудил",
"Provider URL": "URL поставщика",
"Provider URL - Tooltip": "URL для настройки поставщика услуг, это поле используется только для ссылки и не используется в Casdoor",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "Идентификатор региона",
"Region ID - Tooltip": "Идентификатор региона для провайдера услуг",
"Region endpoint for Internet": "Региональный конечная точка для Интернета",
@ -701,6 +740,8 @@
"Secret key - Tooltip": "Используется сервером для вызова API-интерфейса поставщика кода подтверждения для проверки",
"Send Testing Email": "Отправить тестовое письмо",
"Send Testing SMS": "Отправить тестовое SMS-сообщение",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "Подпись имени",
@ -733,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "URL информации о пользователе",
"UserInfo URL - Tooltip": "URL пользовательской информации (URL информации о пользователе)",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "администратор (общий)"
},
"record": {
@ -815,6 +858,7 @@
"Error text": "Текст ошибки",
"Error text - Tooltip": "Текст ошибки",
"Is hashed": "Хешировано",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "Новый синхронизатор",
@ -823,9 +867,7 @@
"Table": "Стол",
"Table - Tooltip": "Название таблицы базы данных",
"Table columns": "Столбцы таблицы",
"Table columns - Tooltip": "Столбцы в таблице, участвующие в синхронизации данных. Столбцы, не участвующие в синхронизации, не нужно добавлять",
"Table primary key": "Первичный ключ таблицы",
"Table primary key - Tooltip": "Идентификатор (id) - основной ключ таблицы"
"Table columns - Tooltip": "Столбцы в таблице, участвующие в синхронизации данных. Столбцы, не участвующие в синхронизации, не нужно добавлять"
},
"system": {
"API Latency": "Задержка API",

View File

@ -1,6 +1,5 @@
{
"account": {
"Chats & Messages": "Chats & Messages",
"Logout": "Logout",
"My Account": "My Account",
"Sign Up": "Sign Up"
@ -12,6 +11,7 @@
"New Adapter": "New Adapter",
"Policies": "Policies",
"Policies - Tooltip": "Casbin policy rules",
"Rule type": "Rule type",
"Sync policies successfully": "Sync policies successfully"
},
"application": {
@ -127,19 +127,6 @@
"Scope - Tooltip": "Usage scenarios of the certificate",
"Type - Tooltip": "Type of certificate"
},
"chat": {
"AI": "AI",
"Edit Chat": "Edit Chat",
"Group": "Group",
"Message count": "Message count",
"New Chat": "New Chat",
"Single": "Single",
"User1": "User1",
"User1 - Tooltip": "User1 - Tooltip",
"User2": "User2",
"User2 - Tooltip": "User2 - Tooltip",
"Users - Tooltip": "Users - Tooltip"
},
"code": {
"Code you received": "Code you received",
"Email code": "Email code",
@ -179,22 +166,24 @@
"Adapter - Tooltip": "Table name of the policy store",
"Adapters": "Adapters",
"Add": "Add",
"Admin": "Admin",
"Affiliation URL": "Affiliation URL",
"Affiliation URL - Tooltip": "The homepage URL for the affiliation",
"Application": "Application",
"Application - Tooltip": "Application - Tooltip",
"Applications": "Applications",
"Applications that require authentication": "Applications that require authentication",
"Authorization": "Authorization",
"Avatar": "Avatar",
"Avatar - Tooltip": "Public avatar image for the user",
"Back": "Back",
"Back Home": "Back Home",
"Business & Payments": "Business & Payments",
"Cancel": "Cancel",
"Captcha": "Captcha",
"Cert": "Cert",
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Chats": "Chats",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
@ -224,7 +213,6 @@
"Failed to connect to server": "Failed to connect to server",
"Failed to delete": "Failed to delete",
"Failed to enable": "Failed to enable",
"Failed to get answer": "Failed to get answer",
"Failed to remove": "Failed to remove",
"Failed to save": "Failed to save",
"Failed to verify": "Failed to verify",
@ -242,6 +230,7 @@
"Home - Tooltip": "Home page of the application",
"ID": "ID",
"ID - Tooltip": "Unique random string",
"Identity": "Identity",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
@ -250,6 +239,7 @@
"Languages - Tooltip": "Available languages",
"Last name": "Last name",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"MFA items": "MFA items",
@ -257,7 +247,6 @@
"Master password": "Master password",
"Master password - Tooltip": "Can be used to log in to all users under this organization, making it convenient for administrators to log in as this user to solve technical issues",
"Menu": "Menu",
"Messages": "Messages",
"Method": "Method",
"Model": "Model",
"Model - Tooltip": "Casbin access control model",
@ -344,6 +333,7 @@
"Updated time": "Updated time",
"User": "User",
"User - Tooltip": "Make sure the username is correct",
"User Management": "User Management",
"User containers": "User pools",
"User type": "User type",
"User type - Tooltip": "Tags that the user belongs to, defaulting to \"normal-user\"",
@ -364,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
@ -399,6 +396,7 @@
"Continue with": "Continue with",
"Email or phone": "Email or phone",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"Loading": "Loading",
"Logging out...": "Logging out...",
@ -425,16 +423,6 @@
"sign up now": "sign up now",
"username, Email or phone": "username, Email or phone"
},
"message": {
"Author": "Author",
"Author - Tooltip": "Author - Tooltip",
"Chat": "Chat",
"Chat - Tooltip": "Chat - Tooltip",
"Edit Message": "Edit Message",
"New Message": "New Message",
"Text": "Text",
"Text - Tooltip": "Text - Tooltip"
},
"mfa": {
"Each time you sign in to your Account, you'll need your password and a authentication code": "Each time you sign in to your Account, you'll need your password and a authentication code",
"Enable multi-factor authentication": "Enable multi-factor authentication",
@ -452,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -508,6 +497,7 @@
"Currency - Tooltip": "Like USD, CNY, etc.",
"Download Invoice": "Download Invoice",
"Edit Payment": "Edit Payment",
"Failed reason": "Failed reason",
"Individual": "Individual",
"Invoice URL": "Invoice URL",
"Invoice URL - Tooltip": "URL for downloading the invoice",
@ -541,7 +531,9 @@
"Product - Tooltip": "Product Name",
"Result": "Result",
"Return to Website": "Return to Website",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "The payment has failed",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "The payment is still under processing",
"Type - Tooltip": "Payment method used when purchasing the product",
"You have successfully completed the payment": "You have successfully completed the payment",
@ -623,6 +615,7 @@
"SKU": "SKU",
"Sold": "Sold",
"Sold - Tooltip": "Quantity sold",
"Stripe": "Stripe",
"Tag - Tooltip": "Tag of product",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
@ -635,14 +628,20 @@
"Access key - Tooltip": "Access key",
"Agent ID": "Agent ID",
"Agent ID - Tooltip": "Agent ID",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "App ID",
"App ID - Tooltip": "App ID",
"App key": "App key",
"App key - Tooltip": "App key",
"App secret": "App secret",
"AppSecret - Tooltip": "App secret",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "Auth URL",
"Auth URL - Tooltip": "Auth URL",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "Bucket",
"Bucket - Tooltip": "Name of bucket",
"Can not parse metadata": "Can not parse metadata",
@ -661,6 +660,8 @@
"Client secret - Tooltip": "Client secret",
"Client secret 2": "Client secret 2",
"Client secret 2 - Tooltip": "The second client secret key",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "Copy",
"Disable SSL": "Disable SSL",
"Disable SSL - Tooltip": "Whether to disable SSL protocol when communicating with STMP server",
@ -676,6 +677,7 @@
"Enable QR code - Tooltip": "Whether to allow scanning QR code to login",
"Endpoint": "Endpoint",
"Endpoint (Intranet)": "Endpoint (Intranet)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "Email address of \"From\"",
"From name": "From name",
@ -694,6 +696,8 @@
"Method - Tooltip": "Login method, QR code or silent login",
"New Provider": "New Provider",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "Parse",
"Parse metadata successfully": "Parse metadata successfully",
"Path prefix": "Path prefix",
@ -701,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "Please use WeChat and scan the QR code to sign in",
"Port": "Port",
"Port - Tooltip": "Make sure the port is open",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "Prompted",
"Provider URL": "Provider URL",
"Provider URL - Tooltip": "URL for configuring the service provider, this field is only used for reference and is not used in Casdoor",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "Region ID",
"Region ID - Tooltip": "Region ID for the service provider",
"Region endpoint for Internet": "Region endpoint for Internet",
@ -728,6 +740,8 @@
"Secret key - Tooltip": "Used by the server to call the verification code provider API for verification",
"Send Testing Email": "Send Testing Email",
"Send Testing SMS": "Send Testing SMS",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "Sign Name",
@ -760,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "UserInfo URL",
"UserInfo URL - Tooltip": "UserInfo URL",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "admin (Shared)"
},
"record": {
@ -842,6 +858,7 @@
"Error text": "Error text",
"Error text - Tooltip": "Error text",
"Is hashed": "Is hashed",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer",
@ -850,9 +867,7 @@
"Table": "Table",
"Table - Tooltip": "Name of database table",
"Table columns": "Table columns",
"Table columns - Tooltip": "Columns in the table involved in data synchronization. Columns that are not involved in synchronization do not need to be added",
"Table primary key": "Table primary key",
"Table primary key - Tooltip": "Table primary key, such as id"
"Table columns - Tooltip": "Columns in the table involved in data synchronization. Columns that are not involved in synchronization do not need to be added"
},
"system": {
"API Latency": "API Latency",

View File

@ -11,6 +11,7 @@
"New Adapter": "Bộ chuyển đổi mới",
"Policies": "Chính sách",
"Policies - Tooltip": "Quy tắc chính sách Casbin",
"Rule type": "Rule type",
"Sync policies successfully": "Đồng bộ chính sách thành công"
},
"application": {
@ -165,16 +166,19 @@
"Adapter - Tooltip": "Tên bảng của kho lưu trữ chính sách",
"Adapters": "Bộ chuyển đổi",
"Add": "Thêm vào",
"Admin": "Admin",
"Affiliation URL": "Đường dẫn liên kết liên kết",
"Affiliation URL - Tooltip": "Đường dẫn URL trang chủ của liên kết",
"Application": "Ứng dụng",
"Application - Tooltip": "Application - Tooltip",
"Applications": "Ứng dụng",
"Applications that require authentication": "Các ứng dụng yêu cầu xác thực",
"Authorization": "Authorization",
"Avatar": "Ảnh đại diện",
"Avatar - Tooltip": "Ảnh đại diện công khai cho người dùng",
"Back": "Back",
"Back Home": "Trở về nhà",
"Business & Payments": "Business & Payments",
"Cancel": "Hủy bỏ",
"Captcha": "Captcha",
"Cert": "Chứng chỉ",
@ -226,6 +230,7 @@
"Home - Tooltip": "Trang chủ của ứng dụng",
"ID": "ID",
"ID - Tooltip": "Chuỗi ngẫu nhiên độc nhất",
"Identity": "Identity",
"Is enabled": "Đã được kích hoạt",
"Is enabled - Tooltip": "Đặt liệu nó có thể sử dụng hay không",
"LDAPs": "LDAPs",
@ -234,6 +239,7 @@
"Languages - Tooltip": "Các ngôn ngữ hiện có",
"Last name": "Họ",
"Later": "Later",
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Biểu tượng mà ứng dụng hiển thị ra ngoài thế giới",
"MFA items": "MFA items",
@ -327,6 +333,7 @@
"Updated time": "Updated time",
"User": "Người dùng",
"User - Tooltip": "Hãy đảm bảo tên đăng nhập chính xác",
"User Management": "User Management",
"User containers": "Nhóm người dùng",
"User type": "Loại người dùng",
"User type - Tooltip": "Các thẻ mà người dùng thuộc vào, mặc định là \"người dùng bình thường\"",
@ -347,6 +354,13 @@
"Show all": "Show all",
"Virtual": "Virtual"
},
"home": {
"New users past 30 days": "New users past 30 days",
"New users past 7 days": "New users past 7 days",
"New users today": "New users today",
"Past 30 Days": "Past 30 Days",
"Total users": "Total users"
},
"ldap": {
"Admin": "Admin",
"Admin - Tooltip": "CN hoặc ID của quản trị viên máy chủ LDAP",
@ -382,6 +396,7 @@
"Continue with": "Tiếp tục với",
"Email or phone": "Email hoặc điện thoại",
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Quên mật khẩu?",
"Loading": "Đang tải",
"Logging out...": "Đăng xuất ...",
@ -425,6 +440,7 @@
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
@ -481,6 +497,7 @@
"Currency - Tooltip": "Như USD, CNY, v.v.",
"Download Invoice": "Tải hóa đơn",
"Edit Payment": "Chỉnh sửa thanh toán",
"Failed reason": "Failed reason",
"Individual": "Cá nhân",
"Invoice URL": "Đường link hoá đơn",
"Invoice URL - Tooltip": "Đường link để tải hóa đơn xuống",
@ -514,7 +531,9 @@
"Product - Tooltip": "Tên sản phẩm",
"Result": "Kết quả",
"Return to Website": "Trở lại trang web",
"The payment has been canceled": "The payment has been canceled",
"The payment has failed": "Thanh toán đã thất bại",
"The payment has time out": "The payment has time out",
"The payment is still under processing": "Thanh toán vẫn đang được xử lý",
"Type - Tooltip": "Phương thức thanh toán được sử dụng khi mua sản phẩm",
"You have successfully completed the payment": "Bạn đã hoàn thành thanh toán thành công",
@ -596,6 +615,7 @@
"SKU": "SKU",
"Sold": "Đã bán",
"Sold - Tooltip": "Số lượng bán ra",
"Stripe": "Stripe",
"Tag - Tooltip": "Nhãn sản phẩm",
"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.",
@ -608,14 +628,20 @@
"Access key - Tooltip": "Khóa truy cập",
"Agent ID": "Mã đại lý",
"Agent ID - Tooltip": "Mã đại lý",
"Api Key": "Api Key",
"Api Key - Tooltip": "Api Key - Tooltip",
"App ID": "ID ứng dụng",
"App ID - Tooltip": "Định danh ứng dụng",
"App key": "Khóa ứng dụng",
"App key - Tooltip": "Khóa ứng dụng",
"App secret": "Mã bí mật ứng dụng",
"AppSecret - Tooltip": "Bí mật ứng dụng",
"Auth Key": "Auth Key",
"Auth Key - Tooltip": "Auth Key - Tooltip",
"Auth URL": "URL xác thực",
"Auth URL - Tooltip": "URL chứng thực",
"Base URL": "Base URL",
"Base URL - Tooltip": "Base URL - Tooltip",
"Bucket": "Thùng đựng nước",
"Bucket - Tooltip": "Tên của cái xô",
"Can not parse metadata": "Không thể phân tích siêu dữ liệu",
@ -634,6 +660,8 @@
"Client secret - Tooltip": "Mã bí mật khách hàng",
"Client secret 2": "Khóa bí mật của khách hàng 2",
"Client secret 2 - Tooltip": "Khóa bí mật thứ hai của khách hàng",
"Content": "Content",
"Content - Tooltip": "Content - Tooltip",
"Copy": "Sao chép",
"Disable SSL": "Vô hiệu hóa SSL",
"Disable SSL - Tooltip": "Có nên vô hiệu hóa giao thức SSL khi giao tiếp với máy chủ STMP hay không?",
@ -649,6 +677,7 @@
"Enable QR code - Tooltip": "Cho phép quét mã QR để đăng nhập",
"Endpoint": "Điểm cuối",
"Endpoint (Intranet)": "Điểm kết thúc (mạng nội bộ)",
"Endpoint - Tooltip": "Endpoint - Tooltip",
"From address": "From address",
"From address - Tooltip": "From address - Tooltip",
"From name": "From name",
@ -667,6 +696,8 @@
"Method - Tooltip": "Phương thức đăng nhập, mã QR hoặc đăng nhập im lặng",
"New Provider": "Nhà cung cấp mới",
"Normal": "Normal",
"Parameter name": "Parameter name",
"Parameter name - Tooltip": "Parameter name - Tooltip",
"Parse": "Phân tích cú pháp",
"Parse metadata successfully": "Phân tích siêu dữ liệu thành công",
"Path prefix": "Tiền tố đường dẫn",
@ -674,9 +705,17 @@
"Please use WeChat and scan the QR code to sign in": "Vui lòng sử dụng WeChat và quét mã QR để đăng nhập",
"Port": "Cảng",
"Port - Tooltip": "Chắc chắn rằng cổng đang mở",
"Private Key": "Private Key",
"Private Key - Tooltip": "Private Key - Tooltip",
"Project Id": "Project Id",
"Project Id - Tooltip": "Project Id - Tooltip",
"Prompted": "Thúc đẩy",
"Provider URL": "Địa chỉ URL nhà cung cấp",
"Provider URL - Tooltip": "URL để cấu hình nhà cung cấp dịch vụ, trường này chỉ được sử dụng để tham khảo và không được sử dụng trong Casdoor",
"Public key": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Region": "Region",
"Region - Tooltip": "Region - Tooltip",
"Region ID": "Định danh khu vực",
"Region ID - Tooltip": "Định danh khu vực cho nhà cung cấp dịch vụ",
"Region endpoint for Internet": "Điểm cuối khu vực cho Internet",
@ -701,6 +740,8 @@
"Secret key - Tooltip": "Được sử dụng bởi máy chủ để gọi API nhà cung cấp mã xác minh để xác minh",
"Send Testing Email": "Gửi Email kiểm tra",
"Send Testing SMS": "Gửi SMS kiểm tra",
"Sender Id": "Sender Id",
"Sender Id - Tooltip": "Sender Id - Tooltip",
"Sender number": "Sender number",
"Sender number - Tooltip": "Sender number - Tooltip",
"Sign Name": "Ký tên",
@ -733,6 +774,8 @@
"User mapping - Tooltip": "User mapping - Tooltip",
"UserInfo URL": "Đường dẫn UserInfo",
"UserInfo URL - Tooltip": "Địa chỉ URL của Thông tin người dùng",
"Wallets": "Wallets",
"Wallets - Tooltip": "Wallets - Tooltip",
"admin (Shared)": "quản trị viên (Chung)"
},
"record": {
@ -815,6 +858,7 @@
"Error text": "Văn bản lỗi",
"Error text - Tooltip": "Văn bản lỗi",
"Is hashed": "Đã được băm mã hóa",
"Is key": "Is key",
"Is read-only": "Is read-only",
"Is read-only - Tooltip": "Is read-only - Tooltip",
"New Syncer": "New Syncer: Đồng bộ mới",
@ -823,9 +867,7 @@
"Table": "Bàn",
"Table - Tooltip": "Tên của bảng cơ sở dữ liệu",
"Table columns": "Các cột bảng",
"Table columns - Tooltip": "Cột trong bảng liên quan đến đồng bộ dữ liệu. Các cột không liên quan đến đồng bộ hóa không cần được thêm vào",
"Table primary key": "Khóa chính của bảng",
"Table primary key - Tooltip": "Khóa chính của bảng, ví dụ như id"
"Table columns - Tooltip": "Cột trong bảng liên quan đến đồng bộ dữ liệu. Các cột không liên quan đến đồng bộ hóa không cần được thêm vào"
},
"system": {
"API Latency": "Độ trễ API",

Some files were not shown because too many files have changed in this diff Show More