Compare commits

...

27 Commits

Author SHA1 Message Date
6daadf8d3c feat: add e2e test (#1445)
* add cypress

* feat: add crypress ci
2023-01-09 00:16:32 +08:00
090389b86a Fix bug in CheckAccountItemModifyRule() 2023-01-07 13:49:06 +08:00
b566af8e11 Improve i18n 2023-01-06 20:32:18 +08:00
57028c2059 Remove duplicated i18n words 2023-01-06 20:24:14 +08:00
a6e9084973 Remove duplicated i18n words 2023-01-06 20:12:32 +08:00
6fb3e2cd7f Remove check_util i18n words 2023-01-06 19:57:13 +08:00
8b6bde6d82 Remove generate_backend.go 2023-01-06 19:42:47 +08:00
fb2b03f00f Add category to applyToOtherLanguage() 2023-01-06 19:26:00 +08:00
1681138729 Add getAllFilePathsInFolder() 2023-01-06 19:04:38 +08:00
1d8b0a264e feat(login): add code login limit (#1442) 2023-01-06 18:51:43 +08:00
b525210835 feat: destroy session after delete user (#1441)
* fix: destroy session after delete user

* feat: visual session

* fix: go lint

* feat: add translation

* feat: auto flush after offline

* fix: delete one session

* fix: move 403 page to baseListPage
2023-01-06 15:04:13 +08:00
4ab2ca7a25 feat: fix checkPermissionForUpdateUser() logic (#1454)
* fix: fix `checkPermissionForUpdateUser()` logic

* fix: fix `checkPermissionForUpdateUser()` logic
2023-01-06 00:03:40 +08:00
dcf148fb7f fix: add GetMaskedRoles and GetMaskedPermissions when GetAccount (#1456) 2023-01-06 00:02:52 +08:00
c8846f1a2d feat: fix translate bug in UpdateUser() (#1451)
* fix: fix translate error

* fix translate bug in UpdateUser()

* Delete DiscordLoginButton.js
2023-01-04 22:54:50 +08:00
0559298d6c feat: extend user with roles and permissions in GetAccount (#1449) 2023-01-04 20:23:57 +08:00
ddb5e26fcd fix: mask user in get-account response (#1450) 2023-01-04 18:40:36 +08:00
Liu
1f39027b78 fix: convert line endings to LF on checkout for all envs (#1448)
* Convert line endings to LF on checkout for all envs

* fix: convert line endings to LF on checkout for all envs
2023-01-04 18:36:38 +08:00
eae3b0d367 feat: fix saml login failed by using oauth (#1443) 2023-01-03 19:42:12 +08:00
186f0ac97b feat: check permission when update user (#1438)
* feat: check permission when update user

* feat: check permission when update user

* fix: fix organization accountItem modifyRule

* fix: fix organization accountItem modifyRule
2023-01-02 09:27:25 +08:00
308f305c53 feat: add query and fragment response mode declare in OIDC (#1439) 2023-01-01 21:46:12 +08:00
d498bc60ce feat: edit user properties (#1435) 2022-12-31 15:27:53 +08:00
7bbe1e38c1 fix: fix translate error (#1432)
* fix:fix translate error

* Delete TelegramLoginButton.js

* Update data.json

* Update data.json

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-12-30 12:10:18 +08:00
f465fc6ce0 feat: support changing theme in antd 5 (#1430)
* feat: add global theme change function

* feat: add icons

* feat: in app theme changer

* feat: use antd built-in themes

* fix: multiple styling problem

* fix: theme init from localstorage

* feat: dark mode footer

* feat: casdoor logo color theme

* feat: select theme box icon adaptive to theme

* fix: menu bar style

* fix: language box style

* feat: translation

* feat: update translation of select theme box without reloading

* fix: mobile view

* fix: better structured select theme box

* feat: add compact icon

* fix: redundant theme fetch

* fix: redundant theme fetch

* fix: various styling problems
2022-12-29 22:30:37 +08:00
c952c2f2f4 feat: fix login with password bug when feature is disabled (#1428) 2022-12-27 14:46:57 +08:00
86ae97d1e5 feat: fix the bug that spin is always showing when response error (#1424) 2022-12-24 17:55:36 +08:00
6ea73e3eca fix: show background image in preview (#1425) 2022-12-24 17:47:05 +08:00
a71a190db5 feat: fix bug in redirectToLoginPage() (#1422) 2022-12-24 01:10:02 +08:00
87 changed files with 13544 additions and 12372 deletions

5
.gitattributes vendored
View File

@ -1,2 +1,5 @@
*.go linguist-detectable=true
*.js linguist-detectable=false
*.js linguist-detectable=false
# Declare files that will always have LF line endings on checkout.
# Git will always convert line endings to LF on checkout. You should use this for files that must keep LF endings, even on Windows.
*.sh text eol=lf

View File

@ -76,11 +76,48 @@ jobs:
version: latest
args: --disable-all -c dummy.yml -E=gofumpt --max-same-issues=0 --timeout 5m --modules-download-mode=mod
e2e:
name: e2e-test
runs-on: ubuntu-latest
needs: [ frontend, backend, linter ]
services:
mysql:
image: mysql:5.7
env:
MYSQL_DATABASE: casdoor
MYSQL_ROOT_PASSWORD: 123456
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '^1.16.5'
- uses: actions/setup-node@v2
with:
node-version: 16
- name: front install
run: yarn install
working-directory: ./web
- name: front start
run: nohup yarn start &
working-directory: ./web
- name: back start
run: nohup go run ./main.go &
working-directory: ./
- name: Sleep for starting
run: sleep 60s
shell: bash
- name: e2e
run: npx cypress run --spec "**/e2e/**.cy.js"
working-directory: ./web
release-and-push:
name: Release And Push
runs-on: ubuntu-latest
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
needs: [ frontend, backend, linter ]
needs: [ frontend, backend, linter, e2e ]
steps:
- name: Checkout
uses: actions/checkout@v2

View File

@ -242,6 +242,7 @@ func (c *ApiController) Signup() {
// @router /logout [get,post]
func (c *ApiController) Logout() {
user := c.GetSessionUsername()
object.DeleteSessionId(user, c.Ctx.Input.CruSession.SessionID())
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
application := c.GetSessionApplication()
@ -271,12 +272,17 @@ func (c *ApiController) GetAccount() {
user = object.ExtendManagedAccountsWithUser(user)
}
object.ExtendUserWithRolesAndPermissions(user)
user.Permissions = object.GetMaskedPermissions(user.Permissions)
user.Roles = object.GetMaskedRoles(user.Roles)
organization := object.GetMaskedOrganization(object.GetOrganizationByUser(user))
resp := Response{
Status: "ok",
Sub: user.Id,
Name: user.Name,
Data: user,
Data: object.GetMaskedUser(user),
Data2: organization,
}
c.Data["json"] = resp

View File

@ -86,7 +86,7 @@ func (c *ApiController) GetUserApplication() {
id := c.Input().Get("id")
user := object.GetUser(id)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("application:The user: %s doesn't exist"), id))
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), id))
return
}

View File

@ -103,12 +103,12 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
resp = tokenToResponse(token)
}
} else if form.Type == ResponseTypeSaml { // saml flow
res, redirectUrl, err := object.GetSamlResponse(application, user, form.SamlRequest, c.Ctx.Request.Host)
res, redirectUrl, method, err := object.GetSamlResponse(application, user, form.SamlRequest, c.Ctx.Request.Host)
if err != nil {
c.ResponseError(err.Error(), nil)
return
}
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: redirectUrl}
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]string{"redirectUrl": redirectUrl, "method": method}}
} else if form.Type == ResponseTypeCas {
// not oauth but CAS SSO protocol
service := c.Input().Get("service")
@ -139,6 +139,10 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
})
}
if resp.Status == "ok" {
object.SetSession(user.GetId(), c.Ctx.Input.CruSession.SessionID())
}
return resp
}
@ -205,7 +209,7 @@ func (c *ApiController) Login() {
if form.Username != "" {
if form.Type == ResponseTypeLogin {
if c.GetSessionUsername() != "" {
c.ResponseError(c.T("auth:Please sign out first before signing in"), c.GetSessionUsername())
c.ResponseError(c.T("account:Please sign out first before signing in"), c.GetSessionUsername())
return
}
}
@ -222,12 +226,13 @@ func (c *ApiController) Login() {
}
// check result through Email or Phone
var checkDest string
if strings.Contains(form.Username, "@") {
verificationCodeType = "email"
if user != nil && util.GetMaskedEmail(user.Email) == form.Username {
form.Username = user.Email
}
checkResult = object.CheckVerificationCode(form.Username, form.Code, c.GetAcceptLanguage())
checkDest = form.Username
} else {
verificationCodeType = "phone"
if len(form.PhonePrefix) == 0 {
@ -238,11 +243,16 @@ func (c *ApiController) Login() {
if user != nil && util.GetMaskedPhone(user.Phone) == form.Username {
form.Username = user.Phone
}
checkPhone := fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)
checkResult = object.CheckVerificationCode(checkPhone, form.Code, c.GetAcceptLanguage())
checkDest = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)
}
user = object.GetUserByFields(form.Organization, form.Username)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(form.Organization, form.Username)))
return
}
checkResult = object.CheckSigninCode(user, checkDest, form.Code, c.GetAcceptLanguage())
if len(checkResult) != 0 {
responseText := fmt.Sprintf("%s%s", verificationCodeType, checkResult)
responseText := fmt.Sprintf("%s - %s", verificationCodeType, checkResult)
c.ResponseError(responseText)
return
}
@ -253,18 +263,16 @@ func (c *ApiController) Login() {
} else {
object.DisableVerificationCode(fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username))
}
user = object.GetUserByFields(form.Organization, form.Username)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The user: %s/%s doesn't exist"), form.Organization, form.Username))
return
}
} else {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
return
}
if !application.EnablePassword {
c.ResponseError(c.T("auth:The login method: login with password is not enabled for the application"))
return
}
if object.CheckToEnableCaptcha(application) {
isHuman, err := captcha.VerifyCaptchaByCaptchaType(form.CaptchaType, form.CaptchaToken, form.ClientSecret)
@ -414,7 +422,7 @@ func (c *ApiController) Login() {
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
initScore, err := getInitScore(organization)
if err != nil {
c.ResponseError(fmt.Errorf(c.T("auth:Get init score failed, error: %w"), err).Error())
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
return
}
@ -460,13 +468,13 @@ func (c *ApiController) Login() {
record2.User = user.Name
util.SafeGoroutine(func() { object.AddRecord(record2) })
} else if provider.Category == "SAML" {
resp = &Response{Status: "error", Msg: "The account does not exist"}
resp = &Response{Status: "error", Msg: fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(application.Organization, userInfo.Id))}
}
// resp = &Response{Status: "ok", Msg: "", Data: res}
} else { // form.Method != "signup"
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("auth:The account does not exist"), userInfo)
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(application.Organization, userInfo.Id)), userInfo)
return
}

View File

@ -47,7 +47,7 @@ func (c *ApiController) BatchEnforce() {
func (c *ApiController) GetAllObjects() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("enforcer:Please sign in first"))
c.ResponseError(c.T("general:Please login first"))
return
}
@ -58,7 +58,7 @@ func (c *ApiController) GetAllObjects() {
func (c *ApiController) GetAllActions() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("enforcer:Please sign in first"))
c.ResponseError(c.T("general:Please login first"))
return
}
@ -69,7 +69,7 @@ func (c *ApiController) GetAllActions() {
func (c *ApiController) GetAllRoles() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("enforcer:Please sign in first"))
c.ResponseError(c.T("general:Please login first"))
return
}

View File

@ -141,13 +141,13 @@ func (c *ApiController) BuyProduct() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("product:Please login first"))
c.ResponseError(c.T("general:Please login first"))
return
}
user := object.GetUser(userId)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("product:The user: %s doesn't exist"), userId))
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
return
}

68
controllers/session.go Normal file
View File

@ -0,0 +1,68 @@
// 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 controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// DeleteSession
// @Title DeleteSession
// @Tag Session API
// @Description Delete session by userId
// @Param ID query string true "The ID(owner/name) of user."
// @Success 200 {array} string The Response object
// @router /delete-session [post]
func (c *ApiController) DeleteSession() {
var session object.Session
err := json.Unmarshal(c.Ctx.Input.RequestBody, &session)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteSession(util.GetId(session.Owner, session.Name)))
c.ServeJSON()
}
// GetSessions
// @Title GetSessions
// @Tag Session API
// @Description Get organization user sessions
// @Param owner query string true "The organization name"
// @Success 200 {array} string The Response object
// @router /get-sessions [get]
func (c *ApiController) GetSessions() {
limit := c.Input().Get("pageSize")
page := c.Input().Get("p")
field := c.Input().Get("field")
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
owner := c.Input().Get("owner")
if limit == "" || page == "" {
c.Data["json"] = object.GetSessions(owner)
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetSessionCount(owner, field, value)))
sessions := object.GetPaginationSessions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
c.ResponseOk(sessions, paginator.Nums())
}
}

View File

@ -40,7 +40,7 @@ func (c *ApiController) GetSystemInfo() {
user := object.GetUser(id)
if user == nil || !user.IsGlobalAdmin {
c.ResponseError(c.T("system_info:You are not authorized to access this resource"))
c.ResponseError(c.T("auth:Unauthorized operation"))
return
}

View File

@ -150,7 +150,7 @@ func (c *ApiController) GetOAuthCode() {
codeChallenge := c.Input().Get("code_challenge")
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
c.ResponseError(c.T("token:Challenge method should be S256"))
c.ResponseError(c.T("auth:Challenge method should be S256"))
return
}
host := c.Ctx.Request.Host

View File

@ -159,6 +159,12 @@ func (c *ApiController) UpdateUser() {
}
isGlobalAdmin := c.IsGlobalAdmin()
if pass, err := checkPermissionForUpdateUser(id, user, c); !pass {
c.ResponseError(err)
return
}
affected := object.UpdateUser(id, &user, columns, isGlobalAdmin)
if affected {
object.UpdateUserToOriginalDatabase(&user)
@ -236,7 +242,7 @@ func (c *ApiController) GetEmailAndPhone() {
user := object.GetUserByFields(form.Organization, form.Username)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("user:The user: %s/%s doesn't exist"), form.Organization, form.Username))
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(form.Organization, form.Username)))
return
}

142
controllers/user_util.go Normal file
View File

@ -0,0 +1,142 @@
// 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 controllers
import (
"encoding/json"
"github.com/casdoor/casdoor/object"
)
func checkPermissionForUpdateUser(userId string, newUser object.User, c *ApiController) (bool, string) {
oldUser := object.GetUser(userId)
organization := object.GetOrganizationByUser(oldUser)
var itemsChanged []*object.AccountItem
if oldUser.Owner != newUser.Owner {
item := object.GetAccountItemByName("Organization", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Name != newUser.Name {
item := object.GetAccountItemByName("Name", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Id != newUser.Id {
item := object.GetAccountItemByName("ID", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.DisplayName != newUser.DisplayName {
item := object.GetAccountItemByName("Display name", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Avatar != newUser.Avatar {
item := object.GetAccountItemByName("Avatar", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Type != newUser.Type {
item := object.GetAccountItemByName("User type", organization)
itemsChanged = append(itemsChanged, item)
}
// The password is *** when not modified
if oldUser.Password != newUser.Password && newUser.Password != "***" {
item := object.GetAccountItemByName("Password", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Email != newUser.Email {
item := object.GetAccountItemByName("Email", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Phone != newUser.Phone {
item := object.GetAccountItemByName("Phone", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Region != newUser.Region {
item := object.GetAccountItemByName("Country/Region", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Location != newUser.Location {
item := object.GetAccountItemByName("Location", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Affiliation != newUser.Affiliation {
item := object.GetAccountItemByName("Affiliation", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Title != newUser.Title {
item := object.GetAccountItemByName("Title", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Homepage != newUser.Homepage {
item := object.GetAccountItemByName("Homepage", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Bio != newUser.Bio {
item := object.GetAccountItemByName("Bio", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Tag != newUser.Tag {
item := object.GetAccountItemByName("Tag", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.SignupApplication != newUser.SignupApplication {
item := object.GetAccountItemByName("Signup application", organization)
itemsChanged = append(itemsChanged, item)
}
oldUserRolesJson, _ := json.Marshal(oldUser.Roles)
newUserRolesJson, _ := json.Marshal(newUser.Roles)
if string(oldUserRolesJson) != string(newUserRolesJson) {
item := object.GetAccountItemByName("Roles", organization)
itemsChanged = append(itemsChanged, item)
}
oldUserPermissionJson, _ := json.Marshal(oldUser.Permissions)
newUserPermissionJson, _ := json.Marshal(newUser.Permissions)
if string(oldUserPermissionJson) != string(newUserPermissionJson) {
item := object.GetAccountItemByName("Permissions", organization)
itemsChanged = append(itemsChanged, item)
}
oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties)
newUserPropertiesJson, _ := json.Marshal(newUser.Properties)
if string(oldUserPropertiesJson) != string(newUserPropertiesJson) {
item := object.GetAccountItemByName("Properties", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.IsAdmin != newUser.IsAdmin {
item := object.GetAccountItemByName("Is admin", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.IsGlobalAdmin != newUser.IsGlobalAdmin {
item := object.GetAccountItemByName("Is global admin", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.IsForbidden != newUser.IsForbidden {
item := object.GetAccountItemByName("Is forbidden", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.IsDeleted != newUser.IsDeleted {
item := object.GetAccountItemByName("Is deleted", organization)
itemsChanged = append(itemsChanged, item)
}
for i := range itemsChanged {
if pass, err := object.CheckAccountItemModifyRule(itemsChanged[i], c.getCurrentUser(), c.GetAcceptLanguage()); !pass {
return pass, err
}
}
return true, ""
}

View File

@ -84,7 +84,7 @@ func (c *ApiController) SetTokenErrorHttpStatus() {
func (c *ApiController) RequireSignedIn() (string, bool) {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("util:Please login first"), "Please login first")
c.ResponseError(c.T("general:Please login first"), "Please login first")
return "", false
}
return userId, true
@ -100,7 +100,7 @@ func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
user := object.GetUser(userId)
if user == nil {
c.ClearUserSession()
c.ResponseError(fmt.Sprintf(c.T("util:The user: %s doesn't exist"), userId))
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
return nil, false
}
return user, true

View File

@ -99,7 +99,7 @@ func (c *ApiController) SendVerificationCode() {
}
if checkUser == "true" && user == nil && object.GetUserByFields(organization.Name, dest) == nil {
c.ResponseError(c.T("verification:Please login first"))
c.ResponseError(c.T("general:Please login first"))
return
}

View File

@ -35,7 +35,7 @@ func (c *ApiController) WebAuthnSignupBegin() {
webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host)
user := c.getCurrentUser()
if user == nil {
c.ResponseError(c.T("webauthn:Please login first"))
c.ResponseError(c.T("general:Please login first"))
return
}
@ -66,7 +66,7 @@ func (c *ApiController) WebAuthnSignupFinish() {
webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host)
user := c.getCurrentUser()
if user == nil {
c.ResponseError(c.T("webauthn:Please login first"))
c.ResponseError(c.T("general:Please login first"))
return
}
sessionObj := c.GetSession("registration")
@ -101,7 +101,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
userName := c.Input().Get("name")
user := object.GetUserByFields(userOwner, userName)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("webauthn:The user: %s/%s doesn't exist"), userOwner, userName))
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(userOwner, userName)))
return
}
if len(user.WebauthnCredentials) == 0 {

View File

@ -26,16 +26,22 @@ import (
type I18nData map[string]map[string]string
var reI18n *regexp.Regexp
var (
reI18nFrontend *regexp.Regexp
reI18nBackendObject *regexp.Regexp
reI18nBackendController *regexp.Regexp
)
func init() {
reI18n, _ = regexp.Compile("i18next.t\\(\"(.*?)\"\\)")
reI18nFrontend, _ = regexp.Compile("i18next.t\\(\"(.*?)\"\\)")
reI18nBackendObject, _ = regexp.Compile("i18n.Translate\\((.*?)\"\\)")
reI18nBackendController, _ = regexp.Compile("c.T\\((.*?)\"\\)")
}
func getAllI18nStrings(fileContent string) []string {
func getAllI18nStringsFrontend(fileContent string) []string {
res := []string{}
matches := reI18n.FindAllStringSubmatch(fileContent, -1)
matches := reI18nFrontend.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}
@ -46,17 +52,39 @@ func getAllI18nStrings(fileContent string) []string {
return res
}
func getAllJsFilePaths() []string {
path := "../web/src"
func getAllI18nStringsBackend(fileContent string, isObjectPackage bool) []string {
res := []string{}
err := filepath.Walk(path,
if isObjectPackage {
matches := reI18nBackendObject.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}
for _, match := range matches {
match := strings.SplitN(match[1], ",", 2)
res = append(res, match[1][2:])
}
} else {
matches := reI18nBackendController.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}
for _, match := range matches {
res = append(res, match[1][1:])
}
}
return res
}
func getAllFilePathsInFolder(folder string, fileSuffix string) []string {
res := []string{}
err := filepath.Walk(folder,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !strings.HasSuffix(info.Name(), ".js") {
if !strings.HasSuffix(info.Name(), fileSuffix) {
return nil
}
@ -71,12 +99,25 @@ func getAllJsFilePaths() []string {
return res
}
func parseToData() *I18nData {
func parseEnData(category string) *I18nData {
var paths []string
if category == "backend" {
paths = getAllFilePathsInFolder("../", ".go")
} else {
paths = getAllFilePathsInFolder("../web/src", ".js")
}
allWords := []string{}
paths := getAllJsFilePaths()
for _, path := range paths {
fileContent := util.ReadStringFromPath(path)
words := getAllI18nStrings(fileContent)
var words []string
if category == "backend" {
isObjectPackage := strings.Contains(path, "object")
words = getAllI18nStringsBackend(fileContent, isObjectPackage)
} else {
words = getAllI18nStringsFrontend(fileContent)
}
allWords = append(allWords, words...)
}
fmt.Printf("%v\n", allWords)

View File

@ -1,115 +0,0 @@
// 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 i18n
import (
"os"
"path/filepath"
"regexp"
"strings"
"github.com/casdoor/casdoor/util"
)
var (
reI18nBackendObject *regexp.Regexp
re18nBackendController *regexp.Regexp
)
func init() {
reI18nBackendObject, _ = regexp.Compile("i18n.Translate\\((.*?)\"\\)")
re18nBackendController, _ = regexp.Compile("c.T\\((.*?)\"\\)")
}
func GetAllI18nStrings(fileContent string, path string) []string {
res := []string{}
if strings.Contains(path, "object") {
matches := reI18nBackendObject.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}
for _, match := range matches {
match := strings.SplitN(match[1], ",", 2)
res = append(res, match[1][2:])
}
} else {
matches := re18nBackendController.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}
for _, match := range matches {
res = append(res, match[1][1:])
}
}
return res
}
func getAllGoFilePaths() []string {
path := "../"
res := []string{}
err := filepath.Walk(path,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !strings.HasSuffix(info.Name(), ".go") {
return nil
}
res = append(res, path)
// fmt.Println(path, info.Name())
return nil
})
if err != nil {
panic(err)
}
return res
}
func getErrName(paths []string) map[string]string {
ErrName := make(map[string]string)
for i := 0; i < len(paths); i++ {
content := util.ReadStringFromPath(paths[i])
words := GetAllI18nStrings(content, paths[i])
for j := 0; j < len(words); j++ {
ErrName[words[j]] = paths[i]
}
}
return ErrName
}
func getI18nJSONData(errName map[string]string) *I18nData {
data := I18nData{}
for k, v := range errName {
var index int
if strings.Contains(v, "/") {
index = strings.LastIndex(v, "/")
} else {
index = strings.LastIndex(v, "\\")
}
namespace := v[index+1 : len(v)-3]
key := k[len(namespace)+1:]
// fmt.Printf("k=%s,v=%s,namespace=%s,key=%s\n", k, v, namespace, key)
if _, ok := data[namespace]; !ok {
data[namespace] = map[string]string{}
}
data[namespace][key] = key
}
return &data
}

View File

@ -15,50 +15,39 @@
package i18n
import (
"fmt"
"testing"
)
func applyToOtherLanguage(dataEn *I18nData, lang string) {
dataOther := readI18nFile(lang)
println(dataOther)
func applyToOtherLanguage(category string, language string, i18nData *I18nData) {
newData := readI18nFile(category, language)
println(newData)
applyData(dataEn, dataOther)
writeI18nFile(lang, dataEn)
applyData(i18nData, newData)
writeI18nFile(category, language, i18nData)
}
func TestGenerateI18nStringsForFrontend(t *testing.T) {
dataEn := parseToData()
writeI18nFile("en", dataEn)
func TestGenerateI18nFrontend(t *testing.T) {
enData := parseEnData("frontend")
writeI18nFile("frontend", "en", enData)
applyToOtherLanguage(dataEn, "de")
applyToOtherLanguage(dataEn, "fr")
applyToOtherLanguage(dataEn, "ja")
applyToOtherLanguage(dataEn, "ko")
applyToOtherLanguage(dataEn, "ru")
applyToOtherLanguage(dataEn, "zh")
applyToOtherLanguage("frontend", "de", enData)
applyToOtherLanguage("frontend", "es", enData)
applyToOtherLanguage("frontend", "fr", enData)
applyToOtherLanguage("frontend", "ja", enData)
applyToOtherLanguage("frontend", "ko", enData)
applyToOtherLanguage("frontend", "ru", enData)
applyToOtherLanguage("frontend", "zh", enData)
}
func TestGenerateI18nStringsForBackend(t *testing.T) {
paths := getAllGoFilePaths()
func TestGenerateI18nBackend(t *testing.T) {
enData := parseEnData("backend")
writeI18nFile("backend", "en", enData)
errName := getErrName(paths)
dataEn := getI18nJSONData(errName)
writeI18nFile("backend_en", dataEn)
applyToOtherLanguage(dataEn, "backend_de")
applyToOtherLanguage(dataEn, "backend_es")
applyToOtherLanguage(dataEn, "backend_fr")
applyToOtherLanguage(dataEn, "backend_ja")
applyToOtherLanguage(dataEn, "backend_ko")
applyToOtherLanguage(dataEn, "backend_ru")
applyToOtherLanguage(dataEn, "backend_zh")
fmt.Println("Total Err Words:", len(errName))
for i := range errName {
fmt.Println(i)
}
applyToOtherLanguage("backend", "de", enData)
applyToOtherLanguage("backend", "es", enData)
applyToOtherLanguage("backend", "fr", enData)
applyToOtherLanguage("backend", "ja", enData)
applyToOtherLanguage("backend", "ko", enData)
applyToOtherLanguage("backend", "ru", enData)
applyToOtherLanguage("backend", "zh", enData)
}

View File

@ -4,31 +4,28 @@
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"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",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"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 type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"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/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +50,8 @@
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"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": "The user: %s doesn't exist",
"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",
@ -65,16 +59,13 @@
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"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",
"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"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
"general": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
@ -93,10 +84,6 @@
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
@ -120,11 +107,7 @@
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"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",
@ -134,8 +117,7 @@
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
@ -143,9 +125,7 @@
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"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!",
@ -154,7 +134,6 @@
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"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.",
@ -165,8 +144,6 @@
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

View File

@ -4,31 +4,28 @@
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"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",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"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 type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"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/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +50,8 @@
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"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": "The user: %s doesn't exist",
"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",
@ -65,16 +59,13 @@
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"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",
"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"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
"general": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
@ -93,10 +84,6 @@
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
@ -120,11 +107,7 @@
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"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",
@ -134,8 +117,7 @@
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
@ -143,9 +125,7 @@
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"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!",
@ -154,7 +134,6 @@
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"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.",
@ -165,8 +144,6 @@
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

View File

@ -4,31 +4,28 @@
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"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",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"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 type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"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/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +50,8 @@
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"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": "The user: %s doesn't exist",
"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",
@ -65,16 +59,13 @@
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"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",
"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"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
"general": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
@ -93,10 +84,6 @@
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
@ -120,11 +107,7 @@
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"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",
@ -134,8 +117,7 @@
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
@ -143,9 +125,7 @@
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"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!",
@ -154,7 +134,6 @@
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"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.",
@ -165,8 +144,6 @@
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

View File

@ -4,31 +4,28 @@
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"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",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"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 type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"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/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +50,8 @@
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"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": "The user: %s doesn't exist",
"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",
@ -65,16 +59,13 @@
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"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",
"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"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
"general": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
@ -93,10 +84,6 @@
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
@ -120,11 +107,7 @@
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"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",
@ -134,8 +117,7 @@
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
@ -143,9 +125,7 @@
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"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!",
@ -154,7 +134,6 @@
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"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.",
@ -165,8 +144,6 @@
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

View File

@ -4,31 +4,28 @@
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"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",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"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 type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"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/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +50,8 @@
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"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": "The user: %s doesn't exist",
"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",
@ -65,16 +59,13 @@
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"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",
"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"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
"general": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
@ -93,10 +84,6 @@
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
@ -120,11 +107,7 @@
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"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",
@ -134,8 +117,7 @@
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
@ -143,9 +125,7 @@
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"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!",
@ -154,7 +134,6 @@
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"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.",
@ -165,8 +144,6 @@
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

View File

@ -4,31 +4,28 @@
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"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",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"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 type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"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/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +50,8 @@
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"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": "The user: %s doesn't exist",
"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",
@ -65,16 +59,13 @@
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"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",
"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"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
"general": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
@ -93,10 +84,6 @@
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
@ -120,11 +107,7 @@
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"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",
@ -134,8 +117,7 @@
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
@ -143,9 +125,7 @@
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"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!",
@ -154,7 +134,6 @@
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"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.",
@ -165,8 +144,6 @@
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

View File

@ -4,31 +4,28 @@
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"Parameter organization is missing": "Parameter organization is missing"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"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",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"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 type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"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/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +50,8 @@
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"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": "The user: %s doesn't exist",
"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",
@ -65,16 +59,13 @@
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"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",
"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"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
"general": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
@ -93,10 +84,6 @@
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
@ -120,11 +107,7 @@
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"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",
@ -134,8 +117,7 @@
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
@ -143,9 +125,7 @@
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
"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!",
@ -154,7 +134,6 @@
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"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.",
@ -165,8 +144,6 @@
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

View File

@ -3,170 +3,147 @@
"Email: %s": "邮件: %s",
"Get init score failed, error: %w": "初始化分数失败: %w",
"Invalid information": "无效信息",
"Phone: %s": "电话: %s",
"Please sign out first before signing up": "请在登陆前登出",
"The application does not allow to sign up new account": "该应用不允许注册新账户"
"Phone: %s": "手机号: %s",
"Please sign out first before signing in": "请在登录前先退出登录",
"Please sign out first before signing up": "请在注册前先退出登录",
"The application does not allow to sign up new account": "该应用不允许注册新用户"
},
"application": {
"Parameter organization is missing": "Organization参数丢失",
"The user: %s doesn't exist": "用户不存在: %s"
"Parameter organization is missing": "缺少organization参数"
},
"auth": {
"%s No phone prefix": "%s 无此电话前缀",
"%s No phone prefix": "%s 无此手机号前缀",
"Challenge method should be S256": "Challenge 方法应该为 S256",
"Failed to create user, user information is invalid: %s": "创建用户失败,用户信息无效: %s",
"Failed to login in: %s": "无法登录: %s",
"Get init score failed, error: %w": "初始化分数失败: %w",
"Failed to login in: %s": "登录失败: %s",
"Invalid token": "无效token",
"Please sign out first before signing in": "请在登陆前登出",
"State expected: %s, but got: %s": "期望状态位: %s, 实际状态为: %s",
"The account does not exist": "账户不存在",
"State expected: %s, but got: %s": "期望状态为: %s, 实际状态为: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许通过 %s 注册新账户, 请使用其他方式注册",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许注册新账户, 请联系IT支持",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "提供商账户: %s 与用户名: %s (%s) 已经与其他账户绑定: %s (%s)",
"The application: %s does not exist": "应用 %s 不存在",
"The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式",
"The provider type: %s is not supported": "不支持该类型的提供商: %s",
"The provider: %s is not enabled for the application": "提供商: %s 未被启用",
"The user is forbidden to sign in, please contact the administrator": "该用户被禁止登,请联系管理员",
"The user: %s/%s doesn't exist": "用户不存在: %s/%s",
"Turing test failed.": "真人验证失败",
"The provider: %s is not enabled for the application": "该应用的提供商: %s 未被启用",
"The user is forbidden to sign in, please contact the administrator": "该用户被禁止登,请联系管理员",
"Turing test failed.": "人机验证失败",
"Unauthorized operation": "未授权的操作",
"Unknown authentication type (not password or provider), form = %s": "未授权的操作"
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s"
},
"cas": {
"Service %s and %s do not match": "服务 %s 与 %s 不匹配"
},
"check": {
"Affiliation cannot be blank": "联系方式不可为空",
"DisplayName cannot be blank": "示名称不可为空",
"DisplayName is not valid real name": "示名称无效",
"Affiliation cannot be blank": "工作单位不可为空",
"DisplayName cannot be blank": "示名称不可为空",
"DisplayName is not valid real name": "示名称必须是真实姓名",
"Email already exists": "该邮箱已存在",
"Email cannot be empty": "邮箱不可为空",
"Email is invalid": "无效邮箱",
"Empty username.": "用户名不可为空",
"FirstName cannot be blank": "名不可以为空",
"LastName cannot be blank": "姓不可以为空",
"Ldap user name or password incorrect": "Ldap密码错误",
"Multiple accounts with same uid, please check your ldap server": "多个帐户具有相同的uid请检查您的 ldap 服务器",
"Ldap user name or password incorrect": "LDAP密码错误",
"Multiple accounts with same uid, please check your ldap server": "多个帐户具有相同的uid请检查您的 LDAP 服务器",
"Organization does not exist": "组织不存在",
"Password must have at least 6 characters": "新密码至少为6位",
"Phone already exists": "该电话已存在",
"Phone cannot be empty": "电话不可为空",
"Phone number is invalid": "无效电话",
"Please login first": "请先登录",
"Session outdated, please login again": "Session已过期请重新登陆",
"The user doesn't exist": "用户不存在",
"The user is forbidden to sign in, please contact the administrator": "该用户被禁止登陆,请联系管理员",
"The user: %s doesn't exist": "用户不存在: %s",
"Phone already exists": "该手机号已存在",
"Phone cannot be empty": "手机号不可为空",
"Phone number is invalid": "无效手机号",
"Session outdated, please login again": "会话已过期,请重新登录",
"The user is forbidden to sign in, please contact the administrator": "该用户被禁止登录,请联系管理员",
"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 cannot be an email address": "用户名不可以是邮箱地址",
"Username cannot contain white spaces": "用户名不可以包含空格",
"Username cannot start with a digit": "用户名禁止使用数字作为第一个字符",
"Username is too long (maximum is 39 characters).": "用户名过长最大长度为39个字符",
"Username cannot contain white spaces": "用户名禁止包含空格",
"Username cannot start with a digit": "用户名禁止使用数字开头",
"Username is too long (maximum is 39 characters).": "用户名过长(最大允许长度为39个字符",
"Username must have at least 2 characters": "用户名至少要有2个字符",
"You don't have the permission to do this": "用户名至少要有2个字符",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "输入密码错误次数已达上限,请在 %d 分 %d 秒后重试",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "密码错误次数已达上限,请在 %d 分后重试",
"password or code is incorrect, you have %d remaining chances": "密码错误,您还有 %d 次尝试的机会",
"unsupported password type: %s": "不支持的密码类型: %s"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "输入密码错误次数已达上限,请在 %d 分后重试",
"password is incorrect, you have %d remaining chances": "密码错误,您还有 %d 次尝试的机会"
},
"enforcer": {
"Please sign in first": "请先登录"
"general": {
"Please login first": "请先登录",
"The user: %s doesn't exist": "用户: %s 不存在"
},
"ldap": {
"Ldap server exist": "Ldap服务器已存在",
"Missing parameter": "参数丢失"
"Ldap server exist": "LDAP服务器已存在",
"Missing parameter": "LDAP缺少参数"
},
"link": {
"Please link first": "请先绑定",
"This application has no providers": "该应用无提供商",
"This application has no providers": "该应用无可用的提供商",
"This application has no providers of type": "应用没有该类型的提供商",
"This provider can't be unlinked": "该提供商不可被链接",
"You are not the global admin, you can't unlink other users": "您不是全局管理员,无法取消链接其他用户",
"You can't unlink yourself, you are not a member of any application": "您无法取消链接,您不是任何应用程序的成员"
"This provider can't be unlinked": "该提供商被禁止解绑",
"You are not the global admin, you can't unlink other users": "您不是全局管理员,无法解绑其他用户",
"You can't unlink yourself, you are not a member of any application": "您无法自行解绑,您不是任何应用程序的成员"
},
"organization": {
"Only admin can modify the %s.": "您无法取消链接,您不是任何应用程序的成员",
"The %s is immutable.": "%s是不可变的",
"Unknown modify rule %s.": "未知的修改规则"
},
"product": {
"Please login first": "请先登录",
"The user: %s doesn't exist": "用户不存在: %s"
"Only admin can modify the %s.": "仅允许管理员可以修改 %s",
"The %s is immutable.": "%s 是不可变的",
"Unknown modify rule %s.": "未知的修改规则: %s"
},
"provider": {
"Invalid application id": "无效的Application ID",
"Invalid application id": "无效的应用ID",
"the provider: %s does not exist": "提供商: %s 不存在"
},
"resource": {
"User is nil for tag: avatar": "用户头像标签为空",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "username或FilePath为空: username = %s, fullFilePath = %s"
"User is nil for tag: avatar": "上传头像时用户为空",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "username或fullFilePath为空: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "应用 %s 未找到"
"Application %s not found": "未找到应用: %s"
},
"saml_sp": {
"provider %s's category is not SAML": "提供商 %s类型不是SAML"
"provider %s's category is not SAML": "提供商: %s 不是SAML类型"
},
"service": {
"Empty parameters for emailForm: %v": "邮件参数为空: %v",
"Invalid Email receivers: %s": " 无效的邮箱接收者: %s",
"Invalid phone receivers: %s": "无效的电话接收者: %s"
"Invalid Email receivers: %s": " 无效的邮箱收件人: %s",
"Invalid phone receivers: %s": "无效的手机短信收信人: %s"
},
"storage": {
"The objectKey: %s is not allowed": "object key :%s 不被允许",
"The provider type: %s is not supported": "提供商类型: %s 尚未支持"
},
"system_info": {
"You are not authorized to access this resource": "您无权获取此资源"
"The objectKey: %s is not allowed": "objectKey: %s 被禁止",
"The provider type: %s is not supported": "不支持的提供商类型: %s"
},
"token": {
"Challenge method should be S256": "Challenge 方法应该为 S256",
"Empty clientId or clientSecret": "clientId或clientSecret为空",
"Grant_type: %s is not supported in this application": "应用不支持此授权类型: %s",
"Grant_type: %s is not supported in this application": "应用不支持Grant_type: %s",
"Invalid application or wrong clientSecret": "无效应用或错误的clientSecret",
"Invalid client_id": "无效的ClientId",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "重定向 URI%s 在列表中未找到"
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "重定向 URI%s 在许可跳转列表中未找到"
},
"user": {
"Display name cannot be empty": "示名称不可为空",
"Display name cannot be empty": "示名称不可为空",
"New password cannot contain blank space.": "新密码不可以包含空格",
"New password must have at least 6 characters": "新密码至少需要6位字符",
"The user: %s/%s doesn't exist": "用户不存在: %s/%s"
"New password must have at least 6 characters": "新密码至少需要6位字符"
},
"user_upload": {
"Failed to import users": "导入用户失败"
},
"util": {
"No application is found for userId: %s": "找不到该用户的应用程序 %s",
"No provider for category: %s is found for application: %s": "找不到该用户的应用程序 %s",
"Please login first": "请先登录",
"The provider: %s is not found": "该提供商未找到: %s",
"The user: %s doesn't exist": "用户不存在: %s"
"No application is found for userId: %s": "未找到用户: %s 的应用",
"No provider for category: %s is found for application: %s": "未找到类别为: %s 的提供商来满足应用: %s",
"The provider: %s is not found": "未找到提供商: %s"
},
"verification": {
"Code has not been sent yet!": "验证码还未发送",
"Email is invalid": "非法的邮箱",
"Invalid captcha provider.": "非法的验证码提供商",
"Missing parameter": "参数丢失",
"Missing parameter": "缺少参数",
"Organization does not exist": "组织不存在",
"Phone number is invalid": "非法的电话号码",
"Please login first": "请先登录",
"Phone number is invalid": "非法的手机号码",
"Turing test failed.": "验证码还未发送",
"Unable to get the email modify rule.": "无法得到邮箱修改规则",
"Unable to get the phone modify rule.": "无法得到电话修改规则",
"Unable to get the email modify rule.": "无法获取邮箱修改规则",
"Unable to get the phone modify rule.": "无法获取手机号修改规则",
"Unknown type": "未知类型",
"Wrong parameter": "参数错误",
"You should verify your code in %d min!": "请在 %d 分钟内输入正确验证码",
"the user does not exist, please sign up first": "用户不存在,请先注册"
},
"webauthn": {
"Found no credentials for this user": "该用户没有WebAuthn凭据",
"Please call WebAuthnSigninBegin first": "请先调用 WebAuthnSigninBegi",
"Please login first": "请先登录",
"The user: %s/%s doesn't exist": "用户: %s/%s 不存在"
"Found no credentials for this user": "该用户没有 WebAuthn 凭据",
"Please call WebAuthnSigninBegin first": "请先调用 WebAuthnSigninBegin"
}
}

View File

@ -27,18 +27,16 @@ var f embed.FS
var langMap = make(map[string]map[string]map[string]string) // for example : langMap[en][account][Invalid information] = Invalid information
func getI18nFilePath(language string) string {
if strings.Contains(language, "backend") {
// change language from 'backend_en' to 'en'
language = language[8:]
func getI18nFilePath(category string, language string) string {
if category == "backend" {
return fmt.Sprintf("../i18n/locales/%s/data.json", language)
} else {
return fmt.Sprintf("../web/src/locales/%s/data.json", language)
}
}
func readI18nFile(language string) *I18nData {
s := util.ReadStringFromPath(getI18nFilePath(language))
func readI18nFile(category string, language string) *I18nData {
s := util.ReadStringFromPath(getI18nFilePath(category, language))
data := &I18nData{}
err := util.JsonToStruct(s, data)
@ -48,13 +46,13 @@ func readI18nFile(language string) *I18nData {
return data
}
func writeI18nFile(language string, data *I18nData) {
func writeI18nFile(category string, language string, data *I18nData) {
s := util.StructToJsonFormatted(data)
s = strings.ReplaceAll(s, "\\u0026", "&")
s += "\n"
println(s)
util.WriteStringToPath(s, getI18nFilePath(language))
util.WriteStringToPath(s, getI18nFilePath(category, language))
}
func applyData(data1 *I18nData, data2 *I18nData) {

View File

@ -222,6 +222,11 @@ func (a *Adapter) createTable() {
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(Session))
if err != nil {
panic(err)
}
}
func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {

View File

@ -143,11 +143,11 @@ func checkSigninErrorTimes(user *User, lang string) string {
if user.SigninWrongTimes >= SigninWrongTimesLimit {
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
seconds := int(LastSignWrongTimeDuration.Seconds() - passedTime.Seconds())
minutes := int(LastSignWrongTimeDuration.Minutes() - passedTime.Minutes())
// deny the login if the error times is greater than the limit and the last login time is less than the duration
if seconds > 0 {
return fmt.Sprintf(i18n.Translate(lang, "check:You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again"), seconds/60, seconds%60)
if minutes > 0 {
return fmt.Sprintf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), minutes)
}
// reset the error times
@ -229,7 +229,7 @@ func checkLdapUserPassword(user *User, password string, lang string) (*User, str
func CheckUserPassword(organization string, username string, password string, lang string) (*User, string) {
user := GetUserByFields(organization, username)
if user == nil || user.IsDeleted == true {
return nil, i18n.Translate(lang, "check:The user doesn't exist")
return nil, fmt.Sprintf(i18n.Translate(lang, "general:The user: %s doesn't exist"), util.GetId(organization, username))
}
if user.IsForbidden {
@ -254,13 +254,13 @@ func filterField(field string) bool {
func CheckUserPermission(requestUserId, userId, userOwner string, strict bool, lang string) (bool, error) {
if requestUserId == "" {
return false, fmt.Errorf(i18n.Translate(lang, "check:Please login first"))
return false, fmt.Errorf(i18n.Translate(lang, "general:Please login first"))
}
if userId != "" {
targetUser := GetUser(userId)
if targetUser == nil {
return false, fmt.Errorf(i18n.Translate(lang, "check:The user: %s doesn't exist"), userId)
return false, fmt.Errorf(i18n.Translate(lang, "general:The user: %s doesn't exist"), userId)
}
userOwner = targetUser.Owner
@ -287,7 +287,7 @@ func CheckUserPermission(requestUserId, userId, userOwner string, strict bool, l
}
}
return hasPermission, fmt.Errorf(i18n.Translate(lang, "check:You don't have the permission to do this"))
return hasPermission, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
}
func CheckAccessPermission(userId string, application *Application) (bool, error) {

View File

@ -58,9 +58,9 @@ func recordSigninErrorInfo(user *User, lang string) string {
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin)
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
if leftChances > 0 {
return fmt.Sprintf(i18n.Translate(lang, "check_util:password is incorrect, you have %d remaining chances"), leftChances)
return fmt.Sprintf(i18n.Translate(lang, "check:password or code is incorrect, you have %d remaining chances"), leftChances)
}
// don't show the chance error message if the user has no chance left
return fmt.Sprintf(i18n.Translate(lang, "check_util:You have entered the wrong password too many times, please wait for %d minutes and try again"), int(LastSignWrongTimeDuration.Minutes()))
return fmt.Sprintf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), int(LastSignWrongTimeDuration.Minutes()))
}

View File

@ -76,7 +76,7 @@ func GetOidcDiscovery(host string) OidcDiscovery {
JwksUri: fmt.Sprintf("%s/.well-known/jwks", originBackend),
IntrospectionEndpoint: fmt.Sprintf("%s/api/login/oauth/introspect", originBackend),
ResponseTypesSupported: []string{"code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token", "none"},
ResponseModesSupported: []string{"login", "code", "link"},
ResponseModesSupported: []string{"query", "fragment", "login", "code", "link"},
GrantTypesSupported: []string{"password", "authorization_code"},
SubjectTypesSupported: []string{"public"},
IdTokenSigningAlgValuesSupported: []string{"RS256"},

View File

@ -204,7 +204,7 @@ func GetAccountItemByName(name string, organization *Organization) *AccountItem
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User, lang string) (bool, string) {
switch accountItem.ModifyRule {
case "Admin":
if !(user.IsAdmin || user.IsGlobalAdmin) {
if user == nil || !user.IsAdmin && !user.IsGlobalAdmin {
return false, fmt.Sprintf(i18n.Translate(lang, "organization:Only admin can modify the %s."), accountItem.Name)
}
case "Immutable":

View File

@ -269,3 +269,12 @@ func ContainsAsterisk(userId string, users []string) bool {
return containsAsterisk
}
func GetMaskedPermissions(permissions []*Permission) []*Permission {
for _, permission := range permissions {
permission.Users = nil
permission.Submitter = ""
}
return permissions
}

View File

@ -192,3 +192,11 @@ func roleChangeTrigger(oldName string, newName string) error {
return session.Commit()
}
func GetMaskedRoles(roles []*Role) []*Role {
for _, role := range roles {
role.Users = nil
}
return roles
}

View File

@ -223,11 +223,14 @@ func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, e
// GetSamlResponse generates a SAML2.0 response
// parameter samlRequest is saml request in base64 format
func GetSamlResponse(application *Application, user *User, samlRequest string, host string) (string, string, error) {
func GetSamlResponse(application *Application, user *User, samlRequest string, host string) (string, string, string, error) {
// request type
method := "GET"
// base64 decode
defated, err := base64.StdEncoding.DecodeString(samlRequest)
if err != nil {
return "", "", fmt.Errorf("err: %s", err.Error())
return "", "", method, fmt.Errorf("err: %s", err.Error())
}
// decompress
var buffer bytes.Buffer
@ -236,12 +239,12 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
var authnRequest saml.AuthnRequest
err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
if err != nil {
return "", "", fmt.Errorf("err: %s", err.Error())
return "", "", method, fmt.Errorf("err: %s", err.Error())
}
// verify samlRequest
if isValid := application.IsRedirectUriValid(authnRequest.Issuer.Url); !isValid {
return "", "", fmt.Errorf("err: Issuer URI: %s doesn't exist in the allowed Redirect URI list", authnRequest.Issuer.Url)
return "", "", method, fmt.Errorf("err: Issuer URI: %s doesn't exist in the allowed Redirect URI list", authnRequest.Issuer.Url)
}
// get certificate string
@ -253,6 +256,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
// redirect Url (Assertion Consumer Url)
if application.SamlReplyUrl != "" {
method = "POST"
authnRequest.AssertionConsumerServiceURL = application.SamlReplyUrl
}
@ -275,7 +279,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
doc.SetRoot(samlResponse)
xmlBytes, err := doc.WriteToBytes()
if err != nil {
return "", "", fmt.Errorf("err: %s", err.Error())
return "", "", method, fmt.Errorf("err: %s", err.Error())
}
// compress
@ -283,7 +287,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
flated := bytes.NewBuffer(nil)
writer, err := flate.NewWriter(flated, flate.DefaultCompression)
if err != nil {
return "", "", fmt.Errorf("err: %s", err.Error())
return "", "", method, fmt.Errorf("err: %s", err.Error())
}
writer.Write(xmlBytes)
writer.Close()
@ -291,7 +295,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
}
// base64 encode
res := base64.StdEncoding.EncodeToString(xmlBytes)
return res, authnRequest.AssertionConsumerServiceURL, nil
return res, authnRequest.AssertionConsumerServiceURL, method, nil
}
// NewSamlResponse11 return a saml1.1 response(not 2.0)

132
object/session.go Normal file
View File

@ -0,0 +1,132 @@
// 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 object
import (
"time"
"github.com/beego/beego"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
)
type Session struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
SessionId []string `json:"sessionId"`
}
func SetSession(id string, sessionId string) {
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
session := &Session{Owner: owner, Name: name}
get, err := adapter.Engine.Get(session)
if err != nil {
panic(err)
}
session.SessionId = append(session.SessionId, sessionId)
if get {
_, err = adapter.Engine.ID(core.PK{owner, name}).Update(session)
} else {
session.CreatedTime = time.Now().Format(time.RFC3339)
_, err = adapter.Engine.Insert(session)
}
if err != nil {
panic(err)
}
}
func DeleteSession(id string) bool {
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
session := &Session{Owner: owner, Name: name}
_, err := adapter.Engine.ID(core.PK{owner, name}).Get(session)
if err != nil {
return false
}
DeleteBeegoSession(session.SessionId)
affected, err := adapter.Engine.ID(core.PK{owner, name}).Delete(session)
return affected != 0
}
func DeleteSessionId(id string, sessionId string) bool {
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
session := &Session{Owner: owner, Name: name}
_, err := adapter.Engine.ID(core.PK{owner, name}).Get(session)
if err != nil {
return false
}
DeleteBeegoSession([]string{sessionId})
session.SessionId = util.DeleteVal(session.SessionId, sessionId)
if len(session.SessionId) < 1 {
affected, _ := adapter.Engine.ID(core.PK{owner, name}).Delete(session)
return affected != 0
} else {
affected, _ := adapter.Engine.ID(core.PK{owner, name}).Update(session)
return affected != 0
}
}
func DeleteBeegoSession(sessionIds []string) {
for _, sessionId := range sessionIds {
err := beego.GlobalSessions.GetProvider().SessionDestroy(sessionId)
if err != nil {
return
}
}
}
func GetSessions(owner string) []*Session {
sessions := []*Session{}
var err error
if owner != "" {
err = adapter.Engine.Desc("created_time").Where("owner = ?", owner).Find(&sessions)
} else {
err = adapter.Engine.Desc("created_time").Find(&sessions)
}
if err != nil {
panic(err)
}
return sessions
}
func GetPaginationSessions(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Session {
sessions := []*Session{}
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
err := session.Find(&sessions)
if err != nil {
panic(err)
}
return sessions
}
func GetSessionCount(owner, field, value string) int {
session := GetSession(owner, -1, -1, field, value, "", "")
count, err := session.Count(&Session{})
if err != nil {
panic(err)
}
return int(count)
}

View File

@ -265,7 +265,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
user := GetUser(userId)
if user == nil {
return &Code{
Message: fmt.Sprintf("token:The user: %s doesn't exist", userId),
Message: fmt.Sprintf("general:The user: %s doesn't exist", userId),
Code: "",
}
}

View File

@ -529,6 +529,9 @@ func AddUsersInBatch(users []*User) bool {
}
func DeleteUser(user *User) bool {
// Forced offline the user first
DeleteSession(user.GetId())
affected, err := adapter.Engine.ID(core.PK{user.Owner, user.Name}).Delete(&User{})
if err != nil {
panic(err)

View File

@ -26,6 +26,10 @@ import (
"xorm.io/core"
)
const (
wrongCode = "wrongCode"
)
type VerificationRecord struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
@ -167,7 +171,7 @@ func CheckVerificationCode(dest, code, lang string) string {
}
if record.Code != code {
return "Wrong code!"
return wrongCode
}
return ""
@ -186,6 +190,24 @@ func DisableVerificationCode(dest string) {
}
}
func CheckSigninCode(user *User, dest, code, lang string) string {
// check the login error times
if msg := checkSigninErrorTimes(user, lang); msg != "" {
return msg
}
result := CheckVerificationCode(dest, code, lang)
switch result {
case "":
resetUserSigninErrorTimes(user)
return ""
case wrongCode:
return recordSigninErrorInfo(user, lang)
default:
return result
}
}
// From Casnode/object/validateCode.go line 116
var stdNums = []byte("0123456789")

View File

@ -164,6 +164,9 @@ func initAPI() {
beego.Router("/api/get-records-filter", &controllers.ApiController{}, "POST:GetRecordsByFilter")
beego.Router("/api/add-record", &controllers.ApiController{}, "POST:AddRecord")
beego.Router("/api/get-sessions", &controllers.ApiController{}, "GET:GetSessions")
beego.Router("/api/delete-session", &controllers.ApiController{}, "POST:DeleteSession")
beego.Router("/api/get-webhooks", &controllers.ApiController{}, "GET:GetWebhooks")
beego.Router("/api/get-webhook", &controllers.ApiController{}, "GET:GetWebhook")
beego.Router("/api/update-webhook", &controllers.ApiController{}, "POST:UpdateWebhook")

25
util/slice.go Normal file
View File

@ -0,0 +1,25 @@
// 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
func DeleteVal(values []string, val string) []string {
newValues := []string{}
for _, v := range values {
if v != val {
newValues = append(newValues, v)
}
}
return newValues
}

7
web/cypress.config.js Normal file
View File

@ -0,0 +1,7 @@
module.exports = {
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
};

View File

@ -0,0 +1,56 @@
describe("Login test", () => {
const selector = {
username: "#input",
password: "#normal_login_password",
loginButton: ".ant-btn",
};
it("Login succeeded", () => {
cy.request({
method: "POST",
url: "http://localhost:7001/api/login",
body: {
"application": "app-built-in",
"organization": "built-in",
"username": "admin",
"password": "123",
"autoSignin": true,
"type": "login",
"phonePrefix": "86",
},
}).then((Response) => {
expect(Response).property("body").property("status").to.equal("ok");
});
});
it("ui Login succeeded", () => {
cy.visit("http://localhost:7001");
cy.get(selector.username).type("admin");
cy.get(selector.password).type("123");
cy.get(selector.loginButton).click();
cy.url().should("eq", "http://localhost:7001/");
});
it("Login failed", () => {
cy.request({
method: "POST",
url: "http://localhost:7001/api/login",
body: {
"application": "app-built-in",
"organization": "built-in",
"username": "admin",
"password": "1234",
"autoSignin": true,
"type": "login",
"phonePrefix": "86",
},
}).then((Response) => {
expect(Response).property("body").property("status").to.equal("error");
});
});
it("ui Login failed", () => {
cy.visit("http://localhost:7001");
cy.get(selector.username).type("admin");
cy.get(selector.password).type("1234");
cy.get(selector.loginButton).click();
cy.url().should("eq", "http://localhost:7001/login");
});
});

View File

@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

View File

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@ -9,7 +9,7 @@
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"antd": "5.0.5",
"antd": "5.1.2",
"codemirror": "^5.61.1",
"copy-to-clipboard": "^3.3.1",
"core-js": "^3.25.0",

View File

@ -170,7 +170,7 @@ class AccountTable extends React.Component {
}
let options;
if (record.viewRule === "Admin") {
if (record.viewRule === "Admin" || record.name === "Is admin" || record.name === "Is global admin") {
options = [
{id: "Admin", name: "Admin"},
{id: "Immutable", name: "Immutable"},

View File

@ -260,6 +260,13 @@ class AdapterListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -17,7 +17,7 @@ import "./App.less";
import {Helmet} from "react-helmet";
import * as Setting from "./Setting";
import {BarsOutlined, DownOutlined, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
import {Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result} from "antd";
import {Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result, theme} from "antd";
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
import OrganizationListPage from "./OrganizationListPage";
import OrganizationEditPage from "./OrganizationEditPage";
@ -69,6 +69,8 @@ import SystemInfo from "./SystemInfo";
import AdapterListPage from "./AdapterListPage";
import AdapterEditPage from "./AdapterEditPage";
import {withTranslation} from "react-i18next";
import SelectThemeBox from "./SelectThemeBox";
import SessionListPage from "./SessionListPage";
const {Header, Footer, Content} = Layout;
@ -81,6 +83,8 @@ class App extends Component {
account: undefined,
uri: null,
menuVisible: false,
themeAlgorithm: null,
logo: null,
};
Setting.initServerUrl();
@ -95,6 +99,16 @@ class App extends Component {
this.getAccount();
}
componentDidMount() {
localStorage.getItem("theme") ?
this.setState({"themeAlgorithm": this.getTheme()}) : this.setState({"themeAlgorithm": theme.defaultAlgorithm});
this.setState({"logo": Setting.getLogo(localStorage.getItem("theme"))});
addEventListener("themeChange", (e) => {
this.setState({"themeAlgorithm": this.getTheme()});
this.setState({"logo": Setting.getLogo(localStorage.getItem("theme"))});
});
}
componentDidUpdate() {
// eslint-disable-next-line no-restricted-globals
const uri = location.pathname;
@ -184,6 +198,10 @@ class App extends Component {
return "";
}
getTheme() {
return Setting.Themes.find(t => t.key === localStorage.getItem("theme"))["style"];
}
setLanguage(account) {
const language = account?.language;
if (language !== "" && language !== i18next.language) {
@ -404,6 +422,10 @@ class App extends Component {
"/tokens"
));
res.push(Setting.getItem(<Link to="/sessions">{i18next.t("general:Sessions")}</Link>,
"/sessions"
));
res.push(Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>,
"/webhooks"
));
@ -465,54 +487,63 @@ class App extends Component {
renderRouter() {
return (
<div>
<Switch>
<Route exact path="/result" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...props} />)} />
<Route exact path="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...props} />)} />
<Route exact path="/" render={(props) => this.renderLoginIfNotLoggedIn(<HomePage account={this.state.account} {...props} />)} />
<Route exact path="/account" render={(props) => this.renderLoginIfNotLoggedIn(<AccountPage account={this.state.account} {...props} />)} />
<Route exact path="/organizations" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationListPage account={this.state.account} {...props} />)} />
<Route exact path="/organizations/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationEditPage account={this.state.account} {...props} />)} />
<Route exact path="/organizations/:organizationName/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
<Route exact path="/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
<Route exact path="/users/:organizationName/:userName" render={(props) => <UserEditPage account={this.state.account} {...props} />} />
<Route exact path="/roles" render={(props) => this.renderLoginIfNotLoggedIn(<RoleListPage account={this.state.account} {...props} />)} />
<Route exact path="/roles/:organizationName/:roleName" render={(props) => this.renderLoginIfNotLoggedIn(<RoleEditPage account={this.state.account} {...props} />)} />
<Route exact path="/permissions" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionListPage account={this.state.account} {...props} />)} />
<Route exact path="/permissions/:organizationName/:permissionName" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionEditPage account={this.state.account} {...props} />)} />
<Route exact path="/models" render={(props) => this.renderLoginIfNotLoggedIn(<ModelListPage account={this.state.account} {...props} />)} />
<Route exact path="/models/:organizationName/:modelName" render={(props) => this.renderLoginIfNotLoggedIn(<ModelEditPage account={this.state.account} {...props} />)} />
<Route exact path="/adapters" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterListPage account={this.state.account} {...props} />)} />
<Route exact path="/adapters/:organizationName/:adapterName" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterEditPage account={this.state.account} {...props} />)} />
<Route exact path="/providers" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderListPage account={this.state.account} {...props} />)} />
<Route exact path="/providers/:organizationName/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
<Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} />
<Route exact path="/applications/:organizationName/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationEditPage account={this.state.account} {...props} />)} />
<Route exact path="/resources" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceListPage account={this.state.account} {...props} />)} />
{/* <Route exact path="/resources/:resourceName" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceEditPage account={this.state.account} {...props} />)}/>*/}
<Route exact path="/ldap/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapEditPage account={this.state.account} {...props} />)} />
<Route exact path="/ldap/sync/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapSyncPage account={this.state.account} {...props} />)} />
<Route exact path="/tokens" render={(props) => this.renderLoginIfNotLoggedIn(<TokenListPage account={this.state.account} {...props} />)} />
<Route exact path="/tokens/:tokenName" render={(props) => this.renderLoginIfNotLoggedIn(<TokenEditPage account={this.state.account} {...props} />)} />
<Route exact path="/webhooks" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookListPage account={this.state.account} {...props} />)} />
<Route exact path="/webhooks/:webhookName" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookEditPage account={this.state.account} {...props} />)} />
<Route exact path="/syncers" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerListPage account={this.state.account} {...props} />)} />
<Route exact path="/syncers/:syncerName" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerEditPage account={this.state.account} {...props} />)} />
<Route exact path="/certs" render={(props) => this.renderLoginIfNotLoggedIn(<CertListPage account={this.state.account} {...props} />)} />
<Route exact path="/certs/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} />
<Route exact path="/products/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)} />
<Route exact path="/products/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage account={this.state.account} {...props} />)} />
<Route exact path="/payments" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentListPage account={this.state.account} {...props} />)} />
<Route exact path="/payments/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} />
<Route exact path="/payments/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
<Route exact path="/records" render={(props) => this.renderLoginIfNotLoggedIn(<RecordListPage account={this.state.account} {...props} />)} />
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
<Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} />
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
</Switch>
</div>
<ConfigProvider theme={{
token: {
colorPrimary: "rgb(89,54,213)",
colorInfo: "rgb(89,54,213)",
},
algorithm: this.state.themeAlgorithm,
}}>
<div>
<Switch>
<Route exact path="/result" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...props} />)} />
<Route exact path="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...props} />)} />
<Route exact path="/" render={(props) => this.renderLoginIfNotLoggedIn(<HomePage account={this.state.account} {...props} />)} />
<Route exact path="/account" render={(props) => this.renderLoginIfNotLoggedIn(<AccountPage account={this.state.account} {...props} />)} />
<Route exact path="/organizations" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationListPage account={this.state.account} {...props} />)} />
<Route exact path="/organizations/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationEditPage account={this.state.account} {...props} />)} />
<Route exact path="/organizations/:organizationName/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
<Route exact path="/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
<Route exact path="/users/:organizationName/:userName" render={(props) => <UserEditPage account={this.state.account} {...props} />} />
<Route exact path="/roles" render={(props) => this.renderLoginIfNotLoggedIn(<RoleListPage account={this.state.account} {...props} />)} />
<Route exact path="/roles/:organizationName/:roleName" render={(props) => this.renderLoginIfNotLoggedIn(<RoleEditPage account={this.state.account} {...props} />)} />
<Route exact path="/permissions" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionListPage account={this.state.account} {...props} />)} />
<Route exact path="/permissions/:organizationName/:permissionName" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionEditPage account={this.state.account} {...props} />)} />
<Route exact path="/models" render={(props) => this.renderLoginIfNotLoggedIn(<ModelListPage account={this.state.account} {...props} />)} />
<Route exact path="/models/:organizationName/:modelName" render={(props) => this.renderLoginIfNotLoggedIn(<ModelEditPage account={this.state.account} {...props} />)} />
<Route exact path="/adapters" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterListPage account={this.state.account} {...props} />)} />
<Route exact path="/adapters/:organizationName/:adapterName" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterEditPage account={this.state.account} {...props} />)} />
<Route exact path="/providers" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderListPage account={this.state.account} {...props} />)} />
<Route exact path="/providers/:organizationName/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
<Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} />
<Route exact path="/applications/:organizationName/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationEditPage account={this.state.account} {...props} />)} />
<Route exact path="/resources" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceListPage account={this.state.account} {...props} />)} />
{/* <Route exact path="/resources/:resourceName" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceEditPage account={this.state.account} {...props} />)}/>*/}
<Route exact path="/ldap/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapEditPage account={this.state.account} {...props} />)} />
<Route exact path="/ldap/sync/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapSyncPage account={this.state.account} {...props} />)} />
<Route exact path="/tokens" render={(props) => this.renderLoginIfNotLoggedIn(<TokenListPage account={this.state.account} {...props} />)} />
<Route exact path="/sessions" render={(props) => this.renderLoginIfNotLoggedIn(<SessionListPage account={this.state.account} {...props} />)} />
<Route exact path="/tokens/:tokenName" render={(props) => this.renderLoginIfNotLoggedIn(<TokenEditPage account={this.state.account} {...props} />)} />
<Route exact path="/webhooks" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookListPage account={this.state.account} {...props} />)} />
<Route exact path="/webhooks/:webhookName" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookEditPage account={this.state.account} {...props} />)} />
<Route exact path="/syncers" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerListPage account={this.state.account} {...props} />)} />
<Route exact path="/syncers/:syncerName" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerEditPage account={this.state.account} {...props} />)} />
<Route exact path="/certs" render={(props) => this.renderLoginIfNotLoggedIn(<CertListPage account={this.state.account} {...props} />)} />
<Route exact path="/certs/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} />
<Route exact path="/products/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)} />
<Route exact path="/products/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage account={this.state.account} {...props} />)} />
<Route exact path="/payments" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentListPage account={this.state.account} {...props} />)} />
<Route exact path="/payments/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} />
<Route exact path="/payments/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
<Route exact path="/records" render={(props) => this.renderLoginIfNotLoggedIn(<RecordListPage account={this.state.account} {...props} />)} />
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
<Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} />
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
</Switch>
</div>
</ConfigProvider>
);
}
@ -532,30 +563,27 @@ class App extends Component {
if (!Setting.isMobile()) {
return (
<Layout id="parent-area">
<Header style={{marginBottom: "3px", paddingInline: 0}}>
<Header style={{marginBottom: "3px", paddingInline: 0, backgroundColor: this.state.themeAlgorithm === theme.darkAlgorithm ? "black" : "white"}}>
{
Setting.isMobile() ? null : (
<Link to={"/"}>
<div className="logo" />
<div className="logo" style={{background: `url(${this.state.logo})`}} />
</Link>
)
}
<div>
<Menu
// theme="dark"
items={this.renderMenu()}
mode={(Setting.isMobile() && this.isStartPages()) ? "inline" : "horizontal"}
selectedKeys={[`${this.state.selectedMenuKey}`]}
style={{lineHeight: "64px", position: "absolute", left: "145px", right: "200px"}}
>
</Menu>
{
this.renderAccount()
}
{this.state.account && <SelectLanguageBox languages={this.state.account.organization.languages} />}
</div>
<Menu
items={this.renderMenu()}
mode={(Setting.isMobile() && this.isStartPages()) ? "inline" : "horizontal"}
selectedKeys={[`${this.state.selectedMenuKey}`]}
style={{position: "absolute", left: "145px", backgroundColor: this.state.themeAlgorithm === theme.darkAlgorithm ? "black" : "white"}}
/>
{
this.renderAccount()
}
{this.state.account && <SelectThemeBox themes={this.state.account.organization.themes} />}
{this.state.account && <SelectLanguageBox languages={this.state.account.organization.languages} />}
</Header>
<Content style={{backgroundColor: "#f5f5f5", alignItems: "stretch", display: "flex", flexDirection: "column"}}>
<Content style={{alignItems: "stretch", display: "flex", flexDirection: "column"}}>
<Card className="content-warp-card">
{
this.renderRouter()
@ -570,11 +598,11 @@ class App extends Component {
} else {
return (
<Layout>
<Header style={{padding: "0", marginBottom: "3px"}}>
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: this.state.themeAlgorithm === theme.darkAlgorithm ? "black" : "white"}}>
{
Setting.isMobile() ? null : (
<Link to={"/"}>
<div className="logo" />
<div className="logo" style={{background: `url(${this.state.logo})`}} />
</Link>
)
}
@ -592,12 +620,11 @@ class App extends Component {
<Button icon={<BarsOutlined />} onClick={this.showMenu} type="text">
{i18next.t("general:Menu")}
</Button>
<div style = {{float: "right"}}>
{
this.renderAccount()
}
{this.state.account && <SelectLanguageBox languages={this.state.account.organization.languages} />}
</div>
{
this.renderAccount()
}
{this.state.account && <SelectThemeBox themes={this.state.account.organization.themes} />}
{this.state.account && <SelectLanguageBox languages={this.state.account.organization.languages} />}
</Header>
<Content style={{display: "flex", flexDirection: "column"}} >{
this.renderRouter()}
@ -617,12 +644,10 @@ class App extends Component {
{!this.state.account ? null : <div style={{display: "none"}} id="CasdoorApplicationName" value={this.state.account.signupApplication} />}
<Footer id="footer" style={
{
borderTop: "1px solid #e8e8e8",
backgroundColor: "white",
textAlign: "center",
}
}>
Powered by <a target="_blank" href="https://casdoor.org" rel="noreferrer"><img style={{paddingBottom: "3px"}} height={"20px"} alt={"Casdoor"} src={`${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`} /></a>
Powered by <a target="_blank" href="https://casdoor.org" rel="noreferrer"><img style={{paddingBottom: "3px"}} height={"20px"} alt={"Casdoor"} src={this.state.logo} /></a>
</Footer>
</React.Fragment>
);
@ -690,6 +715,7 @@ class App extends Component {
colorPrimary: "rgb(89,54,213)",
colorInfo: "rgb(89,54,213)",
},
algorithm: this.state.themeAlgorithm,
}}>
{
this.renderPage()
@ -711,6 +737,7 @@ class App extends Component {
colorPrimary: "rgb(89,54,213)",
colorInfo: "rgb(89,54,213)",
},
algorithm: this.state.themeAlgorithm,
}}>
{
this.renderPage()

View File

@ -13,14 +13,12 @@
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
@ -41,7 +39,6 @@ img {
flex-direction: column;
height: 100%;
min-height: 100vh;
background-color: #f5f5f5;
}
.panel-logo {
@ -55,7 +52,7 @@ img {
background-repeat: no-repeat;
border-radius: 5px;
width: 45px;
height: 65px;
height: 100%;
float: right;
cursor: pointer;
@ -64,9 +61,32 @@ img {
}
}
.login-form .language-box {
height: 65px;
}
.theme-box {
background: url("@{StaticBaseUrl}/img/muti_language.svg");
background-size: 25px, 25px;
background-position: center !important;
background-repeat: no-repeat !important;
border-radius: 5px;
width: 45px;
height: 100%;
float: right;
cursor: pointer;
&:hover {
background-color: #f5f5f5 !important;
}
}
.rightDropDown {
border-radius: 7px;
&:hover {
background-color: #f5f5f5;
color: black;
}
}
@ -128,3 +148,9 @@ img {
background-size: 100% 100%;
background-attachment: fixed;
}
.ant-menu-horizontal {
border-bottom: none !important;
margin-right: 30px;
right: 230px;
}

View File

@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {Button, Card, Col, Input, Popover, Radio, Row, Select, Switch, Upload} from "antd";
import {Button, Card, Col, Input, Popover, Radio, Result, Row, Select, Switch, Upload} from "antd";
import {CopyOutlined, LinkOutlined, UploadOutlined} from "@ant-design/icons";
import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as CertBackend from "./backend/CertBackend";
@ -103,6 +103,7 @@ class ApplicationEditPage extends React.Component {
uploading: false,
mode: props.location.mode !== undefined ? props.location.mode : "edit",
samlMetadata: null,
isAuthorized: true,
};
}
@ -129,9 +130,15 @@ class ApplicationEditPage extends React.Component {
getOrganizations() {
OrganizationBackend.getOrganizations("admin")
.then((res) => {
this.setState({
organizations: (res.msg === undefined) ? res : [],
});
if (res?.status === "error") {
this.setState({
isAuthorized: false,
});
} else {
this.setState({
organizations: (res.msg === undefined) ? res : [],
});
}
});
}
@ -726,7 +733,7 @@ class ApplicationEditPage extends React.Component {
renderSignupSigninPreview() {
let signUpUrl = `/signup/${this.state.application.name}`;
const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${this.state.application.redirectUris[0]}&scope=read&state=casdoor`;
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "100%", width: "100%", background: "rgba(0,0,0,0.4)"};
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"};
if (!this.state.application.enablePassword) {
signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize");
}
@ -742,15 +749,19 @@ class ApplicationEditPage extends React.Component {
{i18next.t("application:Copy signup page URL")}
</Button>
<br />
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems: "center", overflow: "auto", flexDirection: "column", flex: "auto"}}>
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", overflow: "auto"}}>
{
this.state.application.enablePassword ? (
<SignupPage application={this.state.application} />
<div className="loginBackground" style={{backgroundImage: `url(${this.state.application?.formBackgroundUrl})`, overflow: "auto"}}>
<SignupPage application={this.state.application} preview = "auto" />
</div>
) : (
<LoginPage type={"login"} mode={"signup"} application={this.state.application} />
<div className="loginBackground" style={{backgroundImage: `url(${this.state.application?.formBackgroundUrl})`, overflow: "auto"}}>
<LoginPage type={"login"} mode={"signup"} application={this.state.application} preview = "auto" />
</div>
)
}
<div style={maskStyle} />
<div style={{overflow: "auto", ...maskStyle}} />
</div>
</Col>
<Col span={previewGrid}>
@ -762,9 +773,11 @@ class ApplicationEditPage extends React.Component {
{i18next.t("application:Copy signin page URL")}
</Button>
<br />
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", alignItems: "center", overflow: "auto", flexDirection: "column", flex: "auto"}}>
<LoginPage type={"login"} mode={"signin"} application={this.state.application} />
<div style={maskStyle} />
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", overflow: "auto"}}>
<div className="loginBackground" style={{backgroundImage: `url(${this.state.application?.formBackgroundUrl})`, overflow: "auto"}}>
<LoginPage type={"login"} mode={"signin"} application={this.state.application} preview = "auto" />
</div>
<div style={{overflow: "auto", ...maskStyle}} />
</div>
</Col>
</React.Fragment>
@ -832,6 +845,17 @@ class ApplicationEditPage extends React.Component {
}
render() {
if (!this.state.isAuthorized) {
return (
<Result
status="403"
title="403 Unauthorized"
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
/>
);
}
return (
<div>
{

View File

@ -14,7 +14,7 @@
import React from "react";
import {Link} from "react-router-dom";
import {Button, Col, List, Popconfirm, Row, Table, Tooltip} from "antd";
import {Button, Col, List, Popconfirm, Result, Row, Table, Tooltip} from "antd";
import {EditOutlined} from "@ant-design/icons";
import moment from "moment";
import * as Setting from "./Setting";
@ -36,6 +36,7 @@ class ApplicationListPage extends BaseListPage {
loading: false,
searchText: "",
searchedColumn: "",
isAuthorized: true,
};
}
@ -258,6 +259,17 @@ class ApplicationListPage extends BaseListPage {
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
};
if (!this.state.isAuthorized) {
return (
<Result
status="403"
title="403 Unauthorized"
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
/>
);
}
return (
<div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={applications} rowKey="name" size="middle" bordered pagination={paginationProps}
@ -292,6 +304,13 @@ class ApplicationListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -13,9 +13,10 @@
// limitations under the License.
import React from "react";
import {Button, Input, Space} from "antd";
import {Button, Input, Result, Space} from "antd";
import {SearchOutlined} from "@ant-design/icons";
import Highlighter from "react-highlight-words";
import i18next from "i18next";
class BaseListPage extends React.Component {
constructor(props) {
@ -127,6 +128,17 @@ class BaseListPage extends React.Component {
};
render() {
if (!this.state.isAuthorized) {
return (
<Result
status="403"
title="403 Unauthorized"
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
/>
);
}
return (
<div>
{

View File

@ -227,6 +227,13 @@ class CertListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -200,6 +200,13 @@ class ModelListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -251,7 +251,7 @@ class OrganizationListPage extends BaseListPage {
<Result
status="403"
title="403 Unauthorized"
subTitle={i18next.t("general:Sorry, you do not have permission to access this page.")}
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
/>
);

View File

@ -278,6 +278,13 @@ class PaymentListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -358,6 +358,13 @@ class PermissionListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -295,6 +295,13 @@ class ProductListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -14,7 +14,7 @@
import React from "react";
import {Link} from "react-router-dom";
import {Button, Popconfirm, Table} from "antd";
import {Button, Popconfirm, Result, Table} from "antd";
import moment from "moment";
import * as Setting from "./Setting";
import * as ProviderBackend from "./backend/ProviderBackend";
@ -36,6 +36,7 @@ class ProviderListPage extends BaseListPage {
loading: false,
searchText: "",
searchedColumn: "",
isAuthorized: true,
};
}
newProvider() {
@ -227,6 +228,17 @@ class ProviderListPage extends BaseListPage {
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
};
if (!this.state.isAuthorized) {
return (
<Result
status="403"
title="403 Unauthorized"
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
/>
);
}
return (
<div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={providers} rowKey="name" size="middle" bordered pagination={paginationProps}
@ -268,6 +280,13 @@ class ProviderListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -222,6 +222,13 @@ class RecordListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.data.includes("Please login first")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {Button, Popconfirm, Table, Upload} from "antd";
import {Button, Popconfirm, Result, Table, Upload} from "antd";
import {UploadOutlined} from "@ant-design/icons";
import copy from "copy-to-clipboard";
import * as Setting from "./Setting";
@ -37,6 +37,7 @@ class ResourceListPage extends BaseListPage {
searchedColumn: "",
fileList: [],
uploading: false,
isAuthorized: true,
};
}
@ -272,6 +273,17 @@ class ResourceListPage extends BaseListPage {
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
};
if (!this.state.isAuthorized) {
return (
<Result
status="403"
title="403 Unauthorized"
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
/>
);
}
return (
<div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={resources} rowKey="name" size="middle" bordered pagination={paginationProps}
@ -308,6 +320,13 @@ class ResourceListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.data.includes("Please login first")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -231,6 +231,13 @@ class RoleListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

81
web/src/SelectThemeBox.js Normal file
View File

@ -0,0 +1,81 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from "react";
import * as Setting from "./Setting";
import {Dropdown} from "antd";
import "./App.less";
import i18next from "i18next";
function themeIcon(themeKey) {
return <img width={24} alt={themeKey} src={getLogoURL(themeKey)} />;
}
function getLogoURL(themeKey) {
if (themeKey) {
return Setting.Themes.find(t => t.key === themeKey)["selectThemeLogo"];
} else {
return Setting.Themes.find(t => t.key === localStorage.getItem("theme"))["selectThemeLogo"];
}
}
class SelectThemeBox extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
themes: props.theme ?? ["Default", "Dark", "Compact"],
icon: null,
};
}
items = this.getThemes();
componentDidMount() {
i18next.on("languageChanged", () => {
this.items = this.getThemes();
});
localStorage.getItem("theme") ? this.setState({"icon": getLogoURL()}) : this.setState({"icon": getLogoURL("Default")});
addEventListener("themeChange", (e) => {
this.setState({"icon": getLogoURL()});
});
}
getThemes() {
return Setting.Themes.map((theme) => Setting.getItem(i18next.t(`general:${theme.label}`), theme.key, themeIcon(theme.key)));
}
getOrganizationThemes(themes) {
const select = [];
for (const theme of themes) {
this.items.map((item, index) => item.key === theme ? select.push(item) : null);
}
return select;
}
render() {
const themeItems = this.getOrganizationThemes(this.state.themes);
const onClick = (e) => {
Setting.setTheme(e.key);
};
return (
<Dropdown menu={{items: themeItems, onClick}} >
<div className="theme-box" style={{display: themeItems.length === 0 ? "none" : null, background: `url(${this.state.icon})`, ...this.props.style}} />
</Dropdown>
);
}
}
export default SelectThemeBox;

162
web/src/SessionListPage.js Normal file
View File

@ -0,0 +1,162 @@
// 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.
import BaseListPage from "./BaseListPage";
import * as Setting from "./Setting";
import i18next from "i18next";
import {Link} from "react-router-dom";
import {Button, Popconfirm, Table, Tag} from "antd";
import React from "react";
import * as SessionBackend from "./backend/SessionBackend";
class SessionListPage extends BaseListPage {
deleteSession(i) {
SessionBackend.deleteSession(this.state.data[i])
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
renderTable(sessions) {
const columns = [
{
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "150px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("name"),
},
{
title: i18next.t("general:Organization"),
dataIndex: "owner",
key: "organization",
width: "110px",
sorter: true,
...this.getColumnSearchProps("organization"),
render: (text, record, index) => {
return (
<Link to={`/organizations/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Created time"),
dataIndex: "createdTime",
key: "createdTime",
width: "180px",
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
},
},
{
title: i18next.t("general:Session ID"),
dataIndex: "sessionId",
key: "sessionId",
width: "180px",
sorter: true,
render: (text, record, index) => {
return text.map((item, index) =>
<Tag key={index}>{item}</Tag>
);
},
},
{
title: i18next.t("general:Action"),
dataIndex: "",
key: "op",
width: "70px",
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
return (
<div>
<Popconfirm
title={`Sure to delete session: ${record.name} ?`}
onConfirm={() => this.deleteSession(index)}
>
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
</Popconfirm>
</div>
);
},
},
];
const paginationProps = {
total: this.state.pagination.total,
showQuickJumper: true,
showSizeChanger: true,
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
};
return (
<div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={sessions} rowKey="name" size="middle" bordered pagination={paginationProps}
loading={this.state.loading}
onChange={this.handleTableChange}
/>
</div>
);
}
fetch = (params = {}) => {
let field = params.searchedColumn, value = params.searchText;
const sortField = params.sortField, sortOrder = params.sortOrder;
if (params.contentType !== undefined && params.contentType !== null) {
field = "contentType";
value = params.contentType;
}
this.setState({loading: true});
SessionBackend.getSessions("", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
.then((res) => {
if (res.status === "ok") {
this.setState({
loading: false,
data: res.data,
pagination: {
...params.pagination,
total: res.data2,
},
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};
}
export default SessionListPage;

View File

@ -14,7 +14,7 @@
import React from "react";
import {Link} from "react-router-dom";
import {Tag, Tooltip, message} from "antd";
import {Tag, Tooltip, message, theme} from "antd";
import {QuestionCircleTwoTone} from "@ant-design/icons";
import {isMobile as isMobileDevice} from "react-device-detect";
import "./i18n";
@ -43,6 +43,13 @@ export const Countries = [{label: "English", key: "en", country: "US", alt: "Eng
{label: "Русский", key: "ru", country: "RU", alt: "Русский"},
];
const {defaultAlgorithm, darkAlgorithm, compactAlgorithm} = theme;
export const Themes = [{label: i18next.t("general:Dark"), key: "Dark", style: darkAlgorithm, selectThemeLogo: `${StaticBaseUrl}/img/dark.svg`},
{label: i18next.t("general:Compact"), key: "Compact", style: compactAlgorithm, selectThemeLogo: `${StaticBaseUrl}/img/compact.svg`},
{label: i18next.t("general:Default"), key: "Default", style: defaultAlgorithm, selectThemeLogo: `${StaticBaseUrl}/img/light.svg`},
];
export const OtherProviderInfo = {
SMS: {
"Aliyun SMS": {
@ -562,6 +569,14 @@ export function getAvatarColor(s) {
return colorList[hash % 4];
}
export function getLogo(theme) {
if (theme === "Dark") {
return `${StaticBaseUrl}/img/casdoor-logo_1185x256_dark.png`;
} else {
return `${StaticBaseUrl}/img/casdoor-logo_1185x256.png`;
}
}
export function getLanguageText(text) {
if (!text.includes("|")) {
return text;
@ -587,6 +602,11 @@ export function setLanguage(language) {
i18next.changeLanguage(language);
}
export function setTheme(themeKey) {
localStorage.setItem("theme", themeKey);
dispatchEvent(new Event("themeChange"));
}
export function getAcceptLanguage() {
if (i18next.language === null || i18next.language === "") {
return "en;q=0.9,en;q=0.8";
@ -800,7 +820,7 @@ export function renderLoginLink(application, text) {
export function redirectToLoginPage(application, history) {
const loginLink = getLoginLink(application);
if (loginLink.indexOf("http") === 0 || loginLink.indexOf("https") === 0) {
openLink(loginLink);
window.location.replace(loginLink);
}
history.push(loginLink);
}

View File

@ -288,6 +288,13 @@ class SyncerListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -253,6 +253,13 @@ class TokenListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -28,11 +28,7 @@ import SamlWidget from "./common/SamlWidget";
import SelectRegionBox from "./SelectRegionBox";
import WebAuthnCredentialTable from "./WebauthnCredentialTable";
import ManagedAccountTable from "./ManagedAccountTable";
import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css";
require("codemirror/theme/material-darker.css");
require("codemirror/mode/javascript/javascript");
import PropertyTable from "./propertyTable";
const {Option} = Select;
@ -490,13 +486,10 @@ class UserEditPage extends React.Component {
return (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{i18next.t("user:Properties")}:
{Setting.getLabel(i18next.t("user:Properties"), i18next.t("user:Properties - Tooltip"))} :
</Col>
<Col span={22} >
<CodeMirror
value={JSON.stringify(this.state.user.properties, null, 4)}
options={{mode: "javascript", theme: "material-darker"}}
/>
<PropertyTable properties={this.state.user.properties} onUpdateTable={(value) => {this.updateUserField("properties", value);}} />
</Col>
</Row>
);
@ -507,7 +500,7 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("user:Is admin"), i18next.t("user:Is admin - Tooltip"))} :
</Col>
<Col span={(Setting.isMobile()) ? 22 : 2} >
<Switch disabled={this.state.user.owner === "built-in"} checked={this.state.user.isAdmin} onChange={checked => {
<Switch disabled={disabled} checked={this.state.user.isAdmin} onChange={checked => {
this.updateUserField("isAdmin", checked);
}} />
</Col>
@ -520,7 +513,7 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("user:Is global admin"), i18next.t("user:Is global admin - Tooltip"))} :
</Col>
<Col span={(Setting.isMobile()) ? 22 : 2} >
<Switch disabled={this.state.user.owner === "built-in"} checked={this.state.user.isGlobalAdmin} onChange={checked => {
<Switch disabled={disabled} checked={this.state.user.isGlobalAdmin} onChange={checked => {
this.updateUserField("isGlobalAdmin", checked);
}} />
</Col>

View File

@ -14,7 +14,7 @@
import React from "react";
import {Link} from "react-router-dom";
import {Button, Popconfirm, Switch, Table, Upload} from "antd";
import {Button, Popconfirm, Result, Switch, Table, Upload} from "antd";
import {UploadOutlined} from "@ant-design/icons";
import moment from "moment";
import * as OrganizationBackend from "./backend/OrganizationBackend";
@ -38,6 +38,7 @@ class UserListPage extends BaseListPage {
loading: false,
searchText: "",
searchedColumn: "",
isAuthorized: true,
};
}
@ -369,6 +370,17 @@ class UserListPage extends BaseListPage {
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
};
if (!this.state.isAuthorized) {
return (
<Result
status="403"
title="403 Unauthorized"
subTitle={i18next.t("general:Sorry, you do not have permission to access this page or logged in status invalid.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
/>
);
}
return (
<div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={users} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
@ -411,6 +423,13 @@ class UserListPage extends BaseListPage {
if (users.length > 0) {
this.getOrganization(users[0].owner);
}
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
} else {
@ -432,6 +451,13 @@ class UserListPage extends BaseListPage {
if (users.length > 0) {
this.getOrganization(users[0].owner);
}
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
}

View File

@ -253,6 +253,13 @@ class WebhookListPage extends BaseListPage {
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};

View File

@ -20,6 +20,7 @@ import * as Util from "./Util";
import {authConfig} from "./Auth";
import * as Setting from "../Setting";
import i18next from "i18next";
import RedirectForm from "../common/RedirectForm";
class AuthCallback extends React.Component {
constructor(props) {
@ -27,6 +28,9 @@ class AuthCallback extends React.Component {
this.state = {
classes: props,
msg: null,
samlResponse: "",
relayState: "",
redirectUrl: "",
};
}
@ -164,9 +168,17 @@ class AuthCallback extends React.Component {
const from = innerParams.get("from");
Setting.goToLinkSoft(this, from);
} else if (responseType === "saml") {
const SAMLResponse = res.data;
const redirectUri = res.data2;
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);
if (res.data2.method === "POST") {
this.setState({
samlResponse: res.data,
redirectUrl: res.data2.redirectUrl,
relayState: oAuthParams.relayState,
});
} else {
const SAMLResponse = res.data;
const redirectUri = res.data2.redirectUrl;
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);
}
}
} else {
this.setState({
@ -177,6 +189,10 @@ class AuthCallback extends React.Component {
}
render() {
if (this.state.samlResponse !== "") {
return <RedirectForm samlResponse={this.state.samlResponse} redirectUrl={this.state.redirectUrl} relayState={this.state.relayState} />;
}
return (
<div style={{display: "flex", justifyContent: "center", alignItems: "center"}}>
{

View File

@ -40,6 +40,7 @@ class CasLogout extends React.Component {
if (res.status === "ok") {
Setting.showMessage("success", "Logged out successfully");
this.props.onUpdateAccount(null);
this.onUpdateApplication(null);
const redirectUri = res.data2;
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
Setting.goToLink(redirectUri);
@ -49,6 +50,7 @@ class CasLogout extends React.Component {
Setting.goToLinkSoft(this, `/cas/${this.state.owner}/${this.state.applicationName}/login`);
}
} else {
this.onUpdateApplication(null);
Setting.showMessage("error", `Failed to log out: ${res.msg}`);
}
});

View File

@ -102,6 +102,7 @@ class LoginPage extends React.Component {
});
} else {
// Setting.showMessage("error", res.msg);
this.onUpdateApplication(null);
this.setState({
application: res.data,
msg: res.msg,
@ -133,6 +134,7 @@ class LoginPage extends React.Component {
applicationName: res.data.name,
});
} else {
this.onUpdateApplication(null);
Setting.showMessage("error", res.msg);
}
});
@ -318,15 +320,15 @@ class LoginPage extends React.Component {
const accessToken = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
} else if (responseType === "saml") {
const SAMLResponse = res.data;
const redirectUri = res.data2;
if (this.state.application.assertionConsumerUrl !== "") {
if (res.data2.method === "POST") {
this.setState({
samlResponse: res.data,
redirectUrl: res.data2,
redirectUrl: res.data2.redirectUrl,
relayState: oAuthParams.relayState,
});
} else {
const SAMLResponse = res.data;
const redirectUri = res.data2.redirectUrl;
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);
}
}
@ -614,13 +616,13 @@ class LoginPage extends React.Component {
this.sendSilentSigninData("signing-in");
const values = {};
values["application"] = this.state.application.name;
values["application"] = application.name;
this.onFinish(values);
}
if (application.enableAutoSignin) {
const values = {};
values["application"] = this.state.application.name;
values["application"] = application.name;
this.onFinish(values);
}
@ -635,7 +637,7 @@ class LoginPage extends React.Component {
<br />
<SelfLoginButton account={this.props.account} onClick={() => {
const values = {};
values["application"] = this.state.application.name;
values["application"] = application.name;
this.onFinish(values);
}} />
<br />
@ -800,7 +802,7 @@ class LoginPage extends React.Component {
return (
<React.Fragment>
<CustomGithubCorner />
<div className="login-content" style={{margin: this.parseOffset(application.formOffset)}}>
<div className="login-content" style={{margin: this.props.preview ?? this.parseOffset(application.formOffset)}}>
{Setting.inIframe() || Setting.isMobile() ? null : <div dangerouslySetInnerHTML={{__html: application.formCss}} />}
<div className="login-panel">
<div className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}>

View File

@ -637,7 +637,7 @@ class SignupPage extends React.Component {
return (
<React.Fragment>
<CustomGithubCorner />
<div className="login-content" style={{margin: this.parseOffset(application.formOffset)}}>
<div className="login-content" style={{margin: this.props.preview ?? this.parseOffset(application.formOffset)}}>
{Setting.inIframe() || Setting.isMobile() ? null : <div dangerouslySetInnerHTML={{__html: application.formCss}} />}
<div className="login-panel" >
<div className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}>

View File

@ -0,0 +1,36 @@
// 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.
import * as Setting from "../Setting";
export function getSessions(owner, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") {
return fetch(`${Setting.ServerUrl}/api/get-sessions?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function deleteSession(session) {
return fetch(`${Setting.ServerUrl}/api/delete-session`, {
method: "POST",
credentials: "include",
body: JSON.stringify(session),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}

View File

@ -28,7 +28,7 @@ code {
.logo {
background: url("https://cdn.casbin.org/img/casdoor-logo_1185x256.png");
background-size: 130px, 27px;
background-size: 130px, 27px !important;
width: 130px;
height: 27px;
margin: 17px 0 16px 15px;
@ -45,7 +45,3 @@ code {
.ant-list-sm .ant-list-item {
padding: 2px !important;
}
.ant-layout-header {
background-color: white !important;
}

View File

@ -156,7 +156,10 @@
"Click to Upload": "Click to Upload",
"Client IP": "Client-IP",
"Close": "Close",
"Compact": "Compact",
"Created time": "Erstellte Zeit",
"Dark": "Dark",
"Default": "Default",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Standard Avatar",
@ -233,6 +236,8 @@
"Roles - Tooltip": "Roles - Tooltip",
"Save": "Speichern",
"Save & Exit": "Speichern & Beenden",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "Anmelde-URL",
"Signin URL - Tooltip": "sign in url",
"Signup URL": "Registrierungs-URL",
@ -241,7 +246,7 @@
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Die von Ihnen besuchte Seite existiert leider nicht.",
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
"Sorry, you do not have permission to access this page.": "Sorry, you do not have permission to access this page.",
"Sorry, you do not have permission to access this page or logged in status invalid.": "Sorry, you do not have permission to access this page or logged in status invalid.",
"State": "State",
"State - Tooltip": "State - Tooltip",
"Successfully added": "Successfully added",
@ -723,6 +728,7 @@
"Is forbidden - Tooltip": "Whether the account is disabled",
"Is global admin": "Ist globaler Admin",
"Is global admin - Tooltip": "Is the application global administrator",
"Keys": "Keys",
"Link": "Link",
"Location": "Standort",
"Location - Tooltip": "Standort - Tooltip",
@ -738,6 +744,7 @@
"Password Set": "Passwort setzen",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Eigenschaften",
"Properties - Tooltip": "Properties - Tooltip",
"Re-enter New": "Neu erneut eingeben",
"Reset Email...": "Reset Email...",
"Reset Phone...": "Telefon zurücksetzen...",
@ -753,6 +760,7 @@
"Unlink": "Link aufheben",
"Upload (.xlsx)": "Upload (.xlsx)",
"Upload a photo": "Foto hochladen",
"Values": "Values",
"WebAuthn credentials": "WebAuthn credentials",
"input password": "Passwort eingeben"
},

View File

@ -156,7 +156,10 @@
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Compact": "Compact",
"Created time": "Created time",
"Dark": "Dark",
"Default": "Default",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Default avatar",
@ -233,6 +236,8 @@
"Roles - Tooltip": "Roles - Tooltip",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "Signin URL",
"Signin URL - Tooltip": "Signin URL - Tooltip",
"Signup URL": "Signup URL",
@ -241,7 +246,7 @@
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist.",
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
"Sorry, you do not have permission to access this page.": "Sorry, you do not have permission to access this page.",
"Sorry, you do not have permission to access this page or logged in status invalid.": "Sorry, you do not have permission to access this page or logged in status invalid.",
"State": "State",
"State - Tooltip": "State - Tooltip",
"Successfully added": "Successfully added",
@ -723,6 +728,7 @@
"Is forbidden - Tooltip": "Is forbidden - Tooltip",
"Is global admin": "Is global admin",
"Is global admin - Tooltip": "Is global admin - Tooltip",
"Keys": "Keys",
"Link": "Link",
"Location": "Location",
"Location - Tooltip": "Location - Tooltip",
@ -738,6 +744,7 @@
"Password Set": "Password Set",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Properties",
"Properties - Tooltip": "Properties - Tooltip",
"Re-enter New": "Re-enter New",
"Reset Email...": "Reset Email...",
"Reset Phone...": "Reset Phone...",
@ -753,6 +760,7 @@
"Unlink": "Unlink",
"Upload (.xlsx)": "Upload (.xlsx)",
"Upload a photo": "Upload a photo",
"Values": "Values",
"WebAuthn credentials": "WebAuthn credentials",
"input password": "input password"
},

View File

@ -5,7 +5,23 @@
"My Account": "Mi cuenta",
"Sign Up": "Registrarme"
},
"adapter": {
"Edit Adapter": "Edit Adapter",
"Failed to sync policies": "Failed to sync policies",
"New Adapter": "New Adapter",
"Policies": "Policies",
"Policies - Tooltip": "Policies - Tooltip",
"Repeated policy rules": "Repeated policy rules",
"Sync": "Sync",
"Sync policies successfully": "Sync policies successfully"
},
"application": {
"Always": "Always",
"Auto signin": "Auto signin",
"Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL",
"Background URL - Tooltip": "Background URL - Tooltip",
"Center": "Center",
"Copy SAML metadata URL": "Copiar SAML metadata URL",
"Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copiar URL de inicio de sesión",
@ -17,39 +33,57 @@
"Enable WebAuthn signin - Tooltip": "Habilitar inicio de sesión de WebAuthn - Tooltip",
"Enable code signin": "Habilitar inicio de sesión por código",
"Enable code signin - Tooltip": "Habilitar inicio de sesión por código - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Habilitar nuevos registros",
"Enable signup - Tooltip": "Habilitar nuevos registros - Tooltip",
"Failed to sign in": "Failed to sign in",
"File uploaded successfully": "El archivo ha sido subido con éxito",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "Form CSS - Tooltip",
"Form position": "Form position",
"Form position - Tooltip": "Form position - Tooltip",
"Grant types": "Grant types",
"Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"New Application": "Nueva Aplicación",
"None": "None",
"Password ON": "Constraseña ON",
"Password ON - Tooltip": "Constraseña ON - Tooltip",
"Please input your application!": "Please input your application!",
"Please input your organization!": "Please input your organization!",
"Please select a HTML file": "Seleccione un archivo HTML",
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Redirect URL": "URL de redirección",
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip",
"Redirect URLs": "URLs de redirección",
"Redirect URLs - Tooltip": "URL de redirección - Tooltip",
"Refresh token expire": "Expiración del Refresh Token",
"Refresh token expire - Tooltip": "Expiración del Refresh Token - Tooltip",
"Right": "Right",
"Rule": "Rule",
"SAML Reply URL": "SAML Reply URL",
"SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copiado al portapapeles con éxito",
"Side panel HTML": "Side panel HTML",
"Side panel HTML - Edit": "Side panel HTML - Edit",
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
"Sign Up Error": "Sign Up Error",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Signin session": "Signin session",
"Sign Up Error": "Sign Up Error",
"Signup items": "Signup items",
"Signup items - Tooltip": "Signup items - Tooltip",
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Expiración del Token",
"Token expire - Tooltip": "Expiración del Token - Tooltip",
"Token format": "Formato del Token",
"Token format - Tooltip": "Formato del Token - Tooltip",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page",
"Rule": "Rule",
"None": "None",
"Always": "Always"
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {
"Bit size": "Tamaño del Bit",
@ -97,13 +131,14 @@
"Please input your username!": "Por favor ingrese su nombre de usuario!",
"Reset": "Reiniciar",
"Retrieve password": "Recuperar Contraseña",
"Unknown forget type": "Tipo de olvido desconocido",
"Unknown forget type: ": "Unknown forget type: ",
"Verify": "Verificar"
},
"general": {
"Action": "Acción",
"Adapter": "Adaptador",
"Adapter - Tooltip": "Adaptador - Tooltip",
"Adapters": "Adapters",
"Add": "Agregar",
"Affiliation URL": "URL de Afiliación",
"Affiliation URL - Tooltip": "URL de Afiliación - Tooltip",
@ -120,7 +155,13 @@
"Certs": "Certificados",
"Click to Upload": "Click para subir archivo",
"Client IP": "IP del Cliente",
"Close": "Close",
"Compact": "Compact",
"Created time": "Fecha de creación",
"Dark": "Dark",
"Default": "Default",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Avatar por defecto",
"Default avatar - Tooltip": "Avatar por defecto - Tooltip",
"Delete": "Eliminar",
@ -132,6 +173,10 @@
"Edit": "Editar",
"Email": "Email",
"Email - Tooltip": "Email - Tooltip",
"Failed to add": "Failed to add",
"Failed to connect to server": "Failed to connect to server",
"Failed to delete": "Failed to delete",
"Failed to save": "Failed to save",
"Favicon": "Favicon",
"Favicon - Tooltip": "Favicon - Tooltip",
"First name": "Nombre",
@ -145,11 +190,14 @@
"Is enabled - Tooltip": "Está habilitado - Tooltip",
"LDAPs": "LDAPs",
"LDAPs - Tooltip": "LDAPs - Tooltip",
"Languages": "Languages",
"Languages - Tooltip": "Languages - Tooltip",
"Last name": "Apellido",
"Logo": "Logo",
"Logo - Tooltip": "Logo - Tooltip",
"Master password": "Contraseña maestra",
"Master password - Tooltip": "Contraseña maestra - Tooltip",
"Menu": "Menu",
"Method": "Metodo",
"Model": "Modelo",
"Model - Tooltip": "Modelo - Tooltip",
@ -188,6 +236,8 @@
"Roles - Tooltip": "Roles - Tooltip",
"Save": "Guardar",
"Save & Exit": "Guardar & Volver",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "URL de inicio de sesión",
"Signin URL - Tooltip": "URL de inicio de sesión - Tooltip",
"Signup URL": "URL de registro",
@ -196,12 +246,16 @@
"Signup application - Tooltip": "Aplicación de inicio de sesión - Tooltip",
"Sorry, the page you visited does not exist.": "Lo sentimos, la página que visitaste no existe.",
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Lo sentimos, el usuario que visitaste no existe o no estás autorizado para acceder.",
"Sorry, you do not have permission to access this page.": "Lo sentimos, no tienes permiso para acceder a esta página.",
"Sorry, you do not have permission to access this page or logged in status invalid.": "Sorry, you do not have permission to access this page or logged in status invalid.",
"State": "Estado",
"State - Tooltip": "Estado - Tooltip",
"Successfully added": "Successfully added",
"Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved",
"Swagger": "Swagger",
"Sync": "Sincronizador",
"Syncers": "Sincronizadores",
"SysInfo": "SysInfo",
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"URL": "URL",
@ -250,6 +304,7 @@
"Continue with": "Continuar con",
"Email or phone": "Email o teléfono",
"Forgot password?": "¿Olvidó su contraseña?",
"Loading": "Loading",
"Logging out...": "Cerrando su sesión...",
"No account?": "¿No estás registrado?",
"Or sign in with another account": "O inicia sesión con otra cuenta",
@ -259,14 +314,16 @@
"Please input your password!": "¡Por favor ingrese su contraseña!",
"Please input your password, at least 6 characters!": "Su contraseña debe contener al menos 6 caracteres.",
"Please input your username, Email or phone!": "¡Ingrese su nombre de usuario, correo electrónico o teléfono!",
"Redirecting, please wait.": "Redirecting, please wait.",
"Sign In": "Iniciar de sesión",
"Sign in with WebAuthn": "Iniciar de sesión con WebAuthn",
"Sign in with code": "Iniciar de sesión con código",
"Sign in with password": "Iniciar de sesión con contraseña",
"Sign in with {type}": "Iniciar de sesión con {type}",
"Signing in...": "Iniciando de sesión...",
"Successfully logged in with webauthn credentials": "Successfully logged in with webauthn credentials",
"The input is not valid Email or Phone!": "El valor ingresado no es un Email o Teléfono válido!",
"To access": "Para ingresar",
"Verification Code": "Verification Code",
"WebAuthn": "WebAuthn",
"sign up now": "Regístrate ahora",
"username, Email or phone": "nombre de usuario, correo electrónico o teléfono"
},
@ -282,17 +339,20 @@
"Default avatar": "Avatar por defecto",
"Edit Organization": "Editar Organización",
"Favicon": "Favicon",
"InitScore": "InitScore",
"Is profile public": "Es el perfil publico",
"Is profile public - Tooltip": "Es el perfil publico - Tooltip",
"Modify rule": "Modify rule",
"New Organization": "Nueva Organización",
"Soft deletion": "Eliminación Soft",
"Soft deletion - Tooltip": "Eliminación Soft - Tooltip",
"Tags": "Etiquetas",
"Tags - Tooltip": "Etiquetas - Tooltip",
"The user's initScore - Tooltip": "The user's initScore - Tooltip",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "URL del sitio web",
"Website URL - Tooltip": "URL del sitio web - Tooltip",
"modifyRule": "modifyRule",
"viewRule": "viewRule"
"Website URL - Tooltip": "URL del sitio web - Tooltip"
},
"payment": {
"Confirm your invoice information": "Confirma la información de tu factura",
@ -349,14 +409,20 @@
"permission": {
"Actions": "Acciones",
"Actions - Tooltip": "Acciones - Tooltip",
"Admin": "Admin",
"Allow": "Allow",
"Approve time": "Fecha de aprobación",
"Approve time - Tooltip": "Fecha de aprobación - Tooltip",
"Approved": "Approved",
"Approver": "Aprobador",
"Approver - Tooltip": "Aprobador - Tooltip",
"Deny": "Deny",
"Edit Permission": "Editar Permiso",
"Effect": "Effect",
"Effect - Tooltip": "Effect - Tooltip",
"New Permission": "Nuevo Permiso",
"Pending": "Pending",
"Read": "Read",
"Resource type": "Tipo de recurso",
"Resource type - Tooltip": "Tipo de recurso - Tooltip",
"Resources": "Recursos",
@ -364,7 +430,9 @@
"State": "Estado",
"State - Tooltip": "Estado - Tooltip",
"Submitter": "Submitter",
"Submitter - Tooltip": "Submitter - Tooltip"
"Submitter - Tooltip": "Submitter - Tooltip",
"TreeNode": "TreeNode",
"Write": "Write"
},
"product": {
"Alipay": "Alipay",
@ -373,9 +441,12 @@
"CNY": "CNY",
"Currency": "Moneda",
"Currency - Tooltip": "Moneda - Tooltip",
"Description": "Description",
"Description - Tooltip": "Description - Tooltip",
"Detail": "Detalle",
"Detail - Tooltip": "Detalle - Tooltip",
"Edit Product": "Editar Producto",
"I have completed the payment": "I have completed the payment",
"Image": "Imagen",
"Image - Tooltip": "Imagen - Tooltip",
"New Product": "Nuevo producto",
@ -384,6 +455,8 @@
"Payment providers - Tooltip": "Pasarelas de pago - Tooltip",
"Paypal": "Paypal",
"Placing order...": "Creando su pedido...",
"Please provide your username in the remark": "Please provide your username in the remark",
"Please scan the QR code to pay": "Please scan the QR code to pay",
"Price": "Precio",
"Price - Tooltip": "Precio - Tooltip",
"Quantity": "Cantidad",
@ -417,6 +490,9 @@
"Bucket": "Bucket",
"Bucket - Tooltip": "Bucket - Tooltip",
"Can not parse Metadata": "Can not parse Metadata",
"Can signin": "Can signin",
"Can signup": "Can signup",
"Can unlink": "Can unlink",
"Category": "Categoria",
"Category - Tooltip": "Categoria - Tooltip",
"Channel No.": "Channel No.",
@ -439,6 +515,8 @@
"Email Content - Tooltip": "Contenido del Email - Tooltip",
"Email Title": "Titulo del Email",
"Email Title - Tooltip": "Titulo del Email - Tooltip",
"Enable QR code": "Enable QR code",
"Enable QR code - Tooltip": "Enable QR code - Tooltip",
"Endpoint": "Endpoint",
"Endpoint (Intranet)": "Endpoint (Intranet)",
"Host": "Host",
@ -456,14 +534,18 @@
"New Provider": "Nuevo Proveedor",
"Parse": "Parsear",
"Parse Metadata successfully": "Metadata parseada con éxito",
"Path prefix": "Path prefix",
"Please use WeChat and scan the QR code to sign in": "Please use WeChat and scan the QR code to sign in",
"Port": "Puerto",
"Port - Tooltip": "Puerto - Tooltip",
"Prompted": "Prompted",
"Provider URL": "URL del Proveedor",
"Provider URL - Tooltip": "URL del Proveedor - Tooltip",
"Region ID": "Region ID",
"Region ID - Tooltip": "Region ID - Tooltip",
"Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account - Tooltip",
@ -500,19 +582,16 @@
"Test Connection": "Probar Conexión",
"Test Email": "Probar Email",
"Test Email - Tooltip": "Probar Email - Tooltip",
"The prefix path of the file - Tooltip": "The prefix path of the file - Tooltip",
"Token URL": "Token URL",
"Token URL - Tooltip": "Token URL - Tooltip",
"Type": "Tipo",
"Type - Tooltip": "Tipo - Tooltip",
"UserInfo URL": "UserInfo URL",
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
"alertType": "alertType",
"canSignIn": "canSignIn",
"canSignUp": "canSignUp",
"canUnlink": "canUnlink",
"prompted": "prompted",
"required": "required",
"visible": "visible"
"Visible": "Visible",
"admin (share)": "admin (share)",
"alertType": "alertType"
},
"record": {
"Is Triggered": "Esta activado"
@ -597,6 +676,18 @@
"Table primary key": "Clave primaria de la tabla",
"Table primary key - Tooltip": "Clave primaria de la tabla - Tooltip"
},
"system": {
"About Casdoor": "About Casdoor",
"An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS": "An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS",
"CPU Usage": "CPU Usage",
"Community": "Community",
"Get CPU Usage Failed": "Get CPU Usage Failed",
"Get Memory Usage Failed": "Get Memory Usage Failed",
"Memory Usage": "Memory Usage",
"Official Website": "Official Website",
"Unknown Version": "Unknown Version",
"Version": "Version"
},
"token": {
"Access token": "Token de acceso",
"Authorization code": "Código de autorización",
@ -637,9 +728,11 @@
"Is forbidden - Tooltip": "Está prohibido - Tooltip",
"Is global admin": "Es admin global",
"Is global admin - Tooltip": "Es admin global - Tooltip",
"Keys": "Keys",
"Link": "Enlace",
"Location": "Ubicación",
"Location - Tooltip": "Ubicación - Tooltip",
"Managed accounts": "Managed accounts",
"Modify password...": "Cambiar contraseña...",
"New Email": "Nuevo Email",
"New Password": "Nueva Contraseña",
@ -649,7 +742,9 @@
"Old Password": "Contraseña anterior",
"Password": "Contraseña",
"Password Set": "Password Set",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Propiedades",
"Properties - Tooltip": "Properties - Tooltip",
"Re-enter New": "Reingrese de nuevo",
"Reset Email...": "Cambiar Email...",
"Reset Phone...": "Cambiar Phone...",
@ -665,6 +760,7 @@
"Unlink": "Desvincular",
"Upload (.xlsx)": "Subir archivo (.xlsx)",
"Upload a photo": "Subir foto",
"Values": "Values",
"WebAuthn credentials": "WebAuthn credentials",
"input password": "input password"
},

View File

@ -156,7 +156,10 @@
"Click to Upload": "Click to Upload",
"Client IP": "IP du client",
"Close": "Close",
"Compact": "Compact",
"Created time": "Date de création",
"Dark": "Dark",
"Default": "Default",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Avatar par défaut",
@ -233,6 +236,8 @@
"Roles - Tooltip": "Roles - Tooltip",
"Save": "Enregistrer",
"Save & Exit": "Enregistrer & Quitter",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "URL de connexion",
"Signin URL - Tooltip": "sign in url",
"Signup URL": "URL d'inscription",
@ -241,7 +246,7 @@
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Désolé, la page que vous avez visitée n'existe pas.",
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
"Sorry, you do not have permission to access this page.": "Désolé, vous n'avez pas la permission d'accéder à cette page.",
"Sorry, you do not have permission to access this page or logged in status invalid.": "Sorry, you do not have permission to access this page or logged in status invalid.",
"State": "State",
"State - Tooltip": "State - Tooltip",
"Successfully added": "Successfully added",
@ -723,6 +728,7 @@
"Is forbidden - Tooltip": "Whether the account is disabled",
"Is global admin": "Est un administrateur global",
"Is global admin - Tooltip": "Is the application global administrator",
"Keys": "Keys",
"Link": "Lier",
"Location": "Localisation",
"Location - Tooltip": "Localisation - Infobulle",
@ -738,6 +744,7 @@
"Password Set": "Mot de passe défini",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Propriétés",
"Properties - Tooltip": "Properties - Tooltip",
"Re-enter New": "Nouvelle entrée",
"Reset Email...": "Reset Email...",
"Reset Phone...": "Réinitialiser le téléphone...",
@ -753,6 +760,7 @@
"Unlink": "Délier",
"Upload (.xlsx)": "Télécharger (.xlsx)",
"Upload a photo": "Télécharger une photo",
"Values": "Values",
"WebAuthn credentials": "WebAuthn credentials",
"input password": "saisir le mot de passe"
},

View File

@ -156,7 +156,10 @@
"Click to Upload": "Click to Upload",
"Client IP": "クライアント IP",
"Close": "Close",
"Compact": "Compact",
"Created time": "作成日時",
"Dark": "Dark",
"Default": "Default",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "デフォルトのアバター",
@ -233,6 +236,8 @@
"Roles - Tooltip": "Roles - Tooltip",
"Save": "保存",
"Save & Exit": "保存して終了",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "サインインURL",
"Signin URL - Tooltip": "sign in url",
"Signup URL": "サインアップURL",
@ -241,7 +246,7 @@
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "申し訳ありませんが、訪問したページは存在しません。",
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
"Sorry, you do not have permission to access this page.": "申し訳ありませんが、このページにアクセスする権限がありません。",
"Sorry, you do not have permission to access this page or logged in status invalid.": "Sorry, you do not have permission to access this page or logged in status invalid.",
"State": "State",
"State - Tooltip": "State - Tooltip",
"Successfully added": "Successfully added",
@ -723,6 +728,7 @@
"Is forbidden - Tooltip": "Whether the account is disabled",
"Is global admin": "グローバル管理者",
"Is global admin - Tooltip": "Is the application global administrator",
"Keys": "Keys",
"Link": "リンク",
"Location": "場所",
"Location - Tooltip": "場所 → ツールチップ",
@ -738,6 +744,7 @@
"Password Set": "パスワード設定",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "プロパティー",
"Properties - Tooltip": "Properties - Tooltip",
"Re-enter New": "新しい入力",
"Reset Email...": "Reset Email...",
"Reset Phone...": "電話番号をリセット...",
@ -753,6 +760,7 @@
"Unlink": "リンクを解除",
"Upload (.xlsx)": "アップロード (.xlsx)",
"Upload a photo": "写真をアップロード",
"Values": "Values",
"WebAuthn credentials": "WebAuthn credentials",
"input password": "パスワードを入力"
},

View File

@ -156,7 +156,10 @@
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Compact": "Compact",
"Created time": "Created time",
"Dark": "Dark",
"Default": "Default",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Default avatar",
@ -233,6 +236,8 @@
"Roles - Tooltip": "Roles - Tooltip",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "Signin URL",
"Signin URL - Tooltip": "sign in url",
"Signup URL": "Signup URL",
@ -241,7 +246,7 @@
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist.",
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
"Sorry, you do not have permission to access this page.": "Sorry, you do not have permission to access this page.",
"Sorry, you do not have permission to access this page or logged in status invalid.": "Sorry, you do not have permission to access this page or logged in status invalid.",
"State": "State",
"State - Tooltip": "State - Tooltip",
"Successfully added": "Successfully added",
@ -723,6 +728,7 @@
"Is forbidden - Tooltip": "Whether the account is disabled",
"Is global admin": "Is global admin",
"Is global admin - Tooltip": "Is the application global administrator",
"Keys": "Keys",
"Link": "Link",
"Location": "Location",
"Location - Tooltip": "Location - Tooltip",
@ -738,6 +744,7 @@
"Password Set": "Password Set",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Properties",
"Properties - Tooltip": "Properties - Tooltip",
"Re-enter New": "Re-enter New",
"Reset Email...": "Reset Email...",
"Reset Phone...": "Reset Phone...",
@ -753,6 +760,7 @@
"Unlink": "Unlink",
"Upload (.xlsx)": "Upload (.xlsx)",
"Upload a photo": "Upload a photo",
"Values": "Values",
"WebAuthn credentials": "WebAuthn credentials",
"input password": "input password"
},

View File

@ -156,7 +156,10 @@
"Click to Upload": "Нажмите здесь, чтобы загрузить",
"Client IP": "IP клиента",
"Close": "Close",
"Compact": "Compact",
"Created time": "Время создания",
"Dark": "Dark",
"Default": "Default",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Аватар по умолчанию",
@ -233,6 +236,8 @@
"Roles - Tooltip": "Roles - Tooltip",
"Save": "Сохранить",
"Save & Exit": "Сохранить и выйти",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "URL входа",
"Signin URL - Tooltip": "sign in url",
"Signup URL": "URL регистрации",
@ -241,7 +246,7 @@
"Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Извините, посещенная вами страница не существует.",
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
"Sorry, you do not have permission to access this page.": "Извините, вы не имеете права доступа к этой странице.",
"Sorry, you do not have permission to access this page or logged in status invalid.": "Sorry, you do not have permission to access this page or logged in status invalid.",
"State": "State",
"State - Tooltip": "State - Tooltip",
"Successfully added": "Successfully added",
@ -723,6 +728,7 @@
"Is forbidden - Tooltip": "Whether the account is disabled",
"Is global admin": "Глобальный администратор",
"Is global admin - Tooltip": "Is the application global administrator",
"Keys": "Keys",
"Link": "Ссылка",
"Location": "Местоположение",
"Location - Tooltip": "Расположение - Подсказка",
@ -738,6 +744,7 @@
"Password Set": "Пароль установлен",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Свойства",
"Properties - Tooltip": "Properties - Tooltip",
"Re-enter New": "Введите еще раз",
"Reset Email...": "Reset Email...",
"Reset Phone...": "Сбросить телефон...",
@ -753,6 +760,7 @@
"Unlink": "Отвязать",
"Upload (.xlsx)": "Загрузить (.xlsx)",
"Upload a photo": "Загрузить фото",
"Values": "Values",
"WebAuthn credentials": "WebAuthn credentials",
"input password": "пароль для ввода"
},

View File

@ -156,7 +156,10 @@
"Click to Upload": "点击上传",
"Client IP": "客户端IP",
"Close": "关闭",
"Compact": "紧凑",
"Created time": "创建时间",
"Dark": "黑暗",
"Default": "默认",
"Default application": "默认应用",
"Default application - Tooltip": "默认应用",
"Default avatar": "默认头像",
@ -233,6 +236,8 @@
"Roles - Tooltip": "角色",
"Save": "保存",
"Save & Exit": "保存 & 退出",
"Session ID": "会话 ID",
"Sessions": "会话",
"Signin URL": "登录URL",
"Signin URL - Tooltip": "用户的登录地址",
"Signup URL": "注册URL",
@ -241,7 +246,7 @@
"Signup application - Tooltip": "表示用户注册时通过哪个应用注册的",
"Sorry, the page you visited does not exist.": "抱歉,您访问的页面不存在",
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "抱歉,您访问的用户不存在或您无权访问该用户",
"Sorry, you do not have permission to access this page.": "抱歉,您无权访问该页面",
"Sorry, you do not have permission to access this page or logged in status invalid.": "抱歉,您无权访问该页面或登录状态失效",
"State": "状态",
"State - Tooltip": "状态",
"Successfully added": "添加成功",
@ -723,6 +728,7 @@
"Is forbidden - Tooltip": "账户是否已被禁用",
"Is global admin": "是全局管理员",
"Is global admin - Tooltip": "是应用程序管理员",
"Keys": "键",
"Link": "绑定",
"Location": "城市",
"Location - Tooltip": "居住地址所在的城市",
@ -738,6 +744,7 @@
"Password Set": "密码已设置",
"Please select avatar from resources": "从资源中选择...",
"Properties": "属性",
"Properties - Tooltip": "属性",
"Re-enter New": "重复新密码",
"Reset Email...": "重置邮箱...",
"Reset Phone...": "重置手机号...",
@ -753,6 +760,7 @@
"Unlink": "解绑",
"Upload (.xlsx)": "上传(.xlsx",
"Upload a photo": "上传头像",
"Values": "值",
"WebAuthn credentials": "WebAuthn凭据",
"input password": "输入密码"
},

131
web/src/propertyTable.js Normal file
View File

@ -0,0 +1,131 @@
// 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.
import React from "react";
import {Button, Input, Table} from "antd";
import i18next from "i18next";
import {DeleteOutlined} from "@ant-design/icons";
import * as Setting from "./Setting";
class PropertyTable extends React.Component {
constructor(props) {
super(props);
this.state = {
properties: [],
count: Object.entries(this.props.properties).length,
};
// transfer the Object to object[]
Object.entries(this.props.properties).map((item, index) => {
this.state.properties.push({key: index, name: item[0], value: item[1]});
});
}
page = 1;
updateTable(table) {
this.setState({properties: table});
const properties = {};
table.map((item) => {
properties[item.name] = item.value;
});
this.props.onUpdateTable(properties);
}
addRow(table) {
const row = {key: this.state.count, name: "", value: ""};
if (table === undefined) {
table = [];
}
table = Setting.addRow(table, row);
this.setState({count: this.state.count + 1});
this.updateTable(table);
}
deleteRow(table, index) {
table = Setting.deleteRow(table, this.getIndex(index));
this.updateTable(table);
}
getIndex(index) {
// Parameter is the row index in table, need to calculate the index in dataSource. 10 is the pageSize.
return index + (this.page - 1) * 10;
}
updateField(table, index, key, value) {
table[this.getIndex(index)][key] = value;
this.updateTable(table);
}
renderTable(table) {
const columns = [
{
title: i18next.t("user:Keys"),
dataIndex: "name",
width: "200px",
render: (text, record, index) => {
return (
<Input value={text} onChange={e => {
this.updateField(table, index, "name", e.target.value);
}} />
);
},
},
{
title: i18next.t("user:Values"),
dataIndex: "value",
width: "200px",
render: (text, record, index) => {
return (
<Input value={text} onChange={e => {
this.updateField(table, index, "value", e.target.value);
}} />
);
},
},
{
title: i18next.t("general:Action"),
dataIndex: "operation",
width: "20px",
render: (text, record, index) => {
return (
<Button icon={<DeleteOutlined />} size="small" onClick={() => this.deleteRow(table, index)} />
);
},
},
];
return (
<Table title={() => (
<div>
<Button style={{marginRight: "5px"}} type="primary" size="small" onClick={() => this.addRow(table)}>{i18next.t("general:Add")}</Button>
</div>
)}
pagination={{onChange: page => {this.page = page;}}}
columns={columns} dataSource={table} rowKey="key" size="middle" bordered
/>
);
}
render() {
return (
<React.Fragment>
{
this.renderTable(this.state.properties)
}
</React.Fragment>
);
}
}
export default PropertyTable;

File diff suppressed because it is too large Load Diff