Compare commits

...

45 Commits

Author SHA1 Message Date
457e6208ad feat: terms of use auto selected (#1485) 2023-01-19 20:31:21 +08:00
d10b1347a8 feat: add terms of use in signin page (#1476)
* feat: extract terms of use renderer

* fix: layout

* fix: form styling

* fix: required state

* feat: application terms of use setting

* fix: refactor getTermsOfUseContent

* fix: refactor renderers
2023-01-19 18:39:24 +08:00
f5b7f8cb45 chore(frontend): remove import of the third-party js script (#1436)
Signed-off-by: qwqcode <qwqcode@gmail.com>

Signed-off-by: qwqcode <qwqcode@gmail.com>
2023-01-19 11:31:27 +08:00
5d9b17542f feat: end-user log out (#1356) 2023-01-17 22:57:05 +08:00
0021226a60 fix: check the duplicated Application ClientId (#1481)
* fix: Check the duplicate ClientId and ClientSecret of Application.

* Bug fix
2023-01-17 17:37:20 +08:00
79fc0516dd feat: check username if it's changed (#1482) 2023-01-17 17:08:37 +08:00
a73be11990 feat: update permission when role deleted (#1480) 2023-01-17 17:04:58 +08:00
eddd8acbf4 feat: update permission rule when role updated (#1477) 2023-01-17 10:27:02 +08:00
d0741e3705 feat: fix compatibility issue between Casbin request and model (#1478) 2023-01-15 12:06:10 +08:00
Liu
c66561dc9a feat: support sqlite database without cgo by using the modernc.org/sqlite driver (#1474)
* Refactor: update sqlite go driver to modernc.org/sqlite without cgo

* fix: update sqlite driver to modernc.org/sqlite

* fix: sqlite driver to modernc.org/sqlite

* Update adapter.go

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-01-14 20:11:23 +08:00
fcdf1e8dd2 feat: improve Select component performance (#1472) 2023-01-12 23:11:11 +08:00
6d4f94986e feat: fix the bug that "app - global" admin is not allowed to update user (#1468) 2023-01-12 12:29:11 +08:00
9ca686b240 feat: disable role and permission update and checks when updating user (#1466) 2023-01-12 11:40:32 +08:00
c93bc0dda2 fix: add e2e cypress screenshots and videos if failed (#1465)
* feat: location error

* feat: location error

* feat: test error

* feat add e2e

* feat: delete cypress dependency

* feat: Add e2e error feedback
2023-01-11 23:56:09 +08:00
7d25b9cdd8 feat: auto link accounts with the same email (#1464) 2023-01-11 23:19:16 +08:00
ead844131e feat: improve user edit page to fix missing fields and page crash (#1463) 2023-01-11 16:15:06 +08:00
ce2a4bbf6e feat: check uniqueness for email and phone when updating user (#1461)
* fix: check unique field when update user

* Update data.json

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-01-10 22:34:08 +08:00
fcb80b800f feat: add refresh token to token login response (#1458)
Signed-off-by: 疯魔慕薇 <kfanjian@gmail.com>

Signed-off-by: 疯魔慕薇 <kfanjian@gmail.com>
2023-01-09 23:33:03 +08:00
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
107 changed files with 15048 additions and 12941 deletions

5
.gitattributes vendored
View File

@ -1,2 +1,5 @@
*.go linguist-detectable=true *.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,59 @@ jobs:
version: latest version: latest
args: --disable-all -c dummy.yml -E=gofumpt --max-same-issues=0 --timeout 5m --modules-download-mode=mod 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 90s
shell: bash
- name: e2e
run: npx cypress run --spec "**/e2e/**.cy.js"
working-directory: ./web
- uses: actions/upload-artifact@v3
if: failure()
with:
name: cypress-screenshots
path: ./web/cypress/screenshots
- uses: actions/upload-artifact@v3
if: always()
with:
name: cypress-videos
path: ./web/cypress/videos
release-and-push: release-and-push:
name: Release And Push name: Release And Push
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
needs: [ frontend, backend, linter ] needs: [ frontend, backend, linter, e2e ]
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2

View File

@ -17,6 +17,7 @@ package controllers
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http"
"strconv" "strconv"
"strings" "strings"
@ -238,20 +239,67 @@ func (c *ApiController) Signup() {
// @Title Logout // @Title Logout
// @Tag Login API // @Tag Login API
// @Description logout the current user // @Description logout the current user
// @Param id_token_hint query string false "id_token_hint"
// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri"
// @Param state query string false "state"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /logout [get,post] // @router /logout [get,post]
func (c *ApiController) Logout() { func (c *ApiController) Logout() {
user := c.GetSessionUsername() user := c.GetSessionUsername()
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
application := c.GetSessionApplication() // https://openid.net/specs/openid-connect-rpinitiated-1_0-final.html
c.ClearUserSession() accessToken := c.Input().Get("id_token_hint")
redirectUri := c.Input().Get("post_logout_redirect_uri")
state := c.Input().Get("state")
if application == nil || application.Name == "app-built-in" || application.HomepageUrl == "" { if accessToken == "" && redirectUri == "" {
c.ResponseOk(user) c.ClearUserSession()
object.DeleteSessionId(user, c.Ctx.Input.CruSession.SessionID())
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
application := c.GetSessionApplication()
if application == nil || application.Name == "app-built-in" || application.HomepageUrl == "" {
c.ResponseOk(user)
return
}
c.ResponseOk(user, application.HomepageUrl)
return return
} else {
if redirectUri == "" {
c.ResponseError(c.T("general:Missing parameter") + ": post_logout_redirect_uri")
return
}
if accessToken == "" {
c.ResponseError(c.T("general:Missing parameter") + ": id_token_hint")
return
}
affected, application, token := object.ExpireTokenByAccessToken(accessToken)
if !affected {
c.ResponseError(c.T("token:Token not found, invalid accessToken"))
return
}
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist")), token.Application)
return
}
if application.IsRedirectUriValid(redirectUri) {
if user == "" {
user = util.GetId(token.Organization, token.User)
}
c.ClearUserSession()
object.DeleteSessionId(user, c.Ctx.Input.CruSession.SessionID())
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
c.Ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?state=%s", strings.TrimRight(redirectUri, "/"), state))
} else {
c.ResponseError(fmt.Sprintf(c.T("token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri))
return
}
} }
c.ResponseOk(user, application.HomepageUrl)
} }
// GetAccount // GetAccount
@ -271,12 +319,17 @@ func (c *ApiController) GetAccount() {
user = object.ExtendManagedAccountsWithUser(user) 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)) organization := object.GetMaskedOrganization(object.GetOrganizationByUser(user))
resp := Response{ resp := Response{
Status: "ok", Status: "ok",
Sub: user.Id, Sub: user.Id,
Name: user.Name, Name: user.Name,
Data: user, Data: object.GetMaskedUser(user),
Data2: organization, Data2: organization,
} }
c.Data["json"] = resp c.Data["json"] = resp

View File

@ -86,7 +86,7 @@ func (c *ApiController) GetUserApplication() {
id := c.Input().Get("id") id := c.Input().Get("id")
user := object.GetUser(id) user := object.GetUser(id)
if user == nil { 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 return
} }
@ -113,7 +113,7 @@ func (c *ApiController) GetOrganizationApplications() {
sortOrder := c.Input().Get("sortOrder") sortOrder := c.Input().Get("sortOrder")
if organization == "" { if organization == "" {
c.ResponseError(c.T("application:Parameter organization is missing")) c.ResponseError(c.T("general:Missing parameter") + ": organization")
return return
} }

View File

@ -52,7 +52,7 @@ func tokenToResponse(token *object.Token) *Response {
if token.AccessToken == "" { if token.AccessToken == "" {
return &Response{Status: "error", Msg: "fail to get accessToken", Data: token.AccessToken} return &Response{Status: "error", Msg: "fail to get accessToken", Data: token.AccessToken}
} }
return &Response{Status: "ok", Msg: "", Data: token.AccessToken} return &Response{Status: "ok", Msg: "", Data: token.AccessToken, Data2: token.RefreshToken}
} }
// HandleLoggedIn ... // HandleLoggedIn ...
@ -103,12 +103,12 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
resp = tokenToResponse(token) resp = tokenToResponse(token)
} }
} else if form.Type == ResponseTypeSaml { // saml flow } 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 { if err != nil {
c.ResponseError(err.Error(), nil) c.ResponseError(err.Error(), nil)
return 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 { } else if form.Type == ResponseTypeCas {
// not oauth but CAS SSO protocol // not oauth but CAS SSO protocol
service := c.Input().Get("service") 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 return resp
} }
@ -205,7 +209,7 @@ func (c *ApiController) Login() {
if form.Username != "" { if form.Username != "" {
if form.Type == ResponseTypeLogin { if form.Type == ResponseTypeLogin {
if c.GetSessionUsername() != "" { 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 return
} }
} }
@ -222,12 +226,13 @@ func (c *ApiController) Login() {
} }
// check result through Email or Phone // check result through Email or Phone
var checkDest string
if strings.Contains(form.Username, "@") { if strings.Contains(form.Username, "@") {
verificationCodeType = "email" verificationCodeType = "email"
if user != nil && util.GetMaskedEmail(user.Email) == form.Username { if user != nil && util.GetMaskedEmail(user.Email) == form.Username {
form.Username = user.Email form.Username = user.Email
} }
checkResult = object.CheckVerificationCode(form.Username, form.Code, c.GetAcceptLanguage()) checkDest = form.Username
} else { } else {
verificationCodeType = "phone" verificationCodeType = "phone"
if len(form.PhonePrefix) == 0 { if len(form.PhonePrefix) == 0 {
@ -238,11 +243,16 @@ func (c *ApiController) Login() {
if user != nil && util.GetMaskedPhone(user.Phone) == form.Username { if user != nil && util.GetMaskedPhone(user.Phone) == form.Username {
form.Username = user.Phone form.Username = user.Phone
} }
checkPhone := fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username) checkDest = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)
checkResult = object.CheckVerificationCode(checkPhone, form.Code, c.GetAcceptLanguage())
} }
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 { if len(checkResult) != 0 {
responseText := fmt.Sprintf("%s%s", verificationCodeType, checkResult) responseText := fmt.Sprintf("%s - %s", verificationCodeType, checkResult)
c.ResponseError(responseText) c.ResponseError(responseText)
return return
} }
@ -253,18 +263,16 @@ func (c *ApiController) Login() {
} else { } else {
object.DisableVerificationCode(fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)) 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 { } else {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application)) application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil { if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application)) c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
return 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) { if object.CheckToEnableCaptcha(application) {
isHuman, err := captcha.VerifyCaptchaByCaptchaType(form.CaptchaType, form.CaptchaToken, form.ClientSecret) isHuman, err := captcha.VerifyCaptchaByCaptchaType(form.CaptchaType, form.CaptchaToken, form.ClientSecret)
@ -397,54 +405,61 @@ func (c *ApiController) Login() {
return return
} }
// Handle username conflicts if application.EnableLinkWithEmail {
tmpUser := object.GetUser(fmt.Sprintf("%s/%s", application.Organization, userInfo.Username)) // find user that has the same email
if tmpUser != nil { user = object.GetUserByField(application.Organization, "email", userInfo.Email)
uid, err := uuid.NewRandom() }
if user == nil || user.IsDeleted {
// Handle username conflicts
tmpUser := object.GetUser(fmt.Sprintf("%s/%s", application.Organization, userInfo.Username))
if tmpUser != nil {
uid, err := uuid.NewRandom()
if err != nil {
c.ResponseError(err.Error())
return
}
uidStr := strings.Split(uid.String(), "-")
userInfo.Username = fmt.Sprintf("%s_%s", userInfo.Username, uidStr[1])
}
properties := map[string]string{}
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
initScore, err := getInitScore(organization)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
return return
} }
uidStr := strings.Split(uid.String(), "-") user = &object.User{
userInfo.Username = fmt.Sprintf("%s_%s", userInfo.Username, uidStr[1]) Owner: application.Organization,
Name: userInfo.Username,
CreatedTime: util.GetCurrentTime(),
Id: util.GenerateId(),
Type: "normal-user",
DisplayName: userInfo.DisplayName,
Avatar: userInfo.AvatarUrl,
Address: []string{},
Email: userInfo.Email,
Score: initScore,
IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false,
IsDeleted: false,
SignupApplication: application.Name,
Properties: properties,
}
affected := object.AddUser(user)
if !affected {
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to create user, user information is invalid: %s"), util.StructToJson(user)))
return
}
} }
properties := map[string]string{}
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())
return
}
user = &object.User{
Owner: application.Organization,
Name: userInfo.Username,
CreatedTime: util.GetCurrentTime(),
Id: util.GenerateId(),
Type: "normal-user",
DisplayName: userInfo.DisplayName,
Avatar: userInfo.AvatarUrl,
Address: []string{},
Email: userInfo.Email,
Score: initScore,
IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false,
IsDeleted: false,
SignupApplication: application.Name,
Properties: properties,
}
// sync info from 3rd-party if possible // sync info from 3rd-party if possible
object.SetUserOAuthProperties(organization, user, provider.Type, userInfo) object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
affected := object.AddUser(user)
if !affected {
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to create user, user information is invalid: %s"), util.StructToJson(user)))
return
}
object.LinkUserAccount(user, provider.Type, userInfo.Id) object.LinkUserAccount(user, provider.Type, userInfo.Id)
resp = c.HandleLoggedIn(application, user, &form) resp = c.HandleLoggedIn(application, user, &form)
@ -460,13 +475,13 @@ func (c *ApiController) Login() {
record2.User = user.Name record2.User = user.Name
util.SafeGoroutine(func() { object.AddRecord(record2) }) util.SafeGoroutine(func() { object.AddRecord(record2) })
} else if provider.Category == "SAML" { } 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} // resp = &Response{Status: "ok", Msg: "", Data: res}
} else { // form.Method != "signup" } else { // form.Method != "signup"
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
if userId == "" { 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 return
} }

View File

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

View File

@ -52,7 +52,7 @@ func (c *ApiController) GetLdapUser() {
ldapServer := LdapServer{} ldapServer := LdapServer{}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer) err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer)
if err != nil || util.IsStrsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) { if err != nil || util.IsStrsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) {
c.ResponseError(c.T("ldap:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }
@ -120,7 +120,7 @@ func (c *ApiController) GetLdap() {
id := c.Input().Get("id") id := c.Input().Get("id")
if util.IsStrsEmpty(id) { if util.IsStrsEmpty(id) {
c.ResponseError(c.T("ldap:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }
@ -136,12 +136,12 @@ func (c *ApiController) AddLdap() {
var ldap object.Ldap var ldap object.Ldap
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap) err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
if err != nil { if err != nil {
c.ResponseError(c.T("ldap:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }
if util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) { if util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
c.ResponseError(c.T("ldap:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }
@ -171,7 +171,7 @@ func (c *ApiController) UpdateLdap() {
var ldap object.Ldap var ldap object.Ldap
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap) err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
if err != nil || util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) { if err != nil || util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
c.ResponseError(c.T("ldap:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }

View File

@ -141,13 +141,13 @@ func (c *ApiController) BuyProduct() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
if userId == "" { if userId == "" {
c.ResponseError(c.T("product:Please login first")) c.ResponseError(c.T("general:Please login first"))
return return
} }
user := object.GetUser(userId) user := object.GetUser(userId)
if user == nil { 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 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) user := object.GetUser(id)
if user == nil || !user.IsGlobalAdmin { 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 return
} }

View File

@ -16,7 +16,6 @@ package controllers
import ( import (
"encoding/json" "encoding/json"
"net/http"
"github.com/beego/beego/utils/pagination" "github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
@ -150,7 +149,7 @@ func (c *ApiController) GetOAuthCode() {
codeChallenge := c.Input().Get("code_challenge") codeChallenge := c.Input().Get("code_challenge")
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" { 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 return
} }
host := c.Ctx.Request.Host host := c.Ctx.Request.Host
@ -247,28 +246,6 @@ func (c *ApiController) RefreshToken() {
c.ServeJSON() c.ServeJSON()
} }
// TokenLogout
// @Title TokenLogout
// @Tag Token API
// @Description delete token by AccessToken
// @Param id_token_hint query string true "id_token_hint"
// @Param post_logout_redirect_uri query string false "post_logout_redirect_uri"
// @Param state query string true "state"
// @Success 200 {object} controllers.Response The Response object
// @router /login/oauth/logout [get]
func (c *ApiController) TokenLogout() {
token := c.Input().Get("id_token_hint")
flag, application := object.DeleteTokenByAccessToken(token)
redirectUri := c.Input().Get("post_logout_redirect_uri")
state := c.Input().Get("state")
if application != nil && application.IsRedirectUriValid(redirectUri) {
c.Ctx.Redirect(http.StatusFound, redirectUri+"?state="+state)
return
}
c.Data["json"] = wrapActionResponse(flag)
c.ServeJSON()
}
// IntrospectToken // IntrospectToken
// @Title IntrospectToken // @Title IntrospectToken
// @Description The introspection endpoint is an OAuth 2.0 endpoint that takes a // @Description The introspection endpoint is an OAuth 2.0 endpoint that takes a

View File

@ -148,8 +148,8 @@ func (c *ApiController) UpdateUser() {
return return
} }
if user.DisplayName == "" { if msg := object.CheckUpdateUser(object.GetUser(id), &user, c.GetAcceptLanguage()); msg != "" {
c.ResponseError(c.T("user:Display name cannot be empty")) c.ResponseError(msg)
return return
} }
@ -159,6 +159,12 @@ func (c *ApiController) UpdateUser() {
} }
isGlobalAdmin := c.IsGlobalAdmin() isGlobalAdmin := c.IsGlobalAdmin()
if pass, err := checkPermissionForUpdateUser(id, user, c); !pass {
c.ResponseError(err)
return
}
affected := object.UpdateUser(id, &user, columns, isGlobalAdmin) affected := object.UpdateUser(id, &user, columns, isGlobalAdmin)
if affected { if affected {
object.UpdateUserToOriginalDatabase(&user) object.UpdateUserToOriginalDatabase(&user)
@ -236,7 +242,7 @@ func (c *ApiController) GetEmailAndPhone() {
user := object.GetUserByFields(form.Organization, form.Username) user := object.GetUserByFields(form.Organization, form.Username)
if user == nil { 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 return
} }

135
controllers/user_util.go Normal file
View File

@ -0,0 +1,135 @@
// 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)
}
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)
}
currentUser := c.getCurrentUser()
if currentUser == nil && c.IsGlobalAdmin() {
currentUser = &object.User{
IsGlobalAdmin: true,
}
}
for i := range itemsChanged {
if pass, err := object.CheckAccountItemModifyRule(itemsChanged[i], currentUser, c.GetAcceptLanguage()); !pass {
return pass, err
}
}
return true, ""
}

View File

@ -84,7 +84,7 @@ func (c *ApiController) SetTokenErrorHttpStatus() {
func (c *ApiController) RequireSignedIn() (string, bool) { func (c *ApiController) RequireSignedIn() (string, bool) {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
if userId == "" { 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 "", false
} }
return userId, true return userId, true
@ -100,7 +100,7 @@ func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
user := object.GetUser(userId) user := object.GetUser(userId)
if user == nil { if user == nil {
c.ClearUserSession() 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 nil, false
} }
return user, true return user, true

View File

@ -51,15 +51,15 @@ func (c *ApiController) SendVerificationCode() {
remoteAddr := util.GetIPFromRequest(c.Ctx.Request) remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
if destType == "" { if destType == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": type.") c.ResponseError(c.T("general:Missing parameter") + ": type.")
return return
} }
if dest == "" { if dest == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": dest.") c.ResponseError(c.T("general:Missing parameter") + ": dest.")
return return
} }
if applicationId == "" { if applicationId == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": applicationId.") c.ResponseError(c.T("general:Missing parameter") + ": applicationId.")
return return
} }
if !strings.Contains(applicationId, "/") { if !strings.Contains(applicationId, "/") {
@ -67,7 +67,7 @@ func (c *ApiController) SendVerificationCode() {
return return
} }
if checkType == "" { if checkType == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": checkType.") c.ResponseError(c.T("general:Missing parameter") + ": checkType.")
return return
} }
@ -75,7 +75,7 @@ func (c *ApiController) SendVerificationCode() {
if captchaProvider != nil { if captchaProvider != nil {
if checkKey == "" { if checkKey == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": checkKey.") c.ResponseError(c.T("general:Missing parameter") + ": checkKey.")
return return
} }
isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId) isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId)
@ -99,7 +99,7 @@ func (c *ApiController) SendVerificationCode() {
} }
if checkUser == "true" && user == nil && object.GetUserByFields(organization.Name, dest) == nil { 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 return
} }
@ -170,14 +170,19 @@ func (c *ApiController) ResetEmailOrPhone() {
dest := c.Ctx.Request.Form.Get("dest") dest := c.Ctx.Request.Form.Get("dest")
code := c.Ctx.Request.Form.Get("code") code := c.Ctx.Request.Form.Get("code")
if len(dest) == 0 || len(code) == 0 || len(destType) == 0 { if len(dest) == 0 || len(code) == 0 || len(destType) == 0 {
c.ResponseError(c.T("verification:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }
checkDest := dest checkDest := dest
org := object.GetOrganizationByUser(user) organization := object.GetOrganizationByUser(user)
if destType == "phone" { if destType == "phone" {
phoneItem := object.GetAccountItemByName("Phone", org) if object.HasUserByField(user.Owner, "phone", user.Phone) {
c.ResponseError(c.T("check:Phone already exists"))
return
}
phoneItem := object.GetAccountItemByName("Phone", organization)
if phoneItem == nil { if phoneItem == nil {
c.ResponseError(c.T("verification:Unable to get the phone modify rule.")) c.ResponseError(c.T("verification:Unable to get the phone modify rule."))
return return
@ -189,12 +194,17 @@ func (c *ApiController) ResetEmailOrPhone() {
} }
phonePrefix := "86" phonePrefix := "86"
if org != nil && org.PhonePrefix != "" { if organization != nil && organization.PhonePrefix != "" {
phonePrefix = org.PhonePrefix phonePrefix = organization.PhonePrefix
} }
checkDest = fmt.Sprintf("+%s%s", phonePrefix, dest) checkDest = fmt.Sprintf("+%s%s", phonePrefix, dest)
} else if destType == "email" { } else if destType == "email" {
emailItem := object.GetAccountItemByName("Email", org) if object.HasUserByField(user.Owner, "email", user.Email) {
c.ResponseError(c.T("check:Email already exists"))
return
}
emailItem := object.GetAccountItemByName("Email", organization)
if emailItem == nil { if emailItem == nil {
c.ResponseError(c.T("verification:Unable to get the email modify rule.")) c.ResponseError(c.T("verification:Unable to get the email modify rule."))
return return
@ -237,11 +247,11 @@ func (c *ApiController) VerifyCaptcha() {
captchaToken := c.Ctx.Request.Form.Get("captchaToken") captchaToken := c.Ctx.Request.Form.Get("captchaToken")
clientSecret := c.Ctx.Request.Form.Get("clientSecret") clientSecret := c.Ctx.Request.Form.Get("clientSecret")
if captchaToken == "" { if captchaToken == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": captchaToken.") c.ResponseError(c.T("general:Missing parameter") + ": captchaToken.")
return return
} }
if clientSecret == "" { if clientSecret == "" {
c.ResponseError(c.T("verification:Missing parameter") + ": clientSecret.") c.ResponseError(c.T("general:Missing parameter") + ": clientSecret.")
return return
} }

View File

@ -35,7 +35,7 @@ func (c *ApiController) WebAuthnSignupBegin() {
webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host) webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host)
user := c.getCurrentUser() user := c.getCurrentUser()
if user == nil { if user == nil {
c.ResponseError(c.T("webauthn:Please login first")) c.ResponseError(c.T("general:Please login first"))
return return
} }
@ -66,7 +66,7 @@ func (c *ApiController) WebAuthnSignupFinish() {
webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host) webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host)
user := c.getCurrentUser() user := c.getCurrentUser()
if user == nil { if user == nil {
c.ResponseError(c.T("webauthn:Please login first")) c.ResponseError(c.T("general:Please login first"))
return return
} }
sessionObj := c.GetSession("registration") sessionObj := c.GetSession("registration")
@ -101,7 +101,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
userName := c.Input().Get("name") userName := c.Input().Get("name")
user := object.GetUserByFields(userOwner, userName) user := object.GetUserByFields(userOwner, userName)
if user == nil { 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 return
} }
if len(user.WebauthnCredentials) == 0 { if len(user.WebauthnCredentials) == 0 {

5
go.mod
View File

@ -13,7 +13,7 @@ require (
github.com/casdoor/go-sms-sender v0.5.1 github.com/casdoor/go-sms-sender v0.5.1
github.com/casdoor/oss v1.2.0 github.com/casdoor/oss v1.2.0
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc github.com/denisenkom/go-mssqldb v0.9.0
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b
github.com/forestmgy/ldapserver v1.1.0 github.com/forestmgy/ldapserver v1.1.0
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
@ -51,7 +51,8 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84
xorm.io/builder v0.3.12 // indirect xorm.io/builder v0.3.12 // indirect
xorm.io/core v0.7.2 xorm.io/core v0.7.2
xorm.io/xorm v1.0.5 xorm.io/xorm v1.1.2
) )

58
go.sum
View File

@ -126,10 +126,13 @@ github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWW
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk=
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b h1:L63RATZFZuFMXy6ixnKmv3eNAXwYQF6HW1vd4IYsQqQ= github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b h1:L63RATZFZuFMXy6ixnKmv3eNAXwYQF6HW1vd4IYsQqQ=
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b/go.mod h1:EYSpSkwoEcryMmQGfhol2IiB3IMN9IIIaNd/wcAQMGQ= github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b/go.mod h1:EYSpSkwoEcryMmQGfhol2IiB3IMN9IIIaNd/wcAQMGQ=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI= github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
@ -231,6 +234,7 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -279,14 +283,17 @@ github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9q
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -327,8 +334,11 @@ github.com/mattermost/xml-roundtrip-validator v0.0.0-20201208211235-fe770d50d911
github.com/mattermost/xml-roundtrip-validator v0.0.0-20201208211235-fe770d50d911/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To= github.com/mattermost/xml-roundtrip-validator v0.0.0-20201208211235-fe770d50d911/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI= github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
@ -384,6 +394,8 @@ github.com/qiangmzsx/string-adapter/v2 v2.1.0/go.mod h1:PElPB7b7HnGKTsuADAffFpOQ
github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk= github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk=
github.com/qiniu/go-sdk/v7 v7.12.1/go.mod h1:btsaOc8CA3hdVloULfFdDgDc+g4f3TDZEFsDY0BLE+w= github.com/qiniu/go-sdk/v7 v7.12.1/go.mod h1:btsaOc8CA3hdVloULfFdDgDc+g4f3TDZEFsDY0BLE+w=
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs= github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -506,6 +518,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -539,6 +552,7 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -561,6 +575,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -585,6 +600,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -599,7 +615,10 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -669,10 +688,13 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@ -795,14 +817,42 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009 h1:u0oCo5b9wyLr++HF3AN9JicGhkUxJhMz51+8TIZH9N0=
modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
modernc.org/ccgo/v3 v3.9.0 h1:JbcEIqjw4Agf+0g3Tc85YvfYqkkFOv6xBwS4zkfqSoA=
modernc.org/ccgo/v3 v3.9.0/go.mod h1:nQbgkn8mwzPdp4mm6BT6+p85ugQ7FrGgIcYaE7nSrpY=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/libc v1.8.0 h1:Pp4uv9g0csgBMpGPABKtkieF6O5MGhfGo6ZiOdlYfR8=
modernc.org/libc v1.8.0/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM=
modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84 h1:rgEUzE849tFlHSoeCrKyS9cZAljC+DY7MdMHKq6R6sY=
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84/go.mod h1:PGzq6qlhyYjL6uVbSgS6WoF7ZopTW/sI7+7p+mb4ZVU=
modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc=
modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
modernc.org/tcl v1.5.0 h1:euZSUNfE0Fd4W8VqXI1Ly1v7fqDJoBuAV88Ea+SnaSs=
modernc.org/tcl v1.5.0/go.mod h1:gb57hj4pO8fRrK54zveIfFXBaMHK3SKJNWcmRw1cRzc=
modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc=
modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.8/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.12 h1:ASZYX7fQmy+o8UJdhlLHSW57JDOkM8DNhcAF5d0LiJM= xorm.io/builder v0.3.12 h1:ASZYX7fQmy+o8UJdhlLHSW57JDOkM8DNhcAF5d0LiJM=
xorm.io/builder v0.3.12/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= xorm.io/builder v0.3.12/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw= xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM= xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
xorm.io/xorm v1.0.3/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4= xorm.io/xorm v1.0.3/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
xorm.io/xorm v1.0.5 h1:LRr5PfOUb4ODPR63YwbowkNDwcolT2LnkwP/TUaMaB0= xorm.io/xorm v1.1.2 h1:bje+1KZvK3m5AHtZNfUDlKEEyuw/IRHT+an0CLIG5TU=
xorm.io/xorm v1.0.5/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4= xorm.io/xorm v1.1.2/go.mod h1:Cb0DKYTHbyECMaSfgRnIZp5aiUgQozxcJJ0vzcLGJSg=

View File

@ -26,16 +26,22 @@ import (
type I18nData map[string]map[string]string type I18nData map[string]map[string]string
var reI18n *regexp.Regexp var (
reI18nFrontend *regexp.Regexp
reI18nBackendObject *regexp.Regexp
reI18nBackendController *regexp.Regexp
)
func init() { 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{} res := []string{}
matches := reI18n.FindAllStringSubmatch(fileContent, -1) matches := reI18nFrontend.FindAllStringSubmatch(fileContent, -1)
if matches == nil { if matches == nil {
return res return res
} }
@ -46,17 +52,39 @@ func getAllI18nStrings(fileContent string) []string {
return res return res
} }
func getAllJsFilePaths() []string { func getAllI18nStringsBackend(fileContent string, isObjectPackage bool) []string {
path := "../web/src"
res := []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 { func(path string, info os.FileInfo, err error) error {
if err != nil { if err != nil {
return err return err
} }
if !strings.HasSuffix(info.Name(), ".js") { if !strings.HasSuffix(info.Name(), fileSuffix) {
return nil return nil
} }
@ -71,12 +99,25 @@ func getAllJsFilePaths() []string {
return res 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{} allWords := []string{}
paths := getAllJsFilePaths()
for _, path := range paths { for _, path := range paths {
fileContent := util.ReadStringFromPath(path) 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...) allWords = append(allWords, words...)
} }
fmt.Printf("%v\n", allWords) 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 package i18n
import ( import (
"fmt"
"testing" "testing"
) )
func applyToOtherLanguage(dataEn *I18nData, lang string) { func applyToOtherLanguage(category string, language string, i18nData *I18nData) {
dataOther := readI18nFile(lang) newData := readI18nFile(category, language)
println(dataOther) println(newData)
applyData(dataEn, dataOther) applyData(i18nData, newData)
writeI18nFile(lang, dataEn) writeI18nFile(category, language, i18nData)
} }
func TestGenerateI18nStringsForFrontend(t *testing.T) { func TestGenerateI18nFrontend(t *testing.T) {
dataEn := parseToData() enData := parseEnData("frontend")
writeI18nFile("en", dataEn) writeI18nFile("frontend", "en", enData)
applyToOtherLanguage(dataEn, "de") applyToOtherLanguage("frontend", "de", enData)
applyToOtherLanguage(dataEn, "fr") applyToOtherLanguage("frontend", "es", enData)
applyToOtherLanguage(dataEn, "ja") applyToOtherLanguage("frontend", "fr", enData)
applyToOtherLanguage(dataEn, "ko") applyToOtherLanguage("frontend", "ja", enData)
applyToOtherLanguage(dataEn, "ru") applyToOtherLanguage("frontend", "ko", enData)
applyToOtherLanguage(dataEn, "zh") applyToOtherLanguage("frontend", "ru", enData)
applyToOtherLanguage("frontend", "zh", enData)
} }
func TestGenerateI18nStringsForBackend(t *testing.T) { func TestGenerateI18nBackend(t *testing.T) {
paths := getAllGoFilePaths() enData := parseEnData("backend")
writeI18nFile("backend", "en", enData)
errName := getErrName(paths) applyToOtherLanguage("backend", "de", enData)
applyToOtherLanguage("backend", "es", enData)
dataEn := getI18nJSONData(errName) applyToOtherLanguage("backend", "fr", enData)
applyToOtherLanguage("backend", "ja", enData)
writeI18nFile("backend_en", dataEn) applyToOtherLanguage("backend", "ko", enData)
applyToOtherLanguage("backend", "ru", enData)
applyToOtherLanguage(dataEn, "backend_de") applyToOtherLanguage("backend", "zh", enData)
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)
}
} }

View File

@ -4,31 +4,25 @@
"Get init score failed, error: %w": "Get init score failed, error: %w", "Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information", "Invalid information": "Invalid information",
"Phone: %s": "Phone: %s", "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", "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" "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"
},
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix", "%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "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 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", "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", "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", "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 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) 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 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 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 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 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 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.", "Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s" "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +47,8 @@
"Phone already exists": "Phone already exists", "Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again", "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 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.", "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 already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -65,20 +56,17 @@
"Username cannot start with a digit": "Username cannot start with a digit", "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 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", "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 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",
"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", "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" "unsupported password type: %s": "unsupported password type: %s"
}, },
"check_util": { "general": {
"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", "Missing parameter": "Missing parameter",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances" "Please login first": "Please login first",
}, "The user: %s doesn't exist": "The user: %s doesn't exist"
"enforcer": {
"Please sign in first": "Please sign in first"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist", "Ldap server exist": "Ldap server exist"
"Missing parameter": "Missing parameter"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Please link first",
@ -93,10 +81,6 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "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": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -120,22 +104,18 @@
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The 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": { "token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.", "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", "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"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Failed to import users"
@ -143,18 +123,14 @@
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "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", "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 provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "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.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
@ -165,8 +141,6 @@
}, },
"webauthn": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first", "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"
} }
} }

View File

@ -4,31 +4,25 @@
"Get init score failed, error: %w": "Get init score failed, error: %w", "Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information", "Invalid information": "Invalid information",
"Phone: %s": "Phone: %s", "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", "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" "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"
},
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix", "%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "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 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", "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", "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", "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 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) 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 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 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 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 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 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.", "Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s" "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +47,8 @@
"Phone already exists": "Phone already exists", "Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again", "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 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.", "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 already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -65,20 +56,17 @@
"Username cannot start with a digit": "Username cannot start with a digit", "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 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", "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 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",
"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", "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" "unsupported password type: %s": "unsupported password type: %s"
}, },
"check_util": { "general": {
"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", "Missing parameter": "Missing parameter",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances" "Please login first": "Please login first",
}, "The user: %s doesn't exist": "The user: %s doesn't exist"
"enforcer": {
"Please sign in first": "Please sign in first"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist", "Ldap server exist": "Ldap server exist"
"Missing parameter": "Missing parameter"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Please link first",
@ -93,10 +81,6 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "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": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -120,22 +104,18 @@
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The 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": { "token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.", "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", "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"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Failed to import users"
@ -143,18 +123,14 @@
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "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", "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 provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "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.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
@ -165,8 +141,6 @@
}, },
"webauthn": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first", "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"
} }
} }

View File

@ -4,31 +4,25 @@
"Get init score failed, error: %w": "Get init score failed, error: %w", "Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information", "Invalid information": "Invalid information",
"Phone: %s": "Phone: %s", "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", "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" "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"
},
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix", "%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "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 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", "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", "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", "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 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) 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 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 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 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 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 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.", "Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s" "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +47,8 @@
"Phone already exists": "Phone already exists", "Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again", "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 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.", "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 already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -65,20 +56,17 @@
"Username cannot start with a digit": "Username cannot start with a digit", "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 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", "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 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",
"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", "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" "unsupported password type: %s": "unsupported password type: %s"
}, },
"check_util": { "general": {
"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", "Missing parameter": "Missing parameter",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances" "Please login first": "Please login first",
}, "The user: %s doesn't exist": "The user: %s doesn't exist"
"enforcer": {
"Please sign in first": "Please sign in first"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist", "Ldap server exist": "Ldap server exist"
"Missing parameter": "Missing parameter"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Please link first",
@ -93,10 +81,6 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "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": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -120,22 +104,18 @@
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The 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": { "token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.", "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", "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"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Failed to import users"
@ -143,18 +123,14 @@
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "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", "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 provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "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.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
@ -165,8 +141,6 @@
}, },
"webauthn": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first", "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"
} }
} }

View File

@ -4,31 +4,25 @@
"Get init score failed, error: %w": "Get init score failed, error: %w", "Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information", "Invalid information": "Invalid information",
"Phone: %s": "Phone: %s", "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", "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" "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"
},
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix", "%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "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 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", "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", "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", "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 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) 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 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 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 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 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 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.", "Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s" "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +47,8 @@
"Phone already exists": "Phone already exists", "Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again", "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 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.", "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 already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -65,20 +56,17 @@
"Username cannot start with a digit": "Username cannot start with a digit", "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 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", "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 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",
"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", "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" "unsupported password type: %s": "unsupported password type: %s"
}, },
"check_util": { "general": {
"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", "Missing parameter": "Missing parameter",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances" "Please login first": "Please login first",
}, "The user: %s doesn't exist": "The user: %s doesn't exist"
"enforcer": {
"Please sign in first": "Please sign in first"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist", "Ldap server exist": "Ldap server exist"
"Missing parameter": "Missing parameter"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Please link first",
@ -93,10 +81,6 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "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": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -120,22 +104,18 @@
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The 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": { "token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.", "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", "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"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Failed to import users"
@ -143,18 +123,14 @@
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "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", "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 provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "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.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
@ -165,8 +141,6 @@
}, },
"webauthn": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first", "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"
} }
} }

View File

@ -4,31 +4,25 @@
"Get init score failed, error: %w": "Get init score failed, error: %w", "Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information", "Invalid information": "Invalid information",
"Phone: %s": "Phone: %s", "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", "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" "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"
},
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix", "%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "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 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", "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", "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", "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 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) 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 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 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 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 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 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.", "Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s" "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +47,8 @@
"Phone already exists": "Phone already exists", "Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again", "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 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.", "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 already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -65,20 +56,17 @@
"Username cannot start with a digit": "Username cannot start with a digit", "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 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", "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 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",
"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", "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" "unsupported password type: %s": "unsupported password type: %s"
}, },
"check_util": { "general": {
"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", "Missing parameter": "Missing parameter",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances" "Please login first": "Please login first",
}, "The user: %s doesn't exist": "The user: %s doesn't exist"
"enforcer": {
"Please sign in first": "Please sign in first"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist", "Ldap server exist": "Ldap server exist"
"Missing parameter": "Missing parameter"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Please link first",
@ -93,10 +81,6 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "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": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -120,22 +104,18 @@
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The 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": { "token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.", "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", "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"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Failed to import users"
@ -143,18 +123,14 @@
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "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", "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 provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "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.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
@ -165,8 +141,6 @@
}, },
"webauthn": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first", "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"
} }
} }

View File

@ -4,31 +4,25 @@
"Get init score failed, error: %w": "Get init score failed, error: %w", "Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information", "Invalid information": "Invalid information",
"Phone: %s": "Phone: %s", "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", "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" "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"
},
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix", "%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "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 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", "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", "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", "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 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) 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 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 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 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 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 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.", "Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s" "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +47,8 @@
"Phone already exists": "Phone already exists", "Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again", "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 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.", "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 already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -65,20 +56,17 @@
"Username cannot start with a digit": "Username cannot start with a digit", "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 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", "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 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",
"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", "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" "unsupported password type: %s": "unsupported password type: %s"
}, },
"check_util": { "general": {
"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", "Missing parameter": "Missing parameter",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances" "Please login first": "Please login first",
}, "The user: %s doesn't exist": "The user: %s doesn't exist"
"enforcer": {
"Please sign in first": "Please sign in first"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist", "Ldap server exist": "Ldap server exist"
"Missing parameter": "Missing parameter"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Please link first",
@ -93,10 +81,6 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "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": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -120,22 +104,18 @@
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The 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": { "token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.", "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", "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"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Failed to import users"
@ -143,18 +123,14 @@
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "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", "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 provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "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.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
@ -165,8 +141,6 @@
}, },
"webauthn": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first", "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"
} }
} }

View File

@ -4,31 +4,25 @@
"Get init score failed, error: %w": "Get init score failed, error: %w", "Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information", "Invalid information": "Invalid information",
"Phone: %s": "Phone: %s", "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", "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" "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"
},
"auth": { "auth": {
"%s No phone prefix": "%s No phone prefix", "%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256", "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 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", "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", "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", "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 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) 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 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 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 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 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 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.", "Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s" "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
@ -53,11 +47,8 @@
"Phone already exists": "Phone already exists", "Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty", "Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again", "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 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.", "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 already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -65,20 +56,17 @@
"Username cannot start with a digit": "Username cannot start with a digit", "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 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", "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 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",
"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", "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" "unsupported password type: %s": "unsupported password type: %s"
}, },
"check_util": { "general": {
"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", "Missing parameter": "Missing parameter",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances" "Please login first": "Please login first",
}, "The user: %s doesn't exist": "The user: %s doesn't exist"
"enforcer": {
"Please sign in first": "Please sign in first"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist", "Ldap server exist": "Ldap server exist"
"Missing parameter": "Missing parameter"
}, },
"link": { "link": {
"Please link first": "Please link first", "Please link first": "Please link first",
@ -93,10 +81,6 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "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": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -120,22 +104,18 @@
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed", "The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported" "The 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": { "token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret", "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list" "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.", "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", "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"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Failed to import users"
@ -143,18 +123,14 @@
"util": { "util": {
"No application is found for userId: %s": "No application is found for userId: %s", "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", "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 provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
}, },
"verification": { "verification": {
"Code has not been sent yet!": "Code has not been sent yet!", "Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid", "Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.", "Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist", "Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid", "Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.", "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.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
@ -165,8 +141,6 @@
}, },
"webauthn": { "webauthn": {
"Found no credentials for this user": "Found no credentials for this user", "Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first", "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"
} }
} }

View File

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

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

View File

@ -25,10 +25,10 @@ import (
_ "github.com/denisenkom/go-mssqldb" // db = mssql _ "github.com/denisenkom/go-mssqldb" // db = mssql
_ "github.com/go-sql-driver/mysql" // db = mysql _ "github.com/go-sql-driver/mysql" // db = mysql
_ "github.com/lib/pq" // db = postgres _ "github.com/lib/pq" // db = postgres
"xorm.io/xorm/migrate" _ "modernc.org/sqlite" // db = sqlite
//_ "github.com/mattn/go-sqlite3" // db = sqlite3
"xorm.io/core" "xorm.io/core"
"xorm.io/xorm" "xorm.io/xorm"
"xorm.io/xorm/migrate"
) )
var adapter *Adapter var adapter *Adapter
@ -222,6 +222,11 @@ func (a *Adapter) createTable() {
if err != nil { if err != nil {
panic(err) 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 { func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {

View File

@ -50,6 +50,7 @@ type Application struct {
EnableCodeSignin bool `json:"enableCodeSignin"` EnableCodeSignin bool `json:"enableCodeSignin"`
EnableSamlCompress bool `json:"enableSamlCompress"` EnableSamlCompress bool `json:"enableSamlCompress"`
EnableWebAuthn bool `json:"enableWebAuthn"` EnableWebAuthn bool `json:"enableWebAuthn"`
EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"` SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"` Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"` SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`
@ -286,7 +287,8 @@ func GetMaskedApplications(applications []*Application, userId string) []*Applic
func UpdateApplication(id string, application *Application) bool { func UpdateApplication(id string, application *Application) bool {
owner, name := util.GetOwnerAndNameFromId(id) owner, name := util.GetOwnerAndNameFromId(id)
if getApplication(owner, name) == nil { oldApplication := getApplication(owner, name)
if oldApplication == nil {
return false return false
} }
@ -301,6 +303,10 @@ func UpdateApplication(id string, application *Application) bool {
} }
} }
if oldApplication.ClientId != application.ClientId && GetApplicationByClientId(application.ClientId) != nil {
return false
}
for _, providerItem := range application.Providers { for _, providerItem := range application.Providers {
providerItem.Provider = nil providerItem.Provider = nil
} }
@ -324,6 +330,9 @@ func AddApplication(application *Application) bool {
if application.ClientSecret == "" { if application.ClientSecret == "" {
application.ClientSecret = util.GenerateClientSecret() application.ClientSecret = util.GenerateClientSecret()
} }
if GetApplicationByClientId(application.ClientId) != nil {
return false
}
for _, providerItem := range application.Providers { for _, providerItem := range application.Providers {
providerItem.Provider = nil providerItem.Provider = nil
} }

View File

@ -60,8 +60,8 @@ func CheckUserSignup(application *Application, organization *Organization, usern
if reWhiteSpace.MatchString(username) { if reWhiteSpace.MatchString(username) {
return i18n.Translate(lang, "check:Username cannot contain white spaces") return i18n.Translate(lang, "check:Username cannot contain white spaces")
} }
msg := CheckUsername(username, lang)
if msg != "" { if msg := CheckUsername(username, lang); msg != "" {
return msg return msg
} }
@ -143,11 +143,11 @@ func checkSigninErrorTimes(user *User, lang string) string {
if user.SigninWrongTimes >= SigninWrongTimesLimit { if user.SigninWrongTimes >= SigninWrongTimesLimit {
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime) lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
passedTime := time.Now().UTC().Sub(lastSignWrongTime) 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 // 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 { if minutes > 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) 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 // 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) { func CheckUserPassword(organization string, username string, password string, lang string) (*User, string) {
user := GetUserByFields(organization, username) user := GetUserByFields(organization, username)
if user == nil || user.IsDeleted == true { 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 { if user.IsForbidden {
@ -254,13 +254,13 @@ func filterField(field string) bool {
func CheckUserPermission(requestUserId, userId, userOwner string, strict bool, lang string) (bool, error) { func CheckUserPermission(requestUserId, userId, userOwner string, strict bool, lang string) (bool, error) {
if requestUserId == "" { 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 != "" { if userId != "" {
targetUser := GetUser(userId) targetUser := GetUser(userId)
if targetUser == nil { 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 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) { func CheckAccessPermission(userId string, application *Application) (bool, error) {
@ -342,6 +342,33 @@ func CheckUsername(username string, lang string) string {
return "" return ""
} }
func CheckUpdateUser(oldUser *User, user *User, lang string) string {
if user.DisplayName == "" {
return i18n.Translate(lang, "user:Display name cannot be empty")
}
if oldUser.Name != user.Name {
if msg := CheckUsername(user.Name, lang); msg != "" {
return msg
}
if HasUserByField(user.Owner, "name", user.Name) {
return i18n.Translate(lang, "check:Username already exists")
}
}
if oldUser.Email != user.Email {
if HasUserByField(user.Name, "email", user.Email) {
return i18n.Translate(lang, "check:Email already exists")
}
}
if oldUser.Phone != user.Phone {
if HasUserByField(user.Owner, "phone", user.Phone) {
return i18n.Translate(lang, "check:Phone already exists")
}
}
return ""
}
func CheckToEnableCaptcha(application *Application) bool { func CheckToEnableCaptcha(application *Application) bool {
if len(application.Providers) == 0 { if len(application.Providers) == 0 {
return false return false

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) UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin)
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
if leftChances > 0 { 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 // 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

@ -68,14 +68,14 @@ func getModel(owner string, name string) *Model {
return nil return nil
} }
model := Model{Owner: owner, Name: name} m := Model{Owner: owner, Name: name}
existed, err := adapter.Engine.Get(&model) existed, err := adapter.Engine.Get(&m)
if err != nil { if err != nil {
panic(err) panic(err)
} }
if existed { if existed {
return &model return &m
} else { } else {
return nil return nil
} }
@ -152,3 +152,7 @@ func modelChangeTrigger(oldName string, newName string) error {
return session.Commit() return session.Commit()
} }
func HasRoleDefinition(m model.Model) bool {
return m["g"] != nil
}

View File

@ -40,6 +40,7 @@ type OidcDiscovery struct {
ClaimsSupported []string `json:"claims_supported"` ClaimsSupported []string `json:"claims_supported"`
RequestParameterSupported bool `json:"request_parameter_supported"` RequestParameterSupported bool `json:"request_parameter_supported"`
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"` RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"`
EndSessionEndpoint string `json:"end_session_endpoint"`
} }
func getOriginFromHost(host string) (string, string) { func getOriginFromHost(host string) (string, string) {
@ -76,7 +77,7 @@ func GetOidcDiscovery(host string) OidcDiscovery {
JwksUri: fmt.Sprintf("%s/.well-known/jwks", originBackend), JwksUri: fmt.Sprintf("%s/.well-known/jwks", originBackend),
IntrospectionEndpoint: fmt.Sprintf("%s/api/login/oauth/introspect", 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"}, 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"}, GrantTypesSupported: []string{"password", "authorization_code"},
SubjectTypesSupported: []string{"public"}, SubjectTypesSupported: []string{"public"},
IdTokenSigningAlgValuesSupported: []string{"RS256"}, IdTokenSigningAlgValuesSupported: []string{"RS256"},
@ -84,6 +85,7 @@ func GetOidcDiscovery(host string) OidcDiscovery {
ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isGlobalAdmin", "isForbidden", "signupApplication", "ldap"}, ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isGlobalAdmin", "isForbidden", "signupApplication", "ldap"},
RequestParameterSupported: true, RequestParameterSupported: true,
RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"}, RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"},
EndSessionEndpoint: fmt.Sprintf("%s/api/logout", originBackend),
} }
return oidcDiscovery return oidcDiscovery

View File

@ -204,7 +204,7 @@ func GetAccountItemByName(name string, organization *Organization) *AccountItem
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User, lang string) (bool, string) { func CheckAccountItemModifyRule(accountItem *AccountItem, user *User, lang string) (bool, string) {
switch accountItem.ModifyRule { switch accountItem.ModifyRule {
case "Admin": 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) return false, fmt.Sprintf(i18n.Translate(lang, "organization:Only admin can modify the %s."), accountItem.Name)
} }
case "Immutable": case "Immutable":

View File

@ -57,6 +57,40 @@ type PermissionRule struct {
Id string `xorm:"varchar(100) index not null default ''" json:"id"` Id string `xorm:"varchar(100) index not null default ''" json:"id"`
} }
const (
builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
builtInAdapter = "permission_rule"
)
func (p *Permission) GetId() string {
return util.GetId(p.Owner, p.Name)
}
func (p *PermissionRule) GetRequest(adapterName string, permissionId string) ([]interface{}, error) {
request := []interface{}{p.V0, p.V1, p.V2}
if p.V3 != "" {
request = append(request, p.V3)
}
if p.V4 != "" {
request = append(request, p.V4)
}
if adapterName == builtInAdapter {
if p.V5 != "" {
return nil, fmt.Errorf("too many parameters. The maximum parameter number cannot exceed %d", builtInAvailableField)
}
request = append(request, permissionId)
return request, nil
} else {
if p.V5 != "" {
request = append(request, p.V5)
}
return request, nil
}
}
func GetPermissionCount(owner, field, value string) int { func GetPermissionCount(owner, field, value string) int {
session := GetSession(owner, -1, -1, field, value, "", "") session := GetSession(owner, -1, -1, field, value, "", "")
count, err := session.Count(&Permission{}) count, err := session.Count(&Permission{})
@ -115,19 +149,25 @@ func GetPermission(id string) *Permission {
func checkPermissionValid(permission *Permission) { func checkPermissionValid(permission *Permission) {
enforcer := getEnforcer(permission) enforcer := getEnforcer(permission)
enforcer.EnableAutoSave(false) enforcer.EnableAutoSave(false)
policies, groupingPolicies := getPolicies(permission)
policies := getPolicies(permission)
_, err := enforcer.AddPolicies(policies)
if err != nil {
panic(err)
}
if !HasRoleDefinition(enforcer.GetModel()) {
permission.Roles = []string{}
return
}
groupingPolicies := getGroupingPolicies(permission)
if len(groupingPolicies) > 0 { if len(groupingPolicies) > 0 {
_, err := enforcer.AddGroupingPolicies(groupingPolicies) _, err := enforcer.AddGroupingPolicies(groupingPolicies)
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
_, err := enforcer.AddPolicies(policies)
if err != nil {
panic(err)
}
} }
func UpdatePermission(id string, permission *Permission) bool { func UpdatePermission(id string, permission *Permission) bool {
@ -144,6 +184,7 @@ func UpdatePermission(id string, permission *Permission) bool {
} }
if affected != 0 { if affected != 0 {
removeGroupingPolicies(oldPermission)
removePolicies(oldPermission) removePolicies(oldPermission)
if oldPermission.Adapter != "" && oldPermission.Adapter != permission.Adapter { if oldPermission.Adapter != "" && oldPermission.Adapter != permission.Adapter {
isEmpty, _ := adapter.Engine.IsTableEmpty(oldPermission.Adapter) isEmpty, _ := adapter.Engine.IsTableEmpty(oldPermission.Adapter)
@ -154,6 +195,7 @@ func UpdatePermission(id string, permission *Permission) bool {
} }
} }
} }
addGroupingPolicies(permission)
addPolicies(permission) addPolicies(permission)
} }
@ -167,6 +209,7 @@ func AddPermission(permission *Permission) bool {
} }
if affected != 0 { if affected != 0 {
addGroupingPolicies(permission)
addPolicies(permission) addPolicies(permission)
} }
@ -180,6 +223,7 @@ func DeletePermission(permission *Permission) bool {
} }
if affected != 0 { if affected != 0 {
removeGroupingPolicies(permission)
removePolicies(permission) removePolicies(permission)
if permission.Adapter != "" && permission.Adapter != "permission_rule" { if permission.Adapter != "" && permission.Adapter != "permission_rule" {
isEmpty, _ := adapter.Engine.IsTableEmpty(permission.Adapter) isEmpty, _ := adapter.Engine.IsTableEmpty(permission.Adapter)
@ -195,10 +239,6 @@ func DeletePermission(permission *Permission) bool {
return affected != 0 return affected != 0
} }
func (permission *Permission) GetId() string {
return fmt.Sprintf("%s/%s", permission.Owner, permission.Name)
}
func GetPermissionsByUser(userId string) []*Permission { func GetPermissionsByUser(userId string) []*Permission {
permissions := []*Permission{} permissions := []*Permission{}
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&permissions) err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&permissions)
@ -269,3 +309,12 @@ func ContainsAsterisk(userId string, users []string) bool {
return containsAsterisk return containsAsterisk
} }
func GetMaskedPermissions(permissions []*Permission) []*Permission {
for _, permission := range permissions {
permission.Users = nil
permission.Submitter = ""
}
return permissions
}

View File

@ -15,9 +15,11 @@
package object package object
import ( import (
"fmt"
"strings" "strings"
"github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/config"
"github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v3" xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
@ -36,47 +38,48 @@ func getEnforcer(permission *Permission) *casbin.Enforcer {
panic(err) panic(err)
} }
modelText := `
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act, "", "", permissionId
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`
permissionModel := getModel(permission.Owner, permission.Model) permissionModel := getModel(permission.Owner, permission.Model)
m := model.Model{}
if permissionModel != nil { if permissionModel != nil {
modelText = permissionModel.ModelText m, err = GetBuiltInModel(permissionModel.ModelText)
} else {
m, err = GetBuiltInModel("")
} }
m, err := model.NewModelFromString(modelText)
if err != nil { if err != nil {
panic(err) panic(err)
} }
policyFilter := xormadapter.Filter{}
if !HasRoleDefinition(m) {
policyFilter.Ptype = []string{"p"}
err = adapter.LoadFilteredPolicy(m, policyFilter)
if err != nil {
panic(err)
}
}
enforcer, err := casbin.NewEnforcer(m, adapter) enforcer, err := casbin.NewEnforcer(m, adapter)
if err != nil { if err != nil {
panic(err) panic(err)
} }
// load Policy with a specific Permission // load Policy with a specific Permission
enforcer.LoadFilteredPolicy(xormadapter.Filter{ policyFilter.V5 = []string{permission.GetId()}
V5: []string{permission.Owner + "/" + permission.Name}, err = enforcer.LoadFilteredPolicy(policyFilter)
}) if err != nil {
panic(err)
}
return enforcer return enforcer
} }
func getPolicies(permission *Permission) ([][]string, [][]string) { func getPolicies(permission *Permission) [][]string {
var policies [][]string var policies [][]string
var groupingPolicies [][]string
permissionId := permission.Owner + "/" + permission.Name permissionId := permission.GetId()
domainExist := len(permission.Domains) > 0 domainExist := len(permission.Domains) > 0
for _, user := range permission.Users { for _, user := range permission.Users {
for _, resource := range permission.Resources { for _, resource := range permission.Resources {
for _, action := range permission.Actions { for _, action := range permission.Actions {
@ -90,26 +93,8 @@ func getPolicies(permission *Permission) ([][]string, [][]string) {
} }
} }
} }
for _, role := range permission.Roles { for _, role := range permission.Roles {
roleObj := GetRole(role)
for _, subUser := range roleObj.Users {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subUser, domain, role, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subUser, role, "", "", "", permissionId})
}
}
for _, subRole := range roleObj.Roles {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subRole, domain, role, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subRole, role, "", "", "", permissionId})
}
}
for _, resource := range permission.Resources { for _, resource := range permission.Resources {
for _, action := range permission.Actions { for _, action := range permission.Actions {
if domainExist { if domainExist {
@ -122,19 +107,49 @@ func getPolicies(permission *Permission) ([][]string, [][]string) {
} }
} }
} }
return policies, groupingPolicies
return policies
}
func getGroupingPolicies(permission *Permission) [][]string {
var groupingPolicies [][]string
domainExist := len(permission.Domains) > 0
permissionId := permission.GetId()
for _, role := range permission.Roles {
roleObj := GetRole(role)
if roleObj == nil {
continue
}
for _, subUser := range roleObj.Users {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subUser, domain, role, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subUser, role, "", "", "", permissionId})
}
}
for _, subRole := range roleObj.Roles {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subRole, domain, role, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subRole, role, "", "", "", permissionId})
}
}
}
return groupingPolicies
} }
func addPolicies(permission *Permission) { func addPolicies(permission *Permission) {
enforcer := getEnforcer(permission) enforcer := getEnforcer(permission)
policies, groupingPolicies := getPolicies(permission) policies := getPolicies(permission)
if len(groupingPolicies) > 0 {
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
_, err := enforcer.AddPolicies(policies) _, err := enforcer.AddPolicies(policies)
if err != nil { if err != nil {
@ -142,9 +157,21 @@ func addPolicies(permission *Permission) {
} }
} }
func removePolicies(permission *Permission) { func addGroupingPolicies(permission *Permission) {
enforcer := getEnforcer(permission) enforcer := getEnforcer(permission)
policies, groupingPolicies := getPolicies(permission) groupingPolicies := getGroupingPolicies(permission)
if len(groupingPolicies) > 0 {
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
}
func removeGroupingPolicies(permission *Permission) {
enforcer := getEnforcer(permission)
groupingPolicies := getGroupingPolicies(permission)
if len(groupingPolicies) > 0 { if len(groupingPolicies) > 0 {
_, err := enforcer.RemoveGroupingPolicies(groupingPolicies) _, err := enforcer.RemoveGroupingPolicies(groupingPolicies)
@ -152,6 +179,11 @@ func removePolicies(permission *Permission) {
panic(err) panic(err)
} }
} }
}
func removePolicies(permission *Permission) {
enforcer := getEnforcer(permission)
policies := getPolicies(permission)
_, err := enforcer.RemovePolicies(policies) _, err := enforcer.RemovePolicies(policies)
if err != nil { if err != nil {
@ -163,10 +195,8 @@ func Enforce(permissionRule *PermissionRule) bool {
permission := GetPermission(permissionRule.Id) permission := GetPermission(permissionRule.Id)
enforcer := getEnforcer(permission) enforcer := getEnforcer(permission)
request := []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2} request, _ := permissionRule.GetRequest(builtInAdapter, permissionRule.Id)
if permissionRule.V3 != "" {
request = append(request, permissionRule.V3)
}
allow, err := enforcer.Enforce(request...) allow, err := enforcer.Enforce(request...)
if err != nil { if err != nil {
panic(err) panic(err)
@ -177,11 +207,8 @@ func Enforce(permissionRule *PermissionRule) bool {
func BatchEnforce(permissionRules []PermissionRule) []bool { func BatchEnforce(permissionRules []PermissionRule) []bool {
var requests [][]interface{} var requests [][]interface{}
for _, permissionRule := range permissionRules { for _, permissionRule := range permissionRules {
if permissionRule.V3 != "" { request, _ := permissionRule.GetRequest(builtInAdapter, permissionRule.Id)
requests = append(requests, []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2, permissionRule.V3}) requests = append(requests, request)
} else {
requests = append(requests, []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2})
}
} }
permission := GetPermission(permissionRules[0].Id) permission := GetPermission(permissionRules[0].Id)
enforcer := getEnforcer(permission) enforcer := getEnforcer(permission)
@ -226,3 +253,46 @@ func GetAllRoles(userId string) []string {
} }
return res return res
} }
func GetBuiltInModel(modelText string) (model.Model, error) {
if modelText == "" {
modelText = `
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act, "", "", permissionId
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`
return model.NewModelFromString(modelText)
} else {
cfg, err := config.NewConfigFromText(modelText)
if err != nil {
return nil, err
}
// load [policy_definition]
policyDefinition := strings.Split(cfg.String("policy_definition::p"), ",")
fieldsNum := len(policyDefinition)
if fieldsNum > builtInAvailableField {
panic(fmt.Errorf("the maximum policy_definition field number cannot exceed %d", builtInAvailableField))
}
// filled empty field with "" and V5 with "permissionId"
for i := builtInAvailableField - fieldsNum; i > 0; i-- {
policyDefinition = append(policyDefinition, "")
}
policyDefinition = append(policyDefinition, "permissionId")
m, _ := model.NewModelFromString(modelText)
m.AddDef("p", "p", strings.Join(policyDefinition, ","))
return m, err
}
}

View File

@ -95,6 +95,12 @@ func UpdateRole(id string, role *Role) bool {
return false return false
} }
permissions := GetPermissionsByRole(id)
for _, permission := range permissions {
removeGroupingPolicies(permission)
removePolicies(permission)
}
if name != role.Name { if name != role.Name {
err := roleChangeTrigger(name, role.Name) err := roleChangeTrigger(name, role.Name)
if err != nil { if err != nil {
@ -107,6 +113,13 @@ func UpdateRole(id string, role *Role) bool {
panic(err) panic(err)
} }
newRoleID := role.GetId()
permissions = GetPermissionsByRole(newRoleID)
for _, permission := range permissions {
addGroupingPolicies(permission)
addPolicies(permission)
}
return affected != 0 return affected != 0
} }
@ -120,6 +133,13 @@ func AddRole(role *Role) bool {
} }
func DeleteRole(role *Role) bool { func DeleteRole(role *Role) bool {
roleId := role.GetId()
permissions := GetPermissionsByRole(roleId)
for _, permission := range permissions {
permission.Roles = util.DeleteVal(permission.Roles, roleId)
UpdatePermission(permission.GetId(), permission)
}
affected, err := adapter.Engine.ID(core.PK{role.Owner, role.Name}).Delete(&Role{}) affected, err := adapter.Engine.ID(core.PK{role.Owner, role.Name}).Delete(&Role{})
if err != nil { if err != nil {
panic(err) panic(err)
@ -192,3 +212,11 @@ func roleChangeTrigger(oldName string, newName string) error {
return session.Commit() 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 // GetSamlResponse generates a SAML2.0 response
// parameter samlRequest is saml request in base64 format // 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 // base64 decode
defated, err := base64.StdEncoding.DecodeString(samlRequest) defated, err := base64.StdEncoding.DecodeString(samlRequest)
if err != nil { if err != nil {
return "", "", fmt.Errorf("err: %s", err.Error()) return "", "", method, fmt.Errorf("err: %s", err.Error())
} }
// decompress // decompress
var buffer bytes.Buffer var buffer bytes.Buffer
@ -236,12 +239,12 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
var authnRequest saml.AuthnRequest var authnRequest saml.AuthnRequest
err = xml.Unmarshal(buffer.Bytes(), &authnRequest) err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
if err != nil { if err != nil {
return "", "", fmt.Errorf("err: %s", err.Error()) return "", "", method, fmt.Errorf("err: %s", err.Error())
} }
// verify samlRequest // verify samlRequest
if isValid := application.IsRedirectUriValid(authnRequest.Issuer.Url); !isValid { 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 // get certificate string
@ -253,6 +256,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
// redirect Url (Assertion Consumer Url) // redirect Url (Assertion Consumer Url)
if application.SamlReplyUrl != "" { if application.SamlReplyUrl != "" {
method = "POST"
authnRequest.AssertionConsumerServiceURL = application.SamlReplyUrl authnRequest.AssertionConsumerServiceURL = application.SamlReplyUrl
} }
@ -275,7 +279,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
doc.SetRoot(samlResponse) doc.SetRoot(samlResponse)
xmlBytes, err := doc.WriteToBytes() xmlBytes, err := doc.WriteToBytes()
if err != nil { if err != nil {
return "", "", fmt.Errorf("err: %s", err.Error()) return "", "", method, fmt.Errorf("err: %s", err.Error())
} }
// compress // compress
@ -283,7 +287,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
flated := bytes.NewBuffer(nil) flated := bytes.NewBuffer(nil)
writer, err := flate.NewWriter(flated, flate.DefaultCompression) writer, err := flate.NewWriter(flated, flate.DefaultCompression)
if err != nil { if err != nil {
return "", "", fmt.Errorf("err: %s", err.Error()) return "", "", method, fmt.Errorf("err: %s", err.Error())
} }
writer.Write(xmlBytes) writer.Write(xmlBytes)
writer.Close() writer.Close()
@ -291,7 +295,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
} }
// base64 encode // base64 encode
res := base64.StdEncoding.EncodeToString(xmlBytes) 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) // NewSamlResponse11 return a saml1.1 response(not 2.0)

130
object/session.go Normal file
View File

@ -0,0 +1,130 @@
// 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 (
"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 = util.GetCurrentTime()
_, 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.GetOwnerAndNameFromId(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

@ -27,7 +27,7 @@ import (
) )
const ( const (
hourSeconds = 3600 hourMinutes = 60
InvalidRequest = "invalid_request" InvalidRequest = "invalid_request"
InvalidClient = "invalid_client" InvalidClient = "invalid_client"
InvalidGrant = "invalid_grant" InvalidGrant = "invalid_grant"
@ -204,7 +204,7 @@ func DeleteToken(token *Token) bool {
return affected != 0 return affected != 0
} }
func DeleteTokenByAccessToken(accessToken string) (bool, *Application) { func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token) {
token := Token{AccessToken: accessToken} token := Token{AccessToken: accessToken}
existed, err := adapter.Engine.Get(&token) existed, err := adapter.Engine.Get(&token)
if err != nil { if err != nil {
@ -212,15 +212,17 @@ func DeleteTokenByAccessToken(accessToken string) (bool, *Application) {
} }
if !existed { if !existed {
return false, nil return false, nil, nil
} }
application := getApplication(token.Owner, token.Application)
affected, err := adapter.Engine.Where("access_token=?", accessToken).Delete(&Token{}) token.ExpiresIn = 0
affected, err := adapter.Engine.ID(core.PK{token.Owner, token.Name}).Cols("expires_in").Update(&token)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return affected != 0, application application := getApplication(token.Owner, token.Application)
return affected != 0, application, &token
} }
func GetTokenByAccessToken(accessToken string) *Token { func GetTokenByAccessToken(accessToken string) *Token {
@ -265,7 +267,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
user := GetUser(userId) user := GetUser(userId)
if user == nil { if user == nil {
return &Code{ 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: "", Code: "",
} }
} }
@ -304,7 +306,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
Code: util.GenerateClientId(), Code: util.GenerateClientId(),
AccessToken: accessToken, AccessToken: accessToken,
RefreshToken: refreshToken, RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds, ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope, Scope: scope,
TokenType: "Bearer", TokenType: "Bearer",
CodeChallenge: challenge, CodeChallenge: challenge,
@ -438,7 +440,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
Code: util.GenerateClientId(), Code: util.GenerateClientId(),
AccessToken: newAccessToken, AccessToken: newAccessToken,
RefreshToken: newRefreshToken, RefreshToken: newRefreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds, ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope, Scope: scope,
TokenType: "Bearer", TokenType: "Bearer",
} }
@ -588,7 +590,7 @@ func GetPasswordToken(application *Application, username string, password string
Code: util.GenerateClientId(), Code: util.GenerateClientId(),
AccessToken: accessToken, AccessToken: accessToken,
RefreshToken: refreshToken, RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds, ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope, Scope: scope,
TokenType: "Bearer", TokenType: "Bearer",
CodeIsUsed: true, CodeIsUsed: true,
@ -628,7 +630,7 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
User: nullUser.Name, User: nullUser.Name,
Code: util.GenerateClientId(), Code: util.GenerateClientId(),
AccessToken: accessToken, AccessToken: accessToken,
ExpiresIn: application.ExpireInHours * hourSeconds, ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope, Scope: scope,
TokenType: "Bearer", TokenType: "Bearer",
CodeIsUsed: true, CodeIsUsed: true,
@ -655,7 +657,7 @@ func GetTokenByUser(application *Application, user *User, scope string, host str
Code: util.GenerateClientId(), Code: util.GenerateClientId(),
AccessToken: accessToken, AccessToken: accessToken,
RefreshToken: refreshToken, RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds, ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope, Scope: scope,
TokenType: "Bearer", TokenType: "Bearer",
CodeIsUsed: true, CodeIsUsed: true,

View File

@ -110,8 +110,8 @@ type User struct {
Ldap string `xorm:"ldap varchar(100)" json:"ldap"` Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
Properties map[string]string `json:"properties"` Properties map[string]string `json:"properties"`
Roles []*Role `json:"roles"` Roles []*Role `xorm:"-" json:"roles"`
Permissions []*Permission `json:"permissions"` Permissions []*Permission `xorm:"-" json:"permissions"`
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"` LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"` SigninWrongTimes int `json:"signinWrongTimes"`
@ -529,6 +529,9 @@ func AddUsersInBatch(users []*User) bool {
} }
func DeleteUser(user *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{}) affected, err := adapter.Engine.ID(core.PK{user.Owner, user.Name}).Delete(&User{})
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -26,6 +26,10 @@ import (
"xorm.io/core" "xorm.io/core"
) )
const (
wrongCode = "wrongCode"
)
type VerificationRecord struct { type VerificationRecord struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"` Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"` Name string `xorm:"varchar(100) notnull pk" json:"name"`
@ -167,7 +171,7 @@ func CheckVerificationCode(dest, code, lang string) string {
} }
if record.Code != code { if record.Code != code {
return "Wrong code!" return wrongCode
} }
return "" 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 // From Casnode/object/validateCode.go line 116
var stdNums = []byte("0123456789") var stdNums = []byte("0123456789")

View File

@ -158,12 +158,13 @@ func initAPI() {
beego.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken") beego.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken")
beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken") beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken")
beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken") beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken")
beego.Router("/api/login/oauth/logout", &controllers.ApiController{}, "GET:TokenLogout")
beego.Router("/api/get-records", &controllers.ApiController{}, "GET:GetRecords") beego.Router("/api/get-records", &controllers.ApiController{}, "GET:GetRecords")
beego.Router("/api/get-records-filter", &controllers.ApiController{}, "POST:GetRecordsByFilter") beego.Router("/api/get-records-filter", &controllers.ApiController{}, "POST:GetRecordsByFilter")
beego.Router("/api/add-record", &controllers.ApiController{}, "POST:AddRecord") 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-webhooks", &controllers.ApiController{}, "GET:GetWebhooks")
beego.Router("/api/get-webhook", &controllers.ApiController{}, "GET:GetWebhook") beego.Router("/api/get-webhook", &controllers.ApiController{}, "GET:GetWebhook")
beego.Router("/api/update-webhook", &controllers.ApiController{}, "POST:UpdateWebhook") beego.Router("/api/update-webhook", &controllers.ApiController{}, "POST:UpdateWebhook")

View File

@ -275,6 +275,34 @@
} }
} }
}, },
"/api/add-record": {
"post": {
"tags": [
"Record API"
],
"description": "add a record",
"operationId": "ApiController.AddRecord",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the record",
"required": true,
"schema": {
"$ref": "#/definitions/object.Record"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/add-resource": { "/api/add-resource": {
"post": { "post": {
"tags": [ "tags": [
@ -431,6 +459,14 @@
"operationId": "ApiController.GetCaptcha" "operationId": "ApiController.GetCaptcha"
} }
}, },
"/api/api/get-webhook-event": {
"get": {
"tags": [
"GetWebhookEventType API"
],
"operationId": "ApiController.GetWebhookEventType"
}
},
"/api/api/reset-email-or-phone": { "/api/api/reset-email-or-phone": {
"post": { "post": {
"tags": [ "tags": [
@ -523,6 +559,14 @@
} }
} }
}, },
"/api/api/webhook": {
"post": {
"tags": [
"HandleOfficialAccountEvent API"
],
"operationId": "ApiController.HandleOfficialAccountEvent"
}
},
"/api/buy-product": { "/api/buy-product": {
"post": { "post": {
"tags": [ "tags": [
@ -840,6 +884,35 @@
} }
} }
}, },
"/api/delete-session": {
"post": {
"tags": [
"Session API"
],
"description": "Delete session by userId",
"operationId": "ApiController.DeleteSession",
"parameters": [
{
"in": "query",
"name": "ID",
"description": "The ID(owner/name) of user.",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
},
"/api/delete-syncer": { "/api/delete-syncer": {
"post": { "post": {
"tags": [ "tags": [
@ -1133,6 +1206,32 @@
} }
} }
}, },
"/api/get-default-application": {
"get": {
"tags": [
"Organization API"
],
"description": "get default application",
"operationId": "ApiController.GetDefaultApplication",
"parameters": [
{
"in": "query",
"name": "id",
"description": "organization id",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/Response"
}
}
}
}
},
"/api/get-email-and-phone": { "/api/get-email-and-phone": {
"post": { "post": {
"tags": [ "tags": [
@ -1166,6 +1265,26 @@
} }
} }
}, },
"/api/get-global-providers": {
"get": {
"tags": [
"Provider API"
],
"description": "get Global providers",
"operationId": "ApiController.GetGlobalProviders",
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Provider"
}
}
}
}
}
},
"/api/get-global-users": { "/api/get-global-users": {
"get": { "get": {
"tags": [ "tags": [
@ -1459,6 +1578,55 @@
} }
} }
}, },
"/api/get-permissions-by-role": {
"get": {
"tags": [
"Permission API"
],
"description": "get permissions by role",
"operationId": "ApiController.GetPermissionsByRole",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id of the role",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Permission"
}
}
}
}
}
},
"/api/get-permissions-by-submitter": {
"get": {
"tags": [
"Permission API"
],
"description": "get permissions by submitter",
"operationId": "ApiController.GetPermissionsBySubmitter",
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Permission"
}
}
}
}
}
},
"/api/get-product": { "/api/get-product": {
"get": { "get": {
"tags": [ "tags": [
@ -1631,6 +1799,20 @@
} }
} }
}, },
"/api/get-release": {
"get": {
"tags": [
"System API"
],
"description": "get local github repo's latest release version info",
"operationId": "ApiController.GitRepoVersion",
"responses": {
"200": {
"description": "{string} local latest version hash of casdoor"
}
}
}
},
"/api/get-resource": { "/api/get-resource": {
"get": { "get": {
"tags": [ "tags": [
@ -1702,6 +1884,35 @@
} }
} }
}, },
"/api/get-sessions": {
"get": {
"tags": [
"Session API"
],
"description": "Get organization user sessions",
"operationId": "ApiController.GetSessions",
"parameters": [
{
"in": "query",
"name": "owner",
"description": "The organization name",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
},
"/api/get-sorted-users": { "/api/get-sorted-users": {
"get": { "get": {
"tags": [ "tags": [
@ -1799,6 +2010,32 @@
} }
} }
}, },
"/api/get-system-info": {
"get": {
"tags": [
"System API"
],
"description": "get user's system info",
"operationId": "ApiController.GetSystemInfo",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id of the user",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/object.SystemInfo"
}
}
}
}
},
"/api/get-token": { "/api/get-token": {
"get": { "get": {
"tags": [ "tags": [
@ -2360,45 +2597,6 @@
} }
} }
}, },
"/api/login/oauth/logout": {
"get": {
"tags": [
"Token API"
],
"description": "delete token by AccessToken",
"operationId": "ApiController.TokenLogout",
"parameters": [
{
"in": "query",
"name": "id_token_hint",
"description": "id_token_hint",
"required": true,
"type": "string"
},
{
"in": "query",
"name": "post_logout_redirect_uri",
"description": "post_logout_redirect_uri",
"type": "string"
},
{
"in": "query",
"name": "state",
"description": "state",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/login/oauth/refresh_token": { "/api/login/oauth/refresh_token": {
"post": { "post": {
"tags": [ "tags": [
@ -2471,6 +2669,26 @@
], ],
"description": "logout the current user", "description": "logout the current user",
"operationId": "ApiController.Logout", "operationId": "ApiController.Logout",
"parameters": [
{
"in": "query",
"name": "id_token_hint",
"description": "id_token_hint",
"type": "string"
},
{
"in": "query",
"name": "post_logout_redirect_uri",
"description": "post_logout_redirect_uri",
"type": "string"
},
{
"in": "query",
"name": "state",
"description": "state",
"type": "string"
}
],
"responses": { "responses": {
"200": { "200": {
"description": "The Response object", "description": "The Response object",
@ -2486,6 +2704,26 @@
], ],
"description": "logout the current user", "description": "logout the current user",
"operationId": "ApiController.Logout", "operationId": "ApiController.Logout",
"parameters": [
{
"in": "query",
"name": "id_token_hint",
"description": "id_token_hint",
"type": "string"
},
{
"in": "query",
"name": "post_logout_redirect_uri",
"description": "post_logout_redirect_uri",
"type": "string"
},
{
"in": "query",
"name": "state",
"description": "state",
"type": "string"
}
],
"responses": { "responses": {
"200": { "200": {
"description": "The Response object", "description": "The Response object",
@ -3267,11 +3505,11 @@
} }
}, },
"definitions": { "definitions": {
"2200.0xc0003f8480.false": { "2268.0xc0000f9650.false": {
"title": "false", "title": "false",
"type": "object" "type": "object"
}, },
"2235.0xc0003f84b0.false": { "2302.0xc0000f9680.false": {
"title": "false", "title": "false",
"type": "object" "type": "object"
}, },
@ -3316,6 +3554,15 @@
"autoSignin": { "autoSignin": {
"type": "boolean" "type": "boolean"
}, },
"captchaToken": {
"type": "string"
},
"captchaType": {
"type": "string"
},
"clientSecret": {
"type": "string"
},
"code": { "code": {
"type": "string" "type": "string"
}, },
@ -3389,10 +3636,10 @@
"type": "object", "type": "object",
"properties": { "properties": {
"data": { "data": {
"$ref": "#/definitions/2200.0xc0003f8480.false" "$ref": "#/definitions/2268.0xc0000f9650.false"
}, },
"data2": { "data2": {
"$ref": "#/definitions/2235.0xc0003f84b0.false" "$ref": "#/definitions/2302.0xc0000f9680.false"
}, },
"msg": { "msg": {
"type": "string" "type": "string"
@ -3491,9 +3738,15 @@
"displayName": { "displayName": {
"type": "string" "type": "string"
}, },
"enableAutoSignin": {
"type": "boolean"
},
"enableCodeSignin": { "enableCodeSignin": {
"type": "boolean" "type": "boolean"
}, },
"enableLinkWithEmail": {
"type": "boolean"
},
"enablePassword": { "enablePassword": {
"type": "boolean" "type": "boolean"
}, },
@ -3516,6 +3769,19 @@
"forgetUrl": { "forgetUrl": {
"type": "string" "type": "string"
}, },
"formBackgroundUrl": {
"type": "string"
},
"formCss": {
"type": "string"
},
"formOffset": {
"type": "integer",
"format": "int64"
},
"formSideHtml": {
"type": "string"
},
"grantTypes": { "grantTypes": {
"type": "array", "type": "array",
"items": { "items": {
@ -3556,6 +3822,9 @@
"type": "integer", "type": "integer",
"format": "int64" "format": "int64"
}, },
"samlReplyUrl": {
"type": "string"
},
"signinHtml": { "signinHtml": {
"type": "string" "type": "string"
}, },
@ -3689,6 +3958,24 @@
} }
} }
}, },
"object.ManagedAccount": {
"title": "ManagedAccount",
"type": "object",
"properties": {
"application": {
"type": "string"
},
"password": {
"type": "string"
},
"signinUrl": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"object.Model": { "object.Model": {
"title": "Model", "title": "Model",
"type": "object", "type": "object",
@ -3726,6 +4013,9 @@
"type": "string" "type": "string"
} }
}, },
"end_session_endpoint": {
"type": "string"
},
"grant_types_supported": { "grant_types_supported": {
"type": "array", "type": "array",
"items": { "items": {
@ -3801,6 +4091,9 @@
"createdTime": { "createdTime": {
"type": "string" "type": "string"
}, },
"defaultApplication": {
"type": "string"
},
"defaultAvatar": { "defaultAvatar": {
"type": "string" "type": "string"
}, },
@ -3813,9 +4106,19 @@
"favicon": { "favicon": {
"type": "string" "type": "string"
}, },
"initScore": {
"type": "integer",
"format": "int64"
},
"isProfilePublic": { "isProfilePublic": {
"type": "boolean" "type": "boolean"
}, },
"languages": {
"type": "array",
"items": {
"type": "string"
}
},
"masterPassword": { "masterPassword": {
"type": "string" "type": "string"
}, },
@ -3943,12 +4246,27 @@
"type": "string" "type": "string"
} }
}, },
"adapter": {
"type": "string"
},
"approveTime": {
"type": "string"
},
"approver": {
"type": "string"
},
"createdTime": { "createdTime": {
"type": "string" "type": "string"
}, },
"displayName": { "displayName": {
"type": "string" "type": "string"
}, },
"domains": {
"type": "array",
"items": {
"type": "string"
}
},
"effect": { "effect": {
"type": "string" "type": "string"
}, },
@ -3979,6 +4297,12 @@
"type": "string" "type": "string"
} }
}, },
"state": {
"type": "string"
},
"submitter": {
"type": "string"
},
"users": { "users": {
"type": "array", "type": "array",
"items": { "items": {
@ -3997,6 +4321,9 @@
"currency": { "currency": {
"type": "string" "type": "string"
}, },
"description": {
"type": "string"
},
"detail": { "detail": {
"type": "string" "type": "string"
}, },
@ -4016,6 +4343,12 @@
"type": "number", "type": "number",
"format": "double" "format": "double"
}, },
"providerObjs": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Provider"
}
},
"providers": { "providers": {
"type": "array", "type": "array",
"items": { "items": {
@ -4090,6 +4423,9 @@
"customUserInfoUrl": { "customUserInfoUrl": {
"type": "string" "type": "string"
}, },
"disableSsl": {
"type": "boolean"
},
"displayName": { "displayName": {
"type": "string" "type": "string"
}, },
@ -4126,6 +4462,9 @@
"owner": { "owner": {
"type": "string" "type": "string"
}, },
"pathPrefix": {
"type": "string"
},
"port": { "port": {
"type": "integer", "type": "integer",
"format": "int64" "format": "int64"
@ -4133,6 +4472,9 @@
"providerUrl": { "providerUrl": {
"type": "string" "type": "string"
}, },
"receiver": {
"type": "string"
},
"regionId": { "regionId": {
"type": "string" "type": "string"
}, },
@ -4172,11 +4514,17 @@
"name": { "name": {
"type": "string" "type": "string"
}, },
"owner": {
"type": "string"
},
"prompted": { "prompted": {
"type": "boolean" "type": "boolean"
}, },
"provider": { "provider": {
"$ref": "#/definitions/object.Provider" "$ref": "#/definitions/object.Provider"
},
"rule": {
"type": "string"
} }
} }
}, },
@ -4233,6 +4581,12 @@
"displayName": { "displayName": {
"type": "string" "type": "string"
}, },
"domains": {
"type": "array",
"items": {
"type": "string"
}
},
"isEnabled": { "isEnabled": {
"type": "boolean" "type": "boolean"
}, },
@ -4345,6 +4699,10 @@
} }
} }
}, },
"object.SystemInfo": {
"title": "SystemInfo",
"type": "object"
},
"object.TableColumn": { "object.TableColumn": {
"title": "TableColumn", "title": "TableColumn",
"type": "object", "type": "object",
@ -4605,15 +4963,27 @@
"lastSigninTime": { "lastSigninTime": {
"type": "string" "type": "string"
}, },
"lastSigninWrongTime": {
"type": "string"
},
"ldap": { "ldap": {
"type": "string" "type": "string"
}, },
"line": {
"type": "string"
},
"linkedin": { "linkedin": {
"type": "string" "type": "string"
}, },
"location": { "location": {
"type": "string" "type": "string"
}, },
"managedAccounts": {
"type": "array",
"items": {
"$ref": "#/definitions/object.ManagedAccount"
}
},
"name": { "name": {
"type": "string" "type": "string"
}, },
@ -4669,6 +5039,10 @@
"type": "integer", "type": "integer",
"format": "int64" "format": "int64"
}, },
"signinWrongTimes": {
"type": "integer",
"format": "int64"
},
"signupApplication": { "signupApplication": {
"type": "string" "type": "string"
}, },
@ -4687,9 +5061,6 @@
"type": { "type": {
"type": "string" "type": "string"
}, },
"unionId": {
"type": "string"
},
"updatedTime": { "updatedTime": {
"type": "string" "type": "string"
}, },

View File

@ -177,6 +177,24 @@ paths:
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/controllers.Response' $ref: '#/definitions/controllers.Response'
/api/add-record:
post:
tags:
- Record API
description: add a record
operationId: ApiController.AddRecord
parameters:
- in: body
name: body
description: The details of the record
required: true
schema:
$ref: '#/definitions/object.Record'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-resource: /api/add-resource:
post: post:
tags: tags:
@ -277,6 +295,11 @@ paths:
tags: tags:
- Login API - Login API
operationId: ApiController.GetCaptcha operationId: ApiController.GetCaptcha
/api/api/get-webhook-event:
get:
tags:
- GetWebhookEventType API
operationId: ApiController.GetWebhookEventType
/api/api/reset-email-or-phone: /api/api/reset-email-or-phone:
post: post:
tags: tags:
@ -338,6 +361,11 @@ paths:
description: object description: object
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
/api/api/webhook:
post:
tags:
- HandleOfficialAccountEvent API
operationId: ApiController.HandleOfficialAccountEvent
/api/buy-product: /api/buy-product:
post: post:
tags: tags:
@ -542,6 +570,25 @@ paths:
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/controllers.Response' $ref: '#/definitions/controllers.Response'
/api/delete-session:
post:
tags:
- Session API
description: Delete session by userId
operationId: ApiController.DeleteSession
parameters:
- in: query
name: ID
description: The ID(owner/name) of user.
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
type: string
/api/delete-syncer: /api/delete-syncer:
post: post:
tags: tags:
@ -734,6 +781,23 @@ paths:
type: array type: array
items: items:
$ref: '#/definitions/object.Cert' $ref: '#/definitions/object.Cert'
/api/get-default-application:
get:
tags:
- Organization API
description: get default application
operationId: ApiController.GetDefaultApplication
parameters:
- in: query
name: id
description: organization id
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/Response'
/api/get-email-and-phone: /api/get-email-and-phone:
post: post:
tags: tags:
@ -756,6 +820,19 @@ paths:
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/controllers.Response' $ref: '#/definitions/controllers.Response'
/api/get-global-providers:
get:
tags:
- Provider API
description: get Global providers
operationId: ApiController.GetGlobalProviders
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Provider'
/api/get-global-users: /api/get-global-users:
get: get:
tags: tags:
@ -947,6 +1024,38 @@ paths:
type: array type: array
items: items:
$ref: '#/definitions/object.Permission' $ref: '#/definitions/object.Permission'
/api/get-permissions-by-role:
get:
tags:
- Permission API
description: get permissions by role
operationId: ApiController.GetPermissionsByRole
parameters:
- in: query
name: id
description: The id of the role
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Permission'
/api/get-permissions-by-submitter:
get:
tags:
- Permission API
description: get permissions by submitter
operationId: ApiController.GetPermissionsBySubmitter
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Permission'
/api/get-product: /api/get-product:
get: get:
tags: tags:
@ -1060,6 +1169,15 @@ paths:
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/object.Record' $ref: '#/definitions/object.Record'
/api/get-release:
get:
tags:
- System API
description: get local github repo's latest release version info
operationId: ApiController.GitRepoVersion
responses:
"200":
description: '{string} local latest version hash of casdoor'
/api/get-resource: /api/get-resource:
get: get:
tags: tags:
@ -1106,6 +1224,25 @@ paths:
type: array type: array
items: items:
$ref: '#/definitions/object.Role' $ref: '#/definitions/object.Role'
/api/get-sessions:
get:
tags:
- Session API
description: Get organization user sessions
operationId: ApiController.GetSessions
parameters:
- in: query
name: owner
description: The organization name
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
type: string
/api/get-sorted-users: /api/get-sorted-users:
get: get:
tags: tags:
@ -1170,6 +1307,23 @@ paths:
type: array type: array
items: items:
$ref: '#/definitions/object.Syncer' $ref: '#/definitions/object.Syncer'
/api/get-system-info:
get:
tags:
- System API
description: get user's system info
operationId: ApiController.GetSystemInfo
parameters:
- in: query
name: id
description: The id of the user
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/object.SystemInfo'
/api/get-token: /api/get-token:
get: get:
tags: tags:
@ -1544,32 +1698,6 @@ paths:
description: The Response object description: The Response object
schema: schema:
$ref: '#/definitions/object.TokenError' $ref: '#/definitions/object.TokenError'
/api/login/oauth/logout:
get:
tags:
- Token API
description: delete token by AccessToken
operationId: ApiController.TokenLogout
parameters:
- in: query
name: id_token_hint
description: id_token_hint
required: true
type: string
- in: query
name: post_logout_redirect_uri
description: post_logout_redirect_uri
type: string
- in: query
name: state
description: state
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/login/oauth/refresh_token: /api/login/oauth/refresh_token:
post: post:
tags: tags:
@ -1620,6 +1748,19 @@ paths:
- Login API - Login API
description: logout the current user description: logout the current user
operationId: ApiController.Logout operationId: ApiController.Logout
parameters:
- in: query
name: id_token_hint
description: id_token_hint
type: string
- in: query
name: post_logout_redirect_uri
description: post_logout_redirect_uri
type: string
- in: query
name: state
description: state
type: string
responses: responses:
"200": "200":
description: The Response object description: The Response object
@ -1630,6 +1771,19 @@ paths:
- Login API - Login API
description: logout the current user description: logout the current user
operationId: ApiController.Logout operationId: ApiController.Logout
parameters:
- in: query
name: id_token_hint
description: id_token_hint
type: string
- in: query
name: post_logout_redirect_uri
description: post_logout_redirect_uri
type: string
- in: query
name: state
description: state
type: string
responses: responses:
"200": "200":
description: The Response object description: The Response object
@ -2139,10 +2293,10 @@ paths:
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
definitions: definitions:
2200.0xc0003f8480.false: 2268.0xc0000f9650.false:
title: "false" title: "false"
type: object type: object
2235.0xc0003f84b0.false: 2302.0xc0000f9680.false:
title: "false" title: "false"
type: object type: object
Response: Response:
@ -2174,6 +2328,12 @@ definitions:
type: string type: string
autoSignin: autoSignin:
type: boolean type: boolean
captchaToken:
type: string
captchaType:
type: string
clientSecret:
type: string
code: code:
type: string type: string
email: email:
@ -2223,9 +2383,9 @@ definitions:
type: object type: object
properties: properties:
data: data:
$ref: '#/definitions/2200.0xc0003f8480.false' $ref: '#/definitions/2268.0xc0000f9650.false'
data2: data2:
$ref: '#/definitions/2235.0xc0003f84b0.false' $ref: '#/definitions/2302.0xc0000f9680.false'
msg: msg:
type: string type: string
name: name:
@ -2291,8 +2451,12 @@ definitions:
type: string type: string
displayName: displayName:
type: string type: string
enableAutoSignin:
type: boolean
enableCodeSignin: enableCodeSignin:
type: boolean type: boolean
enableLinkWithEmail:
type: boolean
enablePassword: enablePassword:
type: boolean type: boolean
enableSamlCompress: enableSamlCompress:
@ -2308,6 +2472,15 @@ definitions:
format: int64 format: int64
forgetUrl: forgetUrl:
type: string type: string
formBackgroundUrl:
type: string
formCss:
type: string
formOffset:
type: integer
format: int64
formSideHtml:
type: string
grantTypes: grantTypes:
type: array type: array
items: items:
@ -2335,6 +2508,8 @@ definitions:
refreshExpireInHours: refreshExpireInHours:
type: integer type: integer
format: int64 format: int64
samlReplyUrl:
type: string
signinHtml: signinHtml:
type: string type: string
signinUrl: signinUrl:
@ -2424,6 +2599,18 @@ definitions:
type: string type: string
username: username:
type: string type: string
object.ManagedAccount:
title: ManagedAccount
type: object
properties:
application:
type: string
password:
type: string
signinUrl:
type: string
username:
type: string
object.Model: object.Model:
title: Model title: Model
type: object type: object
@ -2450,6 +2637,8 @@ definitions:
type: array type: array
items: items:
type: string type: string
end_session_endpoint:
type: string
grant_types_supported: grant_types_supported:
type: array type: array
items: items:
@ -2500,6 +2689,8 @@ definitions:
$ref: '#/definitions/object.AccountItem' $ref: '#/definitions/object.AccountItem'
createdTime: createdTime:
type: string type: string
defaultApplication:
type: string
defaultAvatar: defaultAvatar:
type: string type: string
displayName: displayName:
@ -2508,8 +2699,15 @@ definitions:
type: boolean type: boolean
favicon: favicon:
type: string type: string
initScore:
type: integer
format: int64
isProfilePublic: isProfilePublic:
type: boolean type: boolean
languages:
type: array
items:
type: string
masterPassword: masterPassword:
type: string type: string
name: name:
@ -2595,10 +2793,20 @@ definitions:
type: array type: array
items: items:
type: string type: string
adapter:
type: string
approveTime:
type: string
approver:
type: string
createdTime: createdTime:
type: string type: string
displayName: displayName:
type: string type: string
domains:
type: array
items:
type: string
effect: effect:
type: string type: string
isEnabled: isEnabled:
@ -2619,6 +2827,10 @@ definitions:
type: array type: array
items: items:
type: string type: string
state:
type: string
submitter:
type: string
users: users:
type: array type: array
items: items:
@ -2631,6 +2843,8 @@ definitions:
type: string type: string
currency: currency:
type: string type: string
description:
type: string
detail: detail:
type: string type: string
displayName: displayName:
@ -2644,6 +2858,10 @@ definitions:
price: price:
type: number type: number
format: double format: double
providerObjs:
type: array
items:
$ref: '#/definitions/object.Provider'
providers: providers:
type: array type: array
items: items:
@ -2694,6 +2912,8 @@ definitions:
type: string type: string
customUserInfoUrl: customUserInfoUrl:
type: string type: string
disableSsl:
type: boolean
displayName: displayName:
type: string type: string
domain: domain:
@ -2718,11 +2938,15 @@ definitions:
type: string type: string
owner: owner:
type: string type: string
pathPrefix:
type: string
port: port:
type: integer type: integer
format: int64 format: int64
providerUrl: providerUrl:
type: string type: string
receiver:
type: string
regionId: regionId:
type: string type: string
signName: signName:
@ -2749,10 +2973,14 @@ definitions:
type: boolean type: boolean
name: name:
type: string type: string
owner:
type: string
prompted: prompted:
type: boolean type: boolean
provider: provider:
$ref: '#/definitions/object.Provider' $ref: '#/definitions/object.Provider'
rule:
type: string
object.Record: object.Record:
title: Record title: Record
type: object type: object
@ -2790,6 +3018,10 @@ definitions:
type: string type: string
displayName: displayName:
type: string type: string
domains:
type: array
items:
type: string
isEnabled: isEnabled:
type: boolean type: boolean
name: name:
@ -2864,6 +3096,9 @@ definitions:
type: string type: string
user: user:
type: string type: string
object.SystemInfo:
title: SystemInfo
type: object
object.TableColumn: object.TableColumn:
title: TableColumn title: TableColumn
type: object type: object
@ -3040,12 +3275,20 @@ definitions:
type: string type: string
lastSigninTime: lastSigninTime:
type: string type: string
lastSigninWrongTime:
type: string
ldap: ldap:
type: string type: string
line:
type: string
linkedin: linkedin:
type: string type: string
location: location:
type: string type: string
managedAccounts:
type: array
items:
$ref: '#/definitions/object.ManagedAccount'
name: name:
type: string type: string
okta: okta:
@ -3083,6 +3326,9 @@ definitions:
score: score:
type: integer type: integer
format: int64 format: int64
signinWrongTimes:
type: integer
format: int64
signupApplication: signupApplication:
type: string type: string
slack: slack:
@ -3095,8 +3341,6 @@ definitions:
type: string type: string
type: type:
type: string type: string
unionId:
type: string
updatedTime: updatedTime:
type: string type: string
webauthnCredentials: webauthnCredentials:

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
}

View File

@ -53,7 +53,7 @@ func Test_IsTokenExpired(t *testing.T) {
for _, scenario := range []testCases{ for _, scenario := range []testCases{
{ {
description: "Token emited now is valid for 60 minutes", description: "Token emitted now is valid for 60 minutes",
input: input{ input: input{
createdTime: time.Now().Format(time.RFC3339), createdTime: time.Now().Format(time.RFC3339),
expiresIn: 60, expiresIn: 60,
@ -61,7 +61,7 @@ func Test_IsTokenExpired(t *testing.T) {
expected: false, expected: false,
}, },
{ {
description: "Token emited 60 minutes before now is valid for 60 minutes", description: "Token emitted 60 minutes before now is valid for 60 minutes",
input: input{ input: input{
createdTime: time.Now().Add(-time.Minute * 60).Format(time.RFC3339), createdTime: time.Now().Add(-time.Minute * 60).Format(time.RFC3339),
expiresIn: 61, expiresIn: 61,
@ -69,7 +69,7 @@ func Test_IsTokenExpired(t *testing.T) {
expected: false, expected: false,
}, },
{ {
description: "Token emited 2 hours before now is Expired after 60 minutes", description: "Token emitted 2 hours before now is Expired after 60 minutes",
input: input{ input: input{
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339), createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
expiresIn: 60, expiresIn: 60,
@ -77,7 +77,7 @@ func Test_IsTokenExpired(t *testing.T) {
expected: true, expected: true,
}, },
{ {
description: "Token emited 61 minutes before now is Expired after 60 minutes", description: "Token emitted 61 minutes before now is Expired after 60 minutes",
input: input{ input: input{
createdTime: time.Now().Add(-time.Minute * 61).Format(time.RFC3339), createdTime: time.Now().Add(-time.Minute * 61).Format(time.RFC3339),
expiresIn: 60, expiresIn: 60,
@ -85,7 +85,7 @@ func Test_IsTokenExpired(t *testing.T) {
expected: true, expected: true,
}, },
{ {
description: "Token emited 2 hours before now is velid for 120 minutes", description: "Token emitted 2 hours before now is valid for 120 minutes",
input: input{ input: input{
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339), createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
expiresIn: 121, expiresIn: 121,
@ -93,7 +93,7 @@ func Test_IsTokenExpired(t *testing.T) {
expected: false, expected: false,
}, },
{ {
description: "Token emited 159 minutes before now is Expired after 60 minutes", description: "Token emitted 159 minutes before now is Expired after 60 minutes",
input: input{ input: input{
createdTime: time.Now().Add(-time.Minute * 159).Format(time.RFC3339), createdTime: time.Now().Add(-time.Minute * 159).Format(time.RFC3339),
expiresIn: 120, expiresIn: 120,

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/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2", "@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2", "@testing-library/user-event": "^7.1.2",
"antd": "5.0.5", "antd": "5.1.2",
"codemirror": "^5.61.1", "codemirror": "^5.61.1",
"copy-to-clipboard": "^3.3.1", "copy-to-clipboard": "^3.3.1",
"core-js": "^3.25.0", "core-js": "^3.25.0",

View File

@ -1,16 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?5998fcd123c220efc0936edf4f250504";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<meta charset="utf-8" /> <meta charset="utf-8" />
<!-- <link rel="icon" href="%PUBLIC_URL%/favicon.png" />--> <!-- <link rel="icon" href="%PUBLIC_URL%/favicon.png" />-->
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />

View File

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

View File

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

View File

@ -13,14 +13,12 @@
} }
.App-header { .App-header {
background-color: #282c34;
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: calc(10px + 2vmin); font-size: calc(10px + 2vmin);
color: white;
} }
.App-link { .App-link {
@ -41,7 +39,6 @@ img {
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
min-height: 100vh; min-height: 100vh;
background-color: #f5f5f5;
} }
.panel-logo { .panel-logo {
@ -55,7 +52,7 @@ img {
background-repeat: no-repeat; background-repeat: no-repeat;
border-radius: 5px; border-radius: 5px;
width: 45px; width: 45px;
height: 65px; height: 100%;
float: right; float: right;
cursor: pointer; 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 { .rightDropDown {
border-radius: 7px;
&:hover { &:hover {
background-color: #f5f5f5; background-color: #f5f5f5;
color: black;
} }
} }
@ -128,3 +148,9 @@ img {
background-size: 100% 100%; background-size: 100% 100%;
background-attachment: fixed; 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. // limitations under the License.
import React from "react"; 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 {CopyOutlined, LinkOutlined, UploadOutlined} from "@ant-design/icons";
import * as ApplicationBackend from "./backend/ApplicationBackend"; import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as CertBackend from "./backend/CertBackend"; import * as CertBackend from "./backend/CertBackend";
@ -103,6 +103,7 @@ class ApplicationEditPage extends React.Component {
uploading: false, uploading: false,
mode: props.location.mode !== undefined ? props.location.mode : "edit", mode: props.location.mode !== undefined ? props.location.mode : "edit",
samlMetadata: null, samlMetadata: null,
isAuthorized: true,
}; };
} }
@ -129,9 +130,15 @@ class ApplicationEditPage extends React.Component {
getOrganizations() { getOrganizations() {
OrganizationBackend.getOrganizations("admin") OrganizationBackend.getOrganizations("admin")
.then((res) => { .then((res) => {
this.setState({ if (res?.status === "error") {
organizations: (res.msg === undefined) ? res : [], this.setState({
}); isAuthorized: false,
});
} else {
this.setState({
organizations: (res.msg === undefined) ? res : [],
});
}
}); });
} }
@ -336,12 +343,9 @@ class ApplicationEditPage extends React.Component {
{Setting.getLabel(i18next.t("application:Token format"), i18next.t("application:Token format - Tooltip"))} : {Setting.getLabel(i18next.t("application:Token format"), i18next.t("application:Token format - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenFormat} onChange={(value => {this.updateApplicationField("tokenFormat", value);})}> <Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenFormat} onChange={(value => {this.updateApplicationField("tokenFormat", value);})}
{ options={["JWT", "JWT-Empty"].map((item) => Setting.getOption(item, item))}
["JWT", "JWT-Empty"] />
.map((item, index) => <Option key={index} value={item}>{item}</Option>)
}
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -599,6 +603,16 @@ class ApplicationEditPage extends React.Component {
/> />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col span={(Setting.isMobile()) ? 19 : 6}>
{Setting.getLabel(i18next.t("application:Enable link accounts that with the same email"), i18next.t("application:Enable link accounts that with the same email - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.application.enableLinkWithEmail} onChange={checked => {
this.updateApplicationField("enableLinkWithEmail", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Preview"), i18next.t("general:Preview - Tooltip"))} : {Setting.getLabel(i18next.t("general:Preview"), i18next.t("general:Preview - Tooltip"))} :
@ -726,7 +740,7 @@ class ApplicationEditPage extends React.Component {
renderSignupSigninPreview() { renderSignupSigninPreview() {
let signUpUrl = `/signup/${this.state.application.name}`; 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 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) { if (!this.state.application.enablePassword) {
signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize"); signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize");
} }
@ -742,15 +756,19 @@ class ApplicationEditPage extends React.Component {
{i18next.t("application:Copy signup page URL")} {i18next.t("application:Copy signup page URL")}
</Button> </Button>
<br /> <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 ? ( 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> </div>
</Col> </Col>
<Col span={previewGrid}> <Col span={previewGrid}>
@ -762,9 +780,11 @@ class ApplicationEditPage extends React.Component {
{i18next.t("application:Copy signin page URL")} {i18next.t("application:Copy signin page URL")}
</Button> </Button>
<br /> <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"}}>
<LoginPage type={"login"} mode={"signin"} application={this.state.application} /> <div className="loginBackground" style={{backgroundImage: `url(${this.state.application?.formBackgroundUrl})`, overflow: "auto"}}>
<div style={maskStyle} /> <LoginPage type={"login"} mode={"signin"} application={this.state.application} preview = "auto" />
</div>
<div style={{overflow: "auto", ...maskStyle}} />
</div> </div>
</Col> </Col>
</React.Fragment> </React.Fragment>
@ -832,6 +852,17 @@ class ApplicationEditPage extends React.Component {
} }
render() { 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 ( return (
<div> <div>
{ {

View File

@ -14,7 +14,7 @@
import React from "react"; import React from "react";
import {Link} from "react-router-dom"; 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 {EditOutlined} from "@ant-design/icons";
import moment from "moment"; import moment from "moment";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
@ -36,6 +36,7 @@ class ApplicationListPage extends BaseListPage {
loading: false, loading: false,
searchText: "", searchText: "",
searchedColumn: "", searchedColumn: "",
isAuthorized: true,
}; };
} }
@ -258,6 +259,17 @@ class ApplicationListPage extends BaseListPage {
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total), 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 ( return (
<div> <div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={applications} rowKey="name" size="middle" bordered pagination={paginationProps} <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, searchText: params.searchText,
searchedColumn: params.searchedColumn, 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. // limitations under the License.
import React from "react"; import React from "react";
import {Button, Input, Space} from "antd"; import {Button, Input, Result, Space} from "antd";
import {SearchOutlined} from "@ant-design/icons"; import {SearchOutlined} from "@ant-design/icons";
import Highlighter from "react-highlight-words"; import Highlighter from "react-highlight-words";
import i18next from "i18next";
class BaseListPage extends React.Component { class BaseListPage extends React.Component {
constructor(props) { constructor(props) {
@ -127,6 +128,17 @@ class BaseListPage extends React.Component {
}; };
render() { 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 ( return (
<div> <div>
{ {

View File

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

View File

@ -153,7 +153,7 @@ export const CropperDiv = (props) => {
<Row style={{width: "100%", marginBottom: "20px"}}> <Row style={{width: "100%", marginBottom: "20px"}}>
<input style={{display: "none"}} ref={input => uploadButton = input} type="file" accept="image/*" onChange={onChange} /> <input style={{display: "none"}} ref={input => uploadButton = input} type="file" accept="image/*" onChange={onChange} />
<Button block onClick={selectFile}>{i18next.t("user:Select a photo...")}</Button> <Button block onClick={selectFile}>{i18next.t("user:Select a photo...")}</Button>
<Select <Select virtual={false}
style={{width: "100%"}} style={{width: "100%"}}
loading={loading} loading={loading}
placeholder={i18next.t("user:Please select avatar from resources")} placeholder={i18next.t("user:Please select avatar from resources")}

View File

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

View File

@ -165,12 +165,9 @@ class OrganizationEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Password type"), i18next.t("general:Password type - Tooltip"))} : {Setting.getLabel(i18next.t("general:Password type"), i18next.t("general:Password type - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.passwordType} onChange={(value => {this.updateOrganizationField("passwordType", value);})}> <Select virtual={false} style={{width: "100%"}} value={this.state.organization.passwordType} onChange={(value => {this.updateOrganizationField("passwordType", value);})}
{ options={["plain", "salt", "md5-salt", "bcrypt", "pbkdf2-salt", "argon2id"].map(item => Setting.getOption(item, item))}
["plain", "salt", "md5-salt", "bcrypt", "pbkdf2-salt", "argon2id"] />
.map((item, index) => <Option key={index} value={item}>{item}</Option>)
}
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -225,11 +222,9 @@ class OrganizationEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Default application"), i18next.t("general:Default application - Tooltip"))} : {Setting.getLabel(i18next.t("general:Default application"), i18next.t("general:Default application - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.defaultApplication} onChange={(value => {this.updateOrganizationField("defaultApplication", value);})}> <Select virtual={false} style={{width: "100%"}} value={this.state.organization.defaultApplication} onChange={(value => {this.updateOrganizationField("defaultApplication", value);})}
{ options={this.state.applications?.map((item) => Setting.getOption(item.name, item.name))
this.state.applications?.map((item, index) => <Option key={index} value={item.name}>{item.name}</Option>) } />
}
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >

View File

@ -251,7 +251,7 @@ class OrganizationListPage extends BaseListPage {
<Result <Result
status="403" status="403"
title="403 Unauthorized" 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>} extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>}
/> />
); );

View File

@ -302,7 +302,7 @@ class PaymentEditPage extends React.Component {
{Setting.getLabel(i18next.t("payment:Invoice type"), i18next.t("payment:Invoice type - Tooltip"))} : {Setting.getLabel(i18next.t("payment:Invoice type"), i18next.t("payment:Invoice type - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select disabled={this.state.payment.invoiceUrl !== ""} virtual={false} style={{width: "100%"}} value={this.state.payment.invoiceType} onChange={(value => { <Select virtual={false} disabled={this.state.payment.invoiceUrl !== ""} style={{width: "100%"}} value={this.state.payment.invoiceType} onChange={(value => {
this.updatePaymentField("invoiceType", value); this.updatePaymentField("invoiceType", value);
if (value === "Individual") { if (value === "Individual") {
this.updatePaymentField("invoiceTitle", this.state.payment.personName); this.updatePaymentField("invoiceTitle", this.state.payment.personName);

View File

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

View File

@ -24,8 +24,6 @@ import * as ModelBackend from "./backend/ModelBackend";
import * as ApplicationBackend from "./backend/ApplicationBackend"; import * as ApplicationBackend from "./backend/ApplicationBackend";
import moment from "moment/moment"; import moment from "moment/moment";
const {Option} = Select;
class PermissionEditPage extends React.Component { class PermissionEditPage extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -163,16 +161,13 @@ class PermissionEditPage extends React.Component {
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.permission.owner} onChange={(owner => { <Select virtual={false} style={{width: "100%"}} value={this.state.permission.owner} onChange={(owner => {
this.updatePermissionField("owner", owner); this.updatePermissionField("owner", owner);
this.getUsers(owner); this.getUsers(owner);
this.getRoles(owner); this.getRoles(owner);
this.getModels(owner); this.getModels(owner);
this.getResources(owner); this.getResources(owner);
})}> })}
{ options={this.state.organizations.map((organization) => Setting.getOption(organization.name, organization.name))
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>) } />
}
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -202,11 +197,9 @@ class PermissionEditPage extends React.Component {
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.permission.model} onChange={(model => { <Select virtual={false} style={{width: "100%"}} value={this.state.permission.model} onChange={(model => {
this.updatePermissionField("model", model); this.updatePermissionField("model", model);
})}> })}
{ options={this.state.models.map((model) => Setting.getOption(model.name, model.name))
this.state.models.map((model, index) => <Option key={index} value={model.name}>{model.name}</Option>) } />
}
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -224,11 +217,10 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub users"), i18next.t("role:Sub users - Tooltip"))} : {Setting.getLabel(i18next.t("role:Sub users"), i18next.t("role:Sub users - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.users} onChange={(value => {this.updatePermissionField("users", value);})}> <Select mode="tags" style={{width: "100%"}} value={this.state.permission.users}
{ onChange={(value => {this.updatePermissionField("users", value);})}
this.state.users.map((user, index) => <Option key={index} value={`${user.owner}/${user.name}`}>{`${user.owner}/${user.name}`}</Option>) options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))}
} />
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -236,11 +228,10 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} : {Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} disabled={!this.hasRoleDefinition(this.state.model)} mode="tags" style={{width: "100%"}} value={this.state.permission.roles} onChange={(value => {this.updatePermissionField("roles", value);})}> <Select virtual={false} disabled={!this.hasRoleDefinition(this.state.model)} mode="tags" style={{width: "100%"}} value={this.state.permission.roles}
{ onChange={(value => {this.updatePermissionField("roles", value);})}
this.state.roles.filter(roles => (roles.owner !== this.state.roles.owner || roles.name !== this.state.roles.name)).map((permission, index) => <Option key={index} value={`${permission.owner}/${permission.name}`}>{`${permission.owner}/${permission.name}`}</Option>) options={this.state.roles.filter(roles => (roles.owner !== this.state.roles.owner || roles.name !== this.state.roles.name)).map((permission) => Setting.getOption(`${permission.owner}/${permission.name}`, `${permission.owner}/${permission.name}`))
} } />
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -248,13 +239,12 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub domains"), i18next.t("role:Sub domains - Tooltip"))} : {Setting.getLabel(i18next.t("role:Sub domains"), i18next.t("role:Sub domains - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.domains} onChange={(value => { <Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.domains}
this.updatePermissionField("domains", value); onChange={(value => {
})}> this.updatePermissionField("domains", value);
{ })}
this.state.permission.domains.map((domain, index) => <Option key={index} value={domain}>{domain}</Option>) options={this.state.permission.domains.map((domain) => Setting.getOption(domain, domain))
} } />
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -264,14 +254,12 @@ class PermissionEditPage extends React.Component {
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.permission.resourceType} onChange={(value => { <Select virtual={false} style={{width: "100%"}} value={this.state.permission.resourceType} onChange={(value => {
this.updatePermissionField("resourceType", value); this.updatePermissionField("resourceType", value);
})}> })}
{ options={[
[ {value: "Application", name: i18next.t("general:Application")},
{id: "Application", name: i18next.t("general:Application")}, {value: "TreeNode", name: i18next.t("permission:TreeNode")},
{id: "TreeNode", name: i18next.t("permission:TreeNode")}, ].map((item) => Setting.getOption(item.name, item.value))}
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>) />
}
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -279,11 +267,10 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("permission:Resources"), i18next.t("permission:Resources - Tooltip"))} : {Setting.getLabel(i18next.t("permission:Resources"), i18next.t("permission:Resources - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.resources} onChange={(value => {this.updatePermissionField("resources", value);})}> <Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.resources}
{ onChange={(value => {this.updatePermissionField("resources", value);})}
this.state.resources.map((resource, index) => <Option key={index} value={`${resource.name}`}>{`${resource.name}`}</Option>) options={this.state.resources.map((resource) => Setting.getOption(`${resource.name}`, `${resource.name}`))
} } />
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -293,15 +280,13 @@ class PermissionEditPage extends React.Component {
<Col span={22} > <Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.actions} onChange={(value => { <Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.actions} onChange={(value => {
this.updatePermissionField("actions", value); this.updatePermissionField("actions", value);
})}> })}
{ options={[
[ {value: "Read", name: i18next.t("permission:Read")},
{id: "Read", name: i18next.t("permission:Read")}, {value: "Write", name: i18next.t("permission:Write")},
{id: "Write", name: i18next.t("permission:Write")}, {value: "Admin", name: i18next.t("permission:Admin")},
{id: "Admin", name: i18next.t("permission:Admin")}, ].map((item) => Setting.getOption(item.name, item.value))}
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>) />
}
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -311,14 +296,12 @@ class PermissionEditPage extends React.Component {
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.permission.effect} onChange={(value => { <Select virtual={false} style={{width: "100%"}} value={this.state.permission.effect} onChange={(value => {
this.updatePermissionField("effect", value); this.updatePermissionField("effect", value);
})}> })}
{ options={[
[ {value: "Allow", name: i18next.t("permission:Allow")},
{id: "Allow", name: i18next.t("permission:Allow")}, {value: "Deny", name: i18next.t("permission:Deny")},
{id: "Deny", name: i18next.t("permission:Deny")}, ].map((item) => Setting.getOption(item.name, item.value))}
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>) />
}
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -366,7 +349,7 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("permission:State"), i18next.t("permission:State - Tooltip"))} : {Setting.getLabel(i18next.t("permission:State"), i18next.t("permission:State - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select disabled={!Setting.isLocalAdminUser(this.props.account)} virtual={false} style={{width: "100%"}} value={this.state.permission.state} onChange={(value => { <Select virtual={false} disabled={!Setting.isLocalAdminUser(this.props.account)} style={{width: "100%"}} value={this.state.permission.state} onChange={(value => {
if (this.state.permission.state !== value) { if (this.state.permission.state !== value) {
if (value === "Approved") { if (value === "Approved") {
this.updatePermissionField("approver", this.props.account.name); this.updatePermissionField("approver", this.props.account.name);
@ -378,14 +361,12 @@ class PermissionEditPage extends React.Component {
} }
this.updatePermissionField("state", value); this.updatePermissionField("state", value);
})}> })}
{ options={[
[ {value: "Approved", name: i18next.t("permission:Approved")},
{id: "Approved", name: i18next.t("permission:Approved")}, {value: "Pending", name: i18next.t("permission:Pending")},
{id: "Pending", name: i18next.t("permission:Pending")}, ].map((item) => Setting.getOption(item.name, item.value))}
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>) />
}
</Select>
</Col> </Col>
</Row> </Row>
</Card> </Card>

View File

@ -358,6 +358,13 @@ class PermissionListPage extends BaseListPage {
searchText: params.searchText, searchText: params.searchText,
searchedColumn: params.searchedColumn, 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, searchText: params.searchText,
searchedColumn: params.searchedColumn, 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 React from "react";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import {Button, Popconfirm, Table} from "antd"; import {Button, Popconfirm, Result, Table} from "antd";
import moment from "moment"; import moment from "moment";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import * as ProviderBackend from "./backend/ProviderBackend"; import * as ProviderBackend from "./backend/ProviderBackend";
@ -36,6 +36,7 @@ class ProviderListPage extends BaseListPage {
loading: false, loading: false,
searchText: "", searchText: "",
searchedColumn: "", searchedColumn: "",
isAuthorized: true,
}; };
} }
newProvider() { newProvider() {
@ -227,6 +228,17 @@ class ProviderListPage extends BaseListPage {
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total), 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 ( return (
<div> <div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={providers} rowKey="name" size="middle" bordered pagination={paginationProps} <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, searchText: params.searchText,
searchedColumn: params.searchedColumn, searchedColumn: params.searchedColumn,
}); });
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
} }
}); });
}; };

View File

@ -179,7 +179,7 @@ class ProviderTable extends React.Component {
// width: '120px', // width: '120px',
// render: (text, record, index) => { // render: (text, record, index) => {
// return ( // return (
// <Select virtual={false} style={{width: '100%'}} value={text} onChange={(value => { // <Select virtual={false} style={{width: '100%'}} value={text} onChange={(value => {
// this.updateField(table, index, 'alertType', value); // this.updateField(table, index, 'alertType', value);
// })}> // })}>
// { // {

View File

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

View File

@ -37,7 +37,11 @@ export const ResetModal = (props) => {
const handleOk = () => { const handleOk = () => {
if (dest === "") { if (dest === "") {
Setting.showMessage("error", i18next.t("user:Empty " + destType)); if (destType === "phone") {
Setting.showMessage("error", i18next.t("user:Phone cannot be empty"));
} else {
Setting.showMessage("error", i18next.t("user:Email cannot be empty"));
}
return; return;
} }
if (code === "") { if (code === "") {

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; 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 {UploadOutlined} from "@ant-design/icons";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
@ -37,6 +37,7 @@ class ResourceListPage extends BaseListPage {
searchedColumn: "", searchedColumn: "",
fileList: [], fileList: [],
uploading: false, 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), 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 ( return (
<div> <div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={resources} rowKey="name" size="middle" bordered pagination={paginationProps} <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, searchText: params.searchText,
searchedColumn: params.searchedColumn, searchedColumn: params.searchedColumn,
}); });
} else {
if (res.data.includes("Please login first")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
} }
}); });
}; };

View File

@ -16,11 +16,9 @@ import React from "react";
import {Button, Card, Col, Input, Row, Select, Switch} from "antd"; import {Button, Card, Col, Input, Row, Select, Switch} from "antd";
import * as RoleBackend from "./backend/RoleBackend"; import * as RoleBackend from "./backend/RoleBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as UserBackend from "./backend/UserBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";
import * as UserBackend from "./backend/UserBackend";
const {Option} = Select;
class RoleEditPage extends React.Component { class RoleEditPage extends React.Component {
constructor(props) { constructor(props) {
@ -113,11 +111,9 @@ class RoleEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} : {Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.role.owner} onChange={(value => {this.updateRoleField("owner", value);})}> <Select virtual={false} style={{width: "100%"}} value={this.state.role.owner} onChange={(value => {this.updateRoleField("owner", value);})}
{ options={this.state.organizations.map((organization) => Setting.getOption(organization.name, organization.name))
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>) } />
}
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -145,11 +141,10 @@ class RoleEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub users"), i18next.t("role:Sub users - Tooltip"))} : {Setting.getLabel(i18next.t("role:Sub users"), i18next.t("role:Sub users - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.role.users} onChange={(value => {this.updateRoleField("users", value);})}> <Select mode="tags" style={{width: "100%"}} value={this.state.role.users}
{ onChange={(value => {this.updateRoleField("users", value);})}
this.state.users.map((user, index) => <Option key={index} value={`${user.owner}/${user.name}`}>{`${user.owner}/${user.name}`}</Option>) options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))}
} />
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -157,11 +152,9 @@ class RoleEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} : {Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.role.roles} onChange={(value => {this.updateRoleField("roles", value);})}> <Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.role.roles} onChange={(value => {this.updateRoleField("roles", value);})}
{ options={this.state.roles.filter(role => (role.owner !== this.state.role.owner || role.name !== this.state.role.name)).map((role) => Setting.getOption(`${role.owner}/${role.name}`, `${role.owner}/${role.name}`))
this.state.roles.filter(role => (role.owner !== this.state.role.owner || role.name !== this.state.role.name)).map((role, index) => <Option key={index} value={`${role.owner}/${role.name}`}>{`${role.owner}/${role.name}`}</Option>) } />
}
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -171,11 +164,9 @@ class RoleEditPage extends React.Component {
<Col span={22} > <Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.role.domains} onChange={(value => { <Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.role.domains} onChange={(value => {
this.updateRoleField("domains", value); this.updateRoleField("domains", value);
})}> })}
{ options={this.state.role.domains?.map((domain) => Setting.getOption(domain, domain))
this.state.role.domains?.map((domain, index) => <Option key={index} value={domain}>{domain}</Option>) } />
}
</Select>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >

View File

@ -231,6 +231,13 @@ class RoleListPage extends BaseListPage {
searchText: params.searchText, searchText: params.searchText,
searchedColumn: params.searchedColumn, 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 React from "react";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import {Tag, Tooltip, message} from "antd"; import {Checkbox, Form, Modal, Tag, Tooltip, message, theme} from "antd";
import {QuestionCircleTwoTone} from "@ant-design/icons"; import {QuestionCircleTwoTone} from "@ant-design/icons";
import {isMobile as isMobileDevice} from "react-device-detect"; import {isMobile as isMobileDevice} from "react-device-detect";
import "./i18n"; import "./i18n";
@ -43,6 +43,13 @@ export const Countries = [{label: "English", key: "en", country: "US", alt: "Eng
{label: "Русский", key: "ru", country: "RU", alt: "Русский"}, {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 = { export const OtherProviderInfo = {
SMS: { SMS: {
"Aliyun SMS": { "Aliyun SMS": {
@ -502,6 +509,78 @@ export function isMobile() {
return isMobileDevice; return isMobileDevice;
} }
export function getTermsOfUseContent(url, setTermsOfUseContent) {
fetch(url, {
method: "GET",
}).then(r => {
r.text().then(setTermsOfUseContent);
});
}
export function isAgreementRequired(application) {
if (application) {
const agreementItem = application.signupItems.find(item => item.name === "Agreement");
if (agreementItem.rule === "None" || !agreementItem.rule) {
return false;
}
if (agreementItem && agreementItem.required) {
return true;
}
}
return false;
}
export function isDefaultTrue(application) {
const agreementItem = application.signupItems.find(item => item.name === "Agreement");
if (isAgreementRequired(application) && agreementItem.rule === "Signin (Default True)") {
return true;
}
return false;
}
export function renderAgreement(required, onClick, noStyle, layout, initialValue) {
return (
<Form.Item
name="agreement"
key="agreement"
valuePropName="checked"
rules={[
{
required: required,
message: i18next.t("signup:Please accept the agreement!"),
},
]}
{...layout}
noStyle={noStyle}
initialValue={initialValue}
>
<Checkbox style={{float: "left"}}>
{i18next.t("signup:Accept")}&nbsp;
<a onClick={onClick}>
{i18next.t("signup:Terms of Use")}
</a>
</Checkbox>
</Form.Item>
);
}
export function renderModal(isOpen, onOk, onCancel, doc) {
return (
<Modal
title={i18next.t("signup:Terms of Use")}
open={isOpen}
width={"55vw"}
closable={false}
okText={i18next.t("signup:Accept")}
cancelText={i18next.t("signup:Decline")}
onOk={onOk}
onCancel={onCancel}
>
<iframe title={"terms"} style={{border: 0, width: "100%", height: "60vh"}} srcDoc={doc} />
</Modal>
);
}
export function getFormattedDate(date) { export function getFormattedDate(date) {
if (date === undefined) { if (date === undefined) {
return null; return null;
@ -562,6 +641,14 @@ export function getAvatarColor(s) {
return colorList[hash % 4]; 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) { export function getLanguageText(text) {
if (!text.includes("|")) { if (!text.includes("|")) {
return text; return text;
@ -587,6 +674,11 @@ export function setLanguage(language) {
i18next.changeLanguage(language); i18next.changeLanguage(language);
} }
export function setTheme(themeKey) {
localStorage.setItem("theme", themeKey);
dispatchEvent(new Event("themeChange"));
}
export function getAcceptLanguage() { export function getAcceptLanguage() {
if (i18next.language === null || i18next.language === "") { if (i18next.language === null || i18next.language === "") {
return "en;q=0.9,en;q=0.8"; return "en;q=0.9,en;q=0.8";
@ -800,7 +892,7 @@ export function renderLoginLink(application, text) {
export function redirectToLoginPage(application, history) { export function redirectToLoginPage(application, history) {
const loginLink = getLoginLink(application); const loginLink = getLoginLink(application);
if (loginLink.indexOf("http") === 0 || loginLink.indexOf("https") === 0) { if (loginLink.indexOf("http") === 0 || loginLink.indexOf("https") === 0) {
openLink(loginLink); window.location.replace(loginLink);
} }
history.push(loginLink); history.push(loginLink);
} }
@ -904,6 +996,14 @@ export function getItem(label, key, icon, children, type) {
type, type,
}; };
} }
export function getOption(label, value) {
return {
label,
value,
};
}
function repeat(str, len) { function repeat(str, len) {
while (str.length < len) { while (str.length < len) {
str += str.substr(0, len - str.length); str += str.substr(0, len - str.length);

View File

@ -186,6 +186,12 @@ class SignupTable extends React.Component {
{id: "Normal", name: "Normal"}, {id: "Normal", name: "Normal"},
{id: "No verification", name: "No verification"}, {id: "No verification", name: "No verification"},
]; ];
} else if (record.name === "Agreement") {
options = [
{id: "None", name: "None"},
{id: "Signin", name: "Signin"},
{id: "Signin (Default True)", name: "Signin (Default True)"},
];
} }
if (options.length === 0) { if (options.length === 0) {
@ -195,11 +201,7 @@ class SignupTable extends React.Component {
return ( return (
<Select virtual={false} style={{width: "100%"}} value={text} onChange={(value => { <Select virtual={false} style={{width: "100%"}} value={text} onChange={(value => {
this.updateField(table, index, "rule", value); this.updateField(table, index, "rule", value);
})}> })} options={options.map(item => Setting.getOption(item.name, item.id))} />
{
options.map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
); );
}, },
}, },

View File

@ -288,6 +288,13 @@ class SyncerListPage extends BaseListPage {
searchText: params.searchText, searchText: params.searchText,
searchedColumn: params.searchedColumn, 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, searchText: params.searchText,
searchedColumn: params.searchedColumn, 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 SelectRegionBox from "./SelectRegionBox";
import WebAuthnCredentialTable from "./WebauthnCredentialTable"; import WebAuthnCredentialTable from "./WebauthnCredentialTable";
import ManagedAccountTable from "./ManagedAccountTable"; import ManagedAccountTable from "./ManagedAccountTable";
import PropertyTable from "./propertyTable";
import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css";
require("codemirror/theme/material-darker.css");
require("codemirror/mode/javascript/javascript");
const {Option} = Select; const {Option} = Select;
@ -261,12 +257,9 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:User type"), i18next.t("general:User type - Tooltip"))} : {Setting.getLabel(i18next.t("general:User type"), i18next.t("general:User type - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.user.type} onChange={(value => {this.updateUserField("type", value);})}> <Select virtual={false} style={{width: "100%"}} value={this.state.user.type} onChange={(value => {this.updateUserField("type", value);})}
{ options={["normal-user"].map(item => Setting.getOption(item, item))}
["normal-user"] />
.map((item, index) => <Option key={index} value={item}>{item}</Option>)
}
</Select>
</Col> </Col>
</Row> </Row>
); );
@ -288,18 +281,23 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Email"), i18next.t("general:Email - Tooltip"))} : {Setting.getLabel(i18next.t("general:Email"), i18next.t("general:Email - Tooltip"))} :
</Col> </Col>
<Col style={{paddingRight: "20px"}} span={11} > <Col style={{paddingRight: "20px"}} span={11} >
<Input value={this.state.user.email} {Setting.isLocalAdminUser(this.props.account) ?
disabled={disabled} (<Input value={this.state.user.email}
onChange={e => { disabled={disabled}
this.updateUserField("email", e.target.value); onChange={e => {
}} /> this.updateUserField("email", e.target.value);
}} />) :
(<Select virtual={false} value={this.state.user.email}
options={[Setting.getItem(this.state.user.email, this.state.user.email)]}
disabled={disabled}
onChange={e => {
this.updateUserField("email", e.target.value);
}} />)
}
</Col> </Col>
<Col span={11} > <Col span={11} >
{ {/* backend auto get the current user, so admin can not edit. Just self can reset*/}
!this.isSelf() ? null : ( {this.isSelf() ? <ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Email...")} destType={"email"} /> : null}
<ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Email...")} destType={"email"} />
)
}
</Col> </Col>
</Row> </Row>
); );
@ -310,14 +308,21 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Phone"), i18next.t("general:Phone - Tooltip"))} : {Setting.getLabel(i18next.t("general:Phone"), i18next.t("general:Phone - Tooltip"))} :
</Col> </Col>
<Col style={{paddingRight: "20px"}} span={11} > <Col style={{paddingRight: "20px"}} span={11} >
<Input value={this.state.user.phone} addonBefore={`+${this.state.application?.organizationObj.phonePrefix}`} {Setting.isLocalAdminUser(this.props.account) ?
disabled={disabled} <Input value={this.state.user.phone} addonBefore={`+${this.state.application?.organizationObj.phonePrefix}`}
onChange={e => { disabled={disabled}
this.updateUserField("phone", e.target.value); onChange={e => {
}} /> this.updateUserField("phone", e.target.value);
}} /> :
(<Select virtual={false} value={`+${this.state.application?.organizationObj.phonePrefix} ${this.state.user.phone}`}
options={[Setting.getItem(`+${this.state.application?.organizationObj.phonePrefix} ${this.state.user.phone}`, this.state.user.phone)]}
disabled={disabled}
onChange={e => {
this.updateUserField("phone", e.target.value);
}} />)}
</Col> </Col>
<Col span={11} > <Col span={11} >
{this.state.user.id === this.props.account?.id ? (<ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Phone...")} destType={"phone"} />) : null} {this.isSelf() ? (<ResetModal application={this.state.application} disabled={disabled} buttonText={i18next.t("user:Reset Phone...")} destType={"phone"} />) : null}
</Col> </Col>
</Row> </Row>
); );
@ -401,16 +406,14 @@ class UserEditPage extends React.Component {
<Col span={22} > <Col span={22} >
{ {
this.state.application?.organizationObj.tags?.length > 0 ? ( this.state.application?.organizationObj.tags?.length > 0 ? (
<Select virtual={false} style={{width: "100%"}} value={this.state.user.tag} onChange={(value => {this.updateUserField("tag", value);})}> <Select virtual={false} style={{width: "100%"}} value={this.state.user.tag}
{ onChange={(value => {this.updateUserField("tag", value);})}
this.state.application.organizationObj.tags?.map((tag, index) => { options={this.state.application.organizationObj.tags?.map((tag) => {
const tokens = tag.split("|"); const tokens = tag.split("|");
const value = tokens[0]; const value = tokens[0];
const displayValue = Setting.getLanguage() !== "zh" ? tokens[0] : tokens[1]; const displayValue = Setting.getLanguage() !== "zh" ? tokens[0] : tokens[1];
return <Option key={index} value={value}>{displayValue}</Option>; return Setting.getOption(displayValue, value);
}) })} />
}
</Select>
) : ( ) : (
<Input value={this.state.user.tag} onChange={e => { <Input value={this.state.user.tag} onChange={e => {
this.updateUserField("tag", e.target.value); this.updateUserField("tag", e.target.value);
@ -427,11 +430,10 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Signup application"), i18next.t("general:Signup application - Tooltip"))} : {Setting.getLabel(i18next.t("general:Signup application"), i18next.t("general:Signup application - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} disabled={disabled} value={this.state.user.signupApplication} onChange={(value => {this.updateUserField("signupApplication", value);})}> <Select virtual={false} style={{width: "100%"}} disabled={disabled} value={this.state.user.signupApplication}
{ onChange={(value => {this.updateUserField("signupApplication", value);})}
this.state.applications.map((application, index) => <Option key={index} value={application.name}>{application.name}</Option>) options={this.state.applications.map((application) => Setting.getOption(application.name, application.name))
} } />
</Select>
</Col> </Col>
</Row> </Row>
); );
@ -472,7 +474,7 @@ class UserEditPage extends React.Component {
<div style={{marginBottom: 20}}> <div style={{marginBottom: 20}}>
{ {
(this.state.application === null || this.state.user === null) ? null : ( (this.state.application === null || this.state.user === null) ? null : (
this.state.application?.providers.filter(providerItem => Setting.isProviderVisible(providerItem)).map((providerItem, index) => this.state.application?.providers.filter(providerItem => Setting.isProviderVisible(providerItem)).map((providerItem) =>
(providerItem.provider.category === "OAuth") ? ( (providerItem.provider.category === "OAuth") ? (
<OAuthWidget key={providerItem.name} labelSpan={(Setting.isMobile()) ? 10 : 3} user={this.state.user} application={this.state.application} providerItem={providerItem} account={this.props.account} onUnlinked={() => {return this.unlinked();}} /> <OAuthWidget key={providerItem.name} labelSpan={(Setting.isMobile()) ? 10 : 3} user={this.state.user} application={this.state.application} providerItem={providerItem} account={this.props.account} onUnlinked={() => {return this.unlinked();}} />
) : ( ) : (
@ -490,13 +492,10 @@ class UserEditPage extends React.Component {
return ( return (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <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>
<Col span={22} > <Col span={22} >
<CodeMirror <PropertyTable properties={this.state.user.properties} onUpdateTable={(value) => {this.updateUserField("properties", value);}} />
value={JSON.stringify(this.state.user.properties, null, 4)}
options={{mode: "javascript", theme: "material-darker"}}
/>
</Col> </Col>
</Row> </Row>
); );
@ -507,7 +506,7 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("user:Is admin"), i18next.t("user:Is admin - Tooltip"))} : {Setting.getLabel(i18next.t("user:Is admin"), i18next.t("user:Is admin - Tooltip"))} :
</Col> </Col>
<Col span={(Setting.isMobile()) ? 22 : 2} > <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); this.updateUserField("isAdmin", checked);
}} /> }} />
</Col> </Col>
@ -520,7 +519,7 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("user:Is global admin"), i18next.t("user:Is global admin - Tooltip"))} : {Setting.getLabel(i18next.t("user:Is global admin"), i18next.t("user:Is global admin - Tooltip"))} :
</Col> </Col>
<Col span={(Setting.isMobile()) ? 22 : 2} > <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); this.updateUserField("isGlobalAdmin", checked);
}} /> }} />
</Col> </Col>

View File

@ -14,7 +14,7 @@
import React from "react"; import React from "react";
import {Link} from "react-router-dom"; 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 {UploadOutlined} from "@ant-design/icons";
import moment from "moment"; import moment from "moment";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
@ -38,6 +38,7 @@ class UserListPage extends BaseListPage {
loading: false, loading: false,
searchText: "", searchText: "",
searchedColumn: "", searchedColumn: "",
isAuthorized: true,
}; };
} }
@ -353,6 +354,7 @@ class UserListPage extends BaseListPage {
<Popconfirm <Popconfirm
title={`Sure to delete user: ${record.name} ?`} title={`Sure to delete user: ${record.name} ?`}
onConfirm={() => this.deleteUser(index)} onConfirm={() => this.deleteUser(index)}
disabled={disabled}
> >
<Button disabled={disabled} style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button> <Button disabled={disabled} style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
</Popconfirm> </Popconfirm>
@ -369,6 +371,17 @@ class UserListPage extends BaseListPage {
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total), 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 ( return (
<div> <div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={users} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps} <Table scroll={{x: "max-content"}} columns={columns} dataSource={users} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
@ -411,6 +424,13 @@ class UserListPage extends BaseListPage {
if (users.length > 0) { if (users.length > 0) {
this.getOrganization(users[0].owner); this.getOrganization(users[0].owner);
} }
} else {
if (res.msg.includes("Unauthorized")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
} }
}); });
} else { } else {
@ -432,6 +452,13 @@ class UserListPage extends BaseListPage {
if (users.length > 0) { if (users.length > 0) {
this.getOrganization(users[0].owner); 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, searchText: params.searchText,
searchedColumn: params.searchedColumn, 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 {authConfig} from "./Auth";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import i18next from "i18next"; import i18next from "i18next";
import RedirectForm from "../common/RedirectForm";
class AuthCallback extends React.Component { class AuthCallback extends React.Component {
constructor(props) { constructor(props) {
@ -27,6 +28,9 @@ class AuthCallback extends React.Component {
this.state = { this.state = {
classes: props, classes: props,
msg: null, msg: null,
samlResponse: "",
relayState: "",
redirectUrl: "",
}; };
} }
@ -164,9 +168,17 @@ class AuthCallback extends React.Component {
const from = innerParams.get("from"); const from = innerParams.get("from");
Setting.goToLinkSoft(this, from); Setting.goToLinkSoft(this, from);
} else if (responseType === "saml") { } else if (responseType === "saml") {
const SAMLResponse = res.data; if (res.data2.method === "POST") {
const redirectUri = res.data2; this.setState({
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`); 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 { } else {
this.setState({ this.setState({
@ -177,6 +189,10 @@ class AuthCallback extends React.Component {
} }
render() { render() {
if (this.state.samlResponse !== "") {
return <RedirectForm samlResponse={this.state.samlResponse} redirectUrl={this.state.redirectUrl} relayState={this.state.relayState} />;
}
return ( return (
<div style={{display: "flex", justifyContent: "center", alignItems: "center"}}> <div style={{display: "flex", justifyContent: "center", alignItems: "center"}}>
{ {

View File

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

View File

@ -320,9 +320,9 @@ class ForgetPage extends React.Component {
> >
{ {
this.state.isFixed ? <Input disabled /> : this.state.isFixed ? <Input disabled /> :
<Select <Select virtual={false}
key={this.state.verifyType} key={this.state.verifyType}
virtual={false} style={{textAlign: "left"}} style={{textAlign: "left"}}
defaultValue={this.state.verifyType} defaultValue={this.state.verifyType}
disabled={this.state.username === ""} disabled={this.state.username === ""}
placeholder={i18next.t("forget:Choose email or phone")} placeholder={i18next.t("forget:Choose email or phone")}

View File

@ -53,12 +53,16 @@ class LoginPage extends React.Component {
samlResponse: "", samlResponse: "",
relayState: "", relayState: "",
redirectUrl: "", redirectUrl: "",
isTermsOfUseVisible: false,
termsOfUseContent: "",
}; };
if (this.state.type === "cas" && props.match?.params.casApplicationName !== undefined) { if (this.state.type === "cas" && props.match?.params.casApplicationName !== undefined) {
this.state.owner = props.match?.params.owner; this.state.owner = props.match?.params.owner;
this.state.applicationName = props.match?.params.casApplicationName; this.state.applicationName = props.match?.params.casApplicationName;
} }
this.form = React.createRef();
} }
componentDidMount() { componentDidMount() {
@ -102,6 +106,7 @@ class LoginPage extends React.Component {
}); });
} else { } else {
// Setting.showMessage("error", res.msg); // Setting.showMessage("error", res.msg);
this.onUpdateApplication(null);
this.setState({ this.setState({
application: res.data, application: res.data,
msg: res.msg, msg: res.msg,
@ -121,7 +126,9 @@ class LoginPage extends React.Component {
this.onUpdateApplication(application); this.onUpdateApplication(application);
this.setState({ this.setState({
application: application, application: application,
}); }, () => Setting.getTermsOfUseContent(this.state.application.termsOfUse, res => {
this.setState({termsOfUseContent: res});
}));
}); });
} else { } else {
OrganizationBackend.getDefaultApplication("admin", this.state.owner) OrganizationBackend.getDefaultApplication("admin", this.state.owner)
@ -131,8 +138,11 @@ class LoginPage extends React.Component {
this.setState({ this.setState({
application: res.data, application: res.data,
applicationName: res.data.name, applicationName: res.data.name,
}); }, () => Setting.getTermsOfUseContent(this.state.application.termsOfUse, res => {
this.setState({termsOfUseContent: res});
}));
} else { } else {
this.onUpdateApplication(null);
Setting.showMessage("error", res.msg); Setting.showMessage("error", res.msg);
} }
}); });
@ -318,15 +328,15 @@ class LoginPage extends React.Component {
const accessToken = res.data; const accessToken = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`); Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
} else if (responseType === "saml") { } else if (responseType === "saml") {
const SAMLResponse = res.data; if (res.data2.method === "POST") {
const redirectUri = res.data2;
if (this.state.application.assertionConsumerUrl !== "") {
this.setState({ this.setState({
samlResponse: res.data, samlResponse: res.data,
redirectUrl: res.data2, redirectUrl: res.data2.redirectUrl,
relayState: oAuthParams.relayState, relayState: oAuthParams.relayState,
}); });
} else { } else {
const SAMLResponse = res.data;
const redirectUri = res.data2.redirectUrl;
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`); Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);
} }
} }
@ -381,6 +391,7 @@ class LoginPage extends React.Component {
onFinish={(values) => {this.onFinish(values);}} onFinish={(values) => {this.onFinish(values);}}
style={{width: "300px"}} style={{width: "300px"}}
size="large" size="large"
ref={this.form}
> >
<Form.Item <Form.Item
hidden={true} hidden={true}
@ -454,11 +465,20 @@ class LoginPage extends React.Component {
} }
</Row> </Row>
<Form.Item> <Form.Item>
<Form.Item name="autoSignin" valuePropName="checked" noStyle> {
<Checkbox style={{float: "left"}} disabled={!application.enablePassword}> Setting.isAgreementRequired(application) ?
{i18next.t("login:Auto sign in")} Setting.renderAgreement(true, () => {
</Checkbox> this.setState({
</Form.Item> isTermsOfUseVisible: true,
});
}, true, {}, Setting.isDefaultTrue(application)) : (
<Form.Item name="autoSignin" valuePropName="checked" noStyle>
<Checkbox style={{float: "left"}} disabled={!application.enablePassword}>
{i18next.t("login:Auto sign in")}
</Checkbox>
</Form.Item>
)
}
{ {
Setting.renderForgetLink(application, i18next.t("login:Forgot password?")) Setting.renderForgetLink(application, i18next.t("login:Forgot password?"))
} }
@ -614,13 +634,13 @@ class LoginPage extends React.Component {
this.sendSilentSigninData("signing-in"); this.sendSilentSigninData("signing-in");
const values = {}; const values = {};
values["application"] = this.state.application.name; values["application"] = application.name;
this.onFinish(values); this.onFinish(values);
} }
if (application.enableAutoSignin) { if (application.enableAutoSignin) {
const values = {}; const values = {};
values["application"] = this.state.application.name; values["application"] = application.name;
this.onFinish(values); this.onFinish(values);
} }
@ -635,7 +655,7 @@ class LoginPage extends React.Component {
<br /> <br />
<SelfLoginButton account={this.props.account} onClick={() => { <SelfLoginButton account={this.props.account} onClick={() => {
const values = {}; const values = {};
values["application"] = this.state.application.name; values["application"] = application.name;
this.onFinish(values); this.onFinish(values);
}} /> }} />
<br /> <br />
@ -800,7 +820,7 @@ class LoginPage extends React.Component {
return ( return (
<React.Fragment> <React.Fragment>
<CustomGithubCorner /> <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}} />} {Setting.inIframe() || Setting.isMobile() ? null : <div dangerouslySetInnerHTML={{__html: application.formCss}} />}
<div className="login-panel"> <div className="login-panel">
<div className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}> <div className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}>
@ -825,6 +845,19 @@ class LoginPage extends React.Component {
{ {
this.renderForm(application) this.renderForm(application)
} }
{
Setting.renderModal(this.state.isTermsOfUseVisible, () => {
this.form.current.setFieldsValue({agreement: true});
this.setState({
isTermsOfUseVisible: false,
});
}, () => {
this.form.current.setFieldsValue({agreement: false});
this.setState({
isTermsOfUseVisible: false,
});
}, this.state.termsOfUseContent)
}
</div> </div>
</div> </div>
</div> </div>

View File

@ -13,8 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Link} from "react-router-dom"; import {Button, Form, Input, Result} from "antd";
import {Button, Checkbox, Form, Input, Modal, Result} from "antd";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import * as AuthBackend from "./AuthBackend"; import * as AuthBackend from "./AuthBackend";
import * as ProviderButton from "./ProviderButton"; import * as ProviderButton from "./ProviderButton";
@ -113,7 +112,9 @@ class SignupPage extends React.Component {
}); });
if (application !== null && application !== undefined) { if (application !== null && application !== undefined) {
this.getTermsofuseContent(application.termsOfUse); Setting.getTermsOfUseContent(application.termsOfUse, res => {
this.setState({termsOfUseContent: res});
});
} }
}); });
} }
@ -134,16 +135,6 @@ class SignupPage extends React.Component {
return this.props.application ?? this.state.application; return this.props.application ?? this.state.application;
} }
getTermsofuseContent(url) {
fetch(url, {
method: "GET",
}).then(r => {
r.text().then(res => {
this.setState({termsOfUseContent: res});
});
});
}
onUpdateAccount(account) { onUpdateAccount(account) {
this.props.onUpdateAccount(account); this.props.onUpdateAccount(account);
} }
@ -484,58 +475,28 @@ class SignupPage extends React.Component {
); );
} else if (signupItem.name === "Agreement") { } else if (signupItem.name === "Agreement") {
return ( return (
<Form.Item Setting.renderAgreement(Setting.isAgreementRequired(application), () => {
name="agreement" this.setState({
key="agreement" isTermsOfUseVisible: true,
valuePropName="checked" });
rules={[ }, false, tailFormItemLayout, Setting.isDefaultTrue(application))
{
required: required,
message: i18next.t("signup:Please accept the agreement!"),
},
]}
{...tailFormItemLayout}
>
<Checkbox>
{i18next.t("signup:Accept")}&nbsp;
<Link onClick={() => {
this.setState({
isTermsOfUseVisible: true,
});
}}>
{i18next.t("signup:Terms of Use")}
</Link>
</Checkbox>
</Form.Item>
); );
} }
} }
renderModal() { renderModal() {
return ( return (
<Modal Setting.renderModal(this.state.isTermsOfUseVisible, () => {
title={i18next.t("signup:Terms of Use")} this.form.current.setFieldsValue({agreement: true});
open={this.state.isTermsOfUseVisible} this.setState({
width={"55vw"} isTermsOfUseVisible: false,
closable={false} });
okText={i18next.t("signup:Accept")} }, () => {
cancelText={i18next.t("signup:Decline")} this.form.current.setFieldsValue({agreement: false});
onOk={() => { this.setState({
this.form.current.setFieldsValue({agreement: true}); isTermsOfUseVisible: false,
this.setState({ });
isTermsOfUseVisible: false, }, this.state.termsOfUseContent)
});
}}
onCancel={() => {
this.form.current.setFieldsValue({agreement: false});
this.setState({
isTermsOfUseVisible: false,
});
this.props.history.goBack();
}}
>
<iframe title={"terms"} style={{border: 0, width: "100%", height: "60vh"}} srcDoc={this.state.termsOfUseContent} />
</Modal>
); );
} }
@ -637,7 +598,7 @@ class SignupPage extends React.Component {
return ( return (
<React.Fragment> <React.Fragment>
<CustomGithubCorner /> <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}} />} {Setting.inIframe() || Setting.isMobile() ? null : <div dangerouslySetInnerHTML={{__html: application.formCss}} />}
<div className="login-panel" > <div className="login-panel" >
<div className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}> <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

@ -18,8 +18,6 @@ import i18next from "i18next";
import * as UserBackend from "../backend/UserBackend"; import * as UserBackend from "../backend/UserBackend";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
const {Option} = Select;
class AffiliationSelect extends React.Component { class AffiliationSelect extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -99,20 +97,16 @@ class AffiliationSelect extends React.Component {
this.updateUserField("affiliation", e.target.value); this.updateUserField("affiliation", e.target.value);
}} /> }} />
) : ( ) : (
<Select virtual={false} style={{width: "100%"}} value={this.props.user.affiliation} onChange={(value => { <Select virtual={false} style={{width: "100%"}} value={this.props.user.affiliation}
const name = value; onChange={(value => {
const affiliationOption = Setting.getArrayItem(this.state.affiliationOptions, "name", name); const name = value;
const id = affiliationOption.id; const affiliationOption = Setting.getArrayItem(this.state.affiliationOptions, "name", name);
this.updateUserField("affiliation", name); const id = affiliationOption.id;
this.updateUserField("score", id); this.updateUserField("affiliation", name);
})}> this.updateUserField("score", id);
{ })}
<Option key={0} value={""}>(empty)</Option> options={[Setting.getOption("(empty)", "")].concat(this.state.affiliationOptions.map((affiliationOption) => Setting.getOption(affiliationOption.name, affiliationOption.name))
} )} />
{
this.state.affiliationOptions.map((affiliationOption, index) => <Option key={affiliationOption.id} value={affiliationOption.name}>{affiliationOption.name}</Option>)
}
</Select>
) )
} }
</Col> </Col>

View File

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

View File

@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip", "Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Code-Anmeldung aktivieren", "Enable code signin": "Code-Anmeldung aktivieren",
"Enable code signin - Tooltip": "Aktiviere Codeanmeldung - Tooltip", "Enable code signin - Tooltip": "Aktiviere Codeanmeldung - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel", "Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Aktiviere Anmeldesession - Tooltip", "Enable signin session - Tooltip": "Aktiviere Anmeldesession - Tooltip",
"Enable signup": "Anmeldung aktivieren", "Enable signup": "Anmeldung aktivieren",
@ -156,7 +158,10 @@
"Click to Upload": "Click to Upload", "Click to Upload": "Click to Upload",
"Client IP": "Client-IP", "Client IP": "Client-IP",
"Close": "Close", "Close": "Close",
"Compact": "Compact",
"Created time": "Erstellte Zeit", "Created time": "Erstellte Zeit",
"Dark": "Dark",
"Default": "Default",
"Default application": "Default application", "Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip", "Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Standard Avatar", "Default avatar": "Standard Avatar",
@ -233,6 +238,8 @@
"Roles - Tooltip": "Roles - Tooltip", "Roles - Tooltip": "Roles - Tooltip",
"Save": "Speichern", "Save": "Speichern",
"Save & Exit": "Speichern & Beenden", "Save & Exit": "Speichern & Beenden",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "Anmelde-URL", "Signin URL": "Anmelde-URL",
"Signin URL - Tooltip": "sign in url", "Signin URL - Tooltip": "sign in url",
"Signup URL": "Registrierungs-URL", "Signup URL": "Registrierungs-URL",
@ -241,7 +248,7 @@
"Signup application - Tooltip": "Signup application - Tooltip", "Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Die von Ihnen besuchte Seite existiert leider nicht.", "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, 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": "State",
"State - Tooltip": "State - Tooltip", "State - Tooltip": "State - Tooltip",
"Successfully added": "Successfully added", "Successfully added": "Successfully added",
@ -709,6 +716,7 @@
"Country/Region": "Land/Region", "Country/Region": "Land/Region",
"Country/Region - Tooltip": "Country/Region", "Country/Region - Tooltip": "Country/Region",
"Edit User": "Benutzer bearbeiten", "Edit User": "Benutzer bearbeiten",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Empty input!", "Empty input!": "Empty input!",
"Homepage": "Homepage", "Homepage": "Homepage",
"Homepage - Tooltip": "Startseite - Tooltip", "Homepage - Tooltip": "Startseite - Tooltip",
@ -723,6 +731,7 @@
"Is forbidden - Tooltip": "Whether the account is disabled", "Is forbidden - Tooltip": "Whether the account is disabled",
"Is global admin": "Ist globaler Admin", "Is global admin": "Ist globaler Admin",
"Is global admin - Tooltip": "Is the application global administrator", "Is global admin - Tooltip": "Is the application global administrator",
"Keys": "Keys",
"Link": "Link", "Link": "Link",
"Location": "Standort", "Location": "Standort",
"Location - Tooltip": "Standort - Tooltip", "Location - Tooltip": "Standort - Tooltip",
@ -736,8 +745,10 @@
"Old Password": "Altes Passwort", "Old Password": "Altes Passwort",
"Password": "Passwort", "Password": "Passwort",
"Password Set": "Passwort setzen", "Password Set": "Passwort setzen",
"Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources", "Please select avatar from resources": "Please select avatar from resources",
"Properties": "Eigenschaften", "Properties": "Eigenschaften",
"Properties - Tooltip": "Properties - Tooltip",
"Re-enter New": "Neu erneut eingeben", "Re-enter New": "Neu erneut eingeben",
"Reset Email...": "Reset Email...", "Reset Email...": "Reset Email...",
"Reset Phone...": "Telefon zurücksetzen...", "Reset Phone...": "Telefon zurücksetzen...",
@ -753,6 +764,7 @@
"Unlink": "Link aufheben", "Unlink": "Link aufheben",
"Upload (.xlsx)": "Upload (.xlsx)", "Upload (.xlsx)": "Upload (.xlsx)",
"Upload a photo": "Foto hochladen", "Upload a photo": "Foto hochladen",
"Values": "Values",
"WebAuthn credentials": "WebAuthn credentials", "WebAuthn credentials": "WebAuthn credentials",
"input password": "Passwort eingeben" "input password": "Passwort eingeben"
}, },

View File

@ -33,6 +33,8 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip", "Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Enable code signin", "Enable code signin": "Enable code signin",
"Enable code signin - Tooltip": "Enable code signin - Tooltip", "Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel", "Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip", "Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Enable signup", "Enable signup": "Enable signup",
@ -156,7 +158,10 @@
"Click to Upload": "Click to Upload", "Click to Upload": "Click to Upload",
"Client IP": "Client IP", "Client IP": "Client IP",
"Close": "Close", "Close": "Close",
"Compact": "Compact",
"Created time": "Created time", "Created time": "Created time",
"Dark": "Dark",
"Default": "Default",
"Default application": "Default application", "Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip", "Default application - Tooltip": "Default application - Tooltip",
"Default avatar": "Default avatar", "Default avatar": "Default avatar",
@ -233,6 +238,8 @@
"Roles - Tooltip": "Roles - Tooltip", "Roles - Tooltip": "Roles - Tooltip",
"Save": "Save", "Save": "Save",
"Save & Exit": "Save & Exit", "Save & Exit": "Save & Exit",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "Signin URL", "Signin URL": "Signin URL",
"Signin URL - Tooltip": "Signin URL - Tooltip", "Signin URL - Tooltip": "Signin URL - Tooltip",
"Signup URL": "Signup URL", "Signup URL": "Signup URL",
@ -241,7 +248,7 @@
"Signup application - Tooltip": "Signup application - Tooltip", "Signup application - Tooltip": "Signup application - Tooltip",
"Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist.", "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, 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": "State",
"State - Tooltip": "State - Tooltip", "State - Tooltip": "State - Tooltip",
"Successfully added": "Successfully added", "Successfully added": "Successfully added",
@ -709,6 +716,7 @@
"Country/Region": "Country/Region", "Country/Region": "Country/Region",
"Country/Region - Tooltip": "Country/Region - Tooltip", "Country/Region - Tooltip": "Country/Region - Tooltip",
"Edit User": "Edit User", "Edit User": "Edit User",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Empty input!", "Empty input!": "Empty input!",
"Homepage": "Homepage", "Homepage": "Homepage",
"Homepage - Tooltip": "Homepage - Tooltip", "Homepage - Tooltip": "Homepage - Tooltip",
@ -723,6 +731,7 @@
"Is forbidden - Tooltip": "Is forbidden - Tooltip", "Is forbidden - Tooltip": "Is forbidden - Tooltip",
"Is global admin": "Is global admin", "Is global admin": "Is global admin",
"Is global admin - Tooltip": "Is global admin - Tooltip", "Is global admin - Tooltip": "Is global admin - Tooltip",
"Keys": "Keys",
"Link": "Link", "Link": "Link",
"Location": "Location", "Location": "Location",
"Location - Tooltip": "Location - Tooltip", "Location - Tooltip": "Location - Tooltip",
@ -736,8 +745,10 @@
"Old Password": "Old Password", "Old Password": "Old Password",
"Password": "Password", "Password": "Password",
"Password Set": "Password Set", "Password Set": "Password Set",
"Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources", "Please select avatar from resources": "Please select avatar from resources",
"Properties": "Properties", "Properties": "Properties",
"Properties - Tooltip": "Properties - Tooltip",
"Re-enter New": "Re-enter New", "Re-enter New": "Re-enter New",
"Reset Email...": "Reset Email...", "Reset Email...": "Reset Email...",
"Reset Phone...": "Reset Phone...", "Reset Phone...": "Reset Phone...",
@ -753,6 +764,7 @@
"Unlink": "Unlink", "Unlink": "Unlink",
"Upload (.xlsx)": "Upload (.xlsx)", "Upload (.xlsx)": "Upload (.xlsx)",
"Upload a photo": "Upload a photo", "Upload a photo": "Upload a photo",
"Values": "Values",
"WebAuthn credentials": "WebAuthn credentials", "WebAuthn credentials": "WebAuthn credentials",
"input password": "input password" "input password": "input password"
}, },

View File

@ -5,7 +5,23 @@
"My Account": "Mi cuenta", "My Account": "Mi cuenta",
"Sign Up": "Registrarme" "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": { "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 SAML metadata URL": "Copiar SAML metadata URL",
"Copy prompt page URL": "Copy prompt page URL", "Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copiar URL de inicio de sesión", "Copy signin page URL": "Copiar URL de inicio de sesión",
@ -17,39 +33,59 @@
"Enable WebAuthn signin - Tooltip": "Habilitar inicio de sesión de WebAuthn - Tooltip", "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": "Habilitar inicio de sesión por código",
"Enable code signin - Tooltip": "Habilitar inicio de sesión por código - Tooltip", "Enable code signin - Tooltip": "Habilitar inicio de sesión por código - Tooltip",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip", "Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Habilitar nuevos registros", "Enable signup": "Habilitar nuevos registros",
"Enable signup - Tooltip": "Habilitar nuevos registros - Tooltip", "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", "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": "Grant types",
"Grant types - Tooltip": "Grant types - Tooltip", "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", "New Application": "Nueva Aplicación",
"None": "None",
"Password ON": "Constraseña ON", "Password ON": "Constraseña ON",
"Password ON - Tooltip": "Constraseña ON - Tooltip", "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", "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", "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": "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": "URLs de redirección",
"Redirect URLs - Tooltip": "URL de redirección - Tooltip", "Redirect URLs - Tooltip": "URL de redirección - Tooltip",
"Refresh token expire": "Expiración del Refresh Token", "Refresh token expire": "Expiración del Refresh Token",
"Refresh token expire - Tooltip": "Expiración del Refresh Token - Tooltip", "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": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip", "SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copiado al portapapeles con éxito", "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 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", "Signin session": "Signin session",
"Sign Up Error": "Sign Up Error",
"Signup items": "Signup items", "Signup items": "Signup items",
"Signup items - Tooltip": "Signup items - Tooltip", "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", "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": "Expiración del Token",
"Token expire - Tooltip": "Expiración del Token - Tooltip", "Token expire - Tooltip": "Expiración del Token - Tooltip",
"Token format": "Formato del Token", "Token format": "Formato del Token",
"Token format - Tooltip": "Formato del Token - Tooltip", "Token format - Tooltip": "Formato del Token - Tooltip",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page", "You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
"Rule": "Rule",
"None": "None",
"Always": "Always"
}, },
"cert": { "cert": {
"Bit size": "Tamaño del Bit", "Bit size": "Tamaño del Bit",
@ -97,13 +133,14 @@
"Please input your username!": "Por favor ingrese su nombre de usuario!", "Please input your username!": "Por favor ingrese su nombre de usuario!",
"Reset": "Reiniciar", "Reset": "Reiniciar",
"Retrieve password": "Recuperar Contraseña", "Retrieve password": "Recuperar Contraseña",
"Unknown forget type": "Tipo de olvido desconocido", "Unknown forget type: ": "Unknown forget type: ",
"Verify": "Verificar" "Verify": "Verificar"
}, },
"general": { "general": {
"Action": "Acción", "Action": "Acción",
"Adapter": "Adaptador", "Adapter": "Adaptador",
"Adapter - Tooltip": "Adaptador - Tooltip", "Adapter - Tooltip": "Adaptador - Tooltip",
"Adapters": "Adapters",
"Add": "Agregar", "Add": "Agregar",
"Affiliation URL": "URL de Afiliación", "Affiliation URL": "URL de Afiliación",
"Affiliation URL - Tooltip": "URL de Afiliación - Tooltip", "Affiliation URL - Tooltip": "URL de Afiliación - Tooltip",
@ -120,7 +157,13 @@
"Certs": "Certificados", "Certs": "Certificados",
"Click to Upload": "Click para subir archivo", "Click to Upload": "Click para subir archivo",
"Client IP": "IP del Cliente", "Client IP": "IP del Cliente",
"Close": "Close",
"Compact": "Compact",
"Created time": "Fecha de creación", "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": "Avatar por defecto",
"Default avatar - Tooltip": "Avatar por defecto - Tooltip", "Default avatar - Tooltip": "Avatar por defecto - Tooltip",
"Delete": "Eliminar", "Delete": "Eliminar",
@ -132,6 +175,10 @@
"Edit": "Editar", "Edit": "Editar",
"Email": "Email", "Email": "Email",
"Email - Tooltip": "Email - Tooltip", "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": "Favicon",
"Favicon - Tooltip": "Favicon - Tooltip", "Favicon - Tooltip": "Favicon - Tooltip",
"First name": "Nombre", "First name": "Nombre",
@ -145,11 +192,14 @@
"Is enabled - Tooltip": "Está habilitado - Tooltip", "Is enabled - Tooltip": "Está habilitado - Tooltip",
"LDAPs": "LDAPs", "LDAPs": "LDAPs",
"LDAPs - Tooltip": "LDAPs - Tooltip", "LDAPs - Tooltip": "LDAPs - Tooltip",
"Languages": "Languages",
"Languages - Tooltip": "Languages - Tooltip",
"Last name": "Apellido", "Last name": "Apellido",
"Logo": "Logo", "Logo": "Logo",
"Logo - Tooltip": "Logo - Tooltip", "Logo - Tooltip": "Logo - Tooltip",
"Master password": "Contraseña maestra", "Master password": "Contraseña maestra",
"Master password - Tooltip": "Contraseña maestra - Tooltip", "Master password - Tooltip": "Contraseña maestra - Tooltip",
"Menu": "Menu",
"Method": "Metodo", "Method": "Metodo",
"Model": "Modelo", "Model": "Modelo",
"Model - Tooltip": "Modelo - Tooltip", "Model - Tooltip": "Modelo - Tooltip",
@ -188,6 +238,8 @@
"Roles - Tooltip": "Roles - Tooltip", "Roles - Tooltip": "Roles - Tooltip",
"Save": "Guardar", "Save": "Guardar",
"Save & Exit": "Guardar & Volver", "Save & Exit": "Guardar & Volver",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "URL de inicio de sesión", "Signin URL": "URL de inicio de sesión",
"Signin URL - Tooltip": "URL de inicio de sesión - Tooltip", "Signin URL - Tooltip": "URL de inicio de sesión - Tooltip",
"Signup URL": "URL de registro", "Signup URL": "URL de registro",
@ -196,12 +248,16 @@
"Signup application - Tooltip": "Aplicación de inicio de sesión - Tooltip", "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 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, 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": "Estado",
"State - Tooltip": "Estado - Tooltip", "State - Tooltip": "Estado - Tooltip",
"Successfully added": "Successfully added",
"Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved",
"Swagger": "Swagger", "Swagger": "Swagger",
"Sync": "Sincronizador", "Sync": "Sincronizador",
"Syncers": "Sincronizadores", "Syncers": "Sincronizadores",
"SysInfo": "SysInfo",
"Timestamp": "Timestamp", "Timestamp": "Timestamp",
"Tokens": "Tokens", "Tokens": "Tokens",
"URL": "URL", "URL": "URL",
@ -250,6 +306,7 @@
"Continue with": "Continuar con", "Continue with": "Continuar con",
"Email or phone": "Email o teléfono", "Email or phone": "Email o teléfono",
"Forgot password?": "¿Olvidó su contraseña?", "Forgot password?": "¿Olvidó su contraseña?",
"Loading": "Loading",
"Logging out...": "Cerrando su sesión...", "Logging out...": "Cerrando su sesión...",
"No account?": "¿No estás registrado?", "No account?": "¿No estás registrado?",
"Or sign in with another account": "O inicia sesión con otra cuenta", "Or sign in with another account": "O inicia sesión con otra cuenta",
@ -259,14 +316,16 @@
"Please input your password!": "¡Por favor ingrese su contraseña!", "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 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!", "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": "Iniciar de sesión",
"Sign in with WebAuthn": "Iniciar de sesión con WebAuthn", "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}", "Sign in with {type}": "Iniciar de sesión con {type}",
"Signing in...": "Iniciando de sesión...", "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!", "The input is not valid Email or Phone!": "El valor ingresado no es un Email o Teléfono válido!",
"To access": "Para ingresar", "To access": "Para ingresar",
"Verification Code": "Verification Code",
"WebAuthn": "WebAuthn",
"sign up now": "Regístrate ahora", "sign up now": "Regístrate ahora",
"username, Email or phone": "nombre de usuario, correo electrónico o teléfono" "username, Email or phone": "nombre de usuario, correo electrónico o teléfono"
}, },
@ -282,17 +341,20 @@
"Default avatar": "Avatar por defecto", "Default avatar": "Avatar por defecto",
"Edit Organization": "Editar Organización", "Edit Organization": "Editar Organización",
"Favicon": "Favicon", "Favicon": "Favicon",
"InitScore": "InitScore",
"Is profile public": "Es el perfil publico", "Is profile public": "Es el perfil publico",
"Is profile public - Tooltip": "Es el perfil publico - Tooltip", "Is profile public - Tooltip": "Es el perfil publico - Tooltip",
"Modify rule": "Modify rule",
"New Organization": "Nueva Organización", "New Organization": "Nueva Organización",
"Soft deletion": "Eliminación Soft", "Soft deletion": "Eliminación Soft",
"Soft deletion - Tooltip": "Eliminación Soft - Tooltip", "Soft deletion - Tooltip": "Eliminación Soft - Tooltip",
"Tags": "Etiquetas", "Tags": "Etiquetas",
"Tags - Tooltip": "Etiquetas - Tooltip", "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": "URL del sitio web",
"Website URL - Tooltip": "URL del sitio web - Tooltip", "Website URL - Tooltip": "URL del sitio web - Tooltip"
"modifyRule": "modifyRule",
"viewRule": "viewRule"
}, },
"payment": { "payment": {
"Confirm your invoice information": "Confirma la información de tu factura", "Confirm your invoice information": "Confirma la información de tu factura",
@ -349,14 +411,20 @@
"permission": { "permission": {
"Actions": "Acciones", "Actions": "Acciones",
"Actions - Tooltip": "Acciones - Tooltip", "Actions - Tooltip": "Acciones - Tooltip",
"Admin": "Admin",
"Allow": "Allow",
"Approve time": "Fecha de aprobación", "Approve time": "Fecha de aprobación",
"Approve time - Tooltip": "Fecha de aprobación - Tooltip", "Approve time - Tooltip": "Fecha de aprobación - Tooltip",
"Approved": "Approved",
"Approver": "Aprobador", "Approver": "Aprobador",
"Approver - Tooltip": "Aprobador - Tooltip", "Approver - Tooltip": "Aprobador - Tooltip",
"Deny": "Deny",
"Edit Permission": "Editar Permiso", "Edit Permission": "Editar Permiso",
"Effect": "Effect", "Effect": "Effect",
"Effect - Tooltip": "Effect - Tooltip", "Effect - Tooltip": "Effect - Tooltip",
"New Permission": "Nuevo Permiso", "New Permission": "Nuevo Permiso",
"Pending": "Pending",
"Read": "Read",
"Resource type": "Tipo de recurso", "Resource type": "Tipo de recurso",
"Resource type - Tooltip": "Tipo de recurso - Tooltip", "Resource type - Tooltip": "Tipo de recurso - Tooltip",
"Resources": "Recursos", "Resources": "Recursos",
@ -364,7 +432,9 @@
"State": "Estado", "State": "Estado",
"State - Tooltip": "Estado - Tooltip", "State - Tooltip": "Estado - Tooltip",
"Submitter": "Submitter", "Submitter": "Submitter",
"Submitter - Tooltip": "Submitter - Tooltip" "Submitter - Tooltip": "Submitter - Tooltip",
"TreeNode": "TreeNode",
"Write": "Write"
}, },
"product": { "product": {
"Alipay": "Alipay", "Alipay": "Alipay",
@ -373,9 +443,12 @@
"CNY": "CNY", "CNY": "CNY",
"Currency": "Moneda", "Currency": "Moneda",
"Currency - Tooltip": "Moneda - Tooltip", "Currency - Tooltip": "Moneda - Tooltip",
"Description": "Description",
"Description - Tooltip": "Description - Tooltip",
"Detail": "Detalle", "Detail": "Detalle",
"Detail - Tooltip": "Detalle - Tooltip", "Detail - Tooltip": "Detalle - Tooltip",
"Edit Product": "Editar Producto", "Edit Product": "Editar Producto",
"I have completed the payment": "I have completed the payment",
"Image": "Imagen", "Image": "Imagen",
"Image - Tooltip": "Imagen - Tooltip", "Image - Tooltip": "Imagen - Tooltip",
"New Product": "Nuevo producto", "New Product": "Nuevo producto",
@ -384,6 +457,8 @@
"Payment providers - Tooltip": "Pasarelas de pago - Tooltip", "Payment providers - Tooltip": "Pasarelas de pago - Tooltip",
"Paypal": "Paypal", "Paypal": "Paypal",
"Placing order...": "Creando su pedido...", "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": "Precio",
"Price - Tooltip": "Precio - Tooltip", "Price - Tooltip": "Precio - Tooltip",
"Quantity": "Cantidad", "Quantity": "Cantidad",
@ -417,6 +492,9 @@
"Bucket": "Bucket", "Bucket": "Bucket",
"Bucket - Tooltip": "Bucket - Tooltip", "Bucket - Tooltip": "Bucket - Tooltip",
"Can not parse Metadata": "Can not parse Metadata", "Can not parse Metadata": "Can not parse Metadata",
"Can signin": "Can signin",
"Can signup": "Can signup",
"Can unlink": "Can unlink",
"Category": "Categoria", "Category": "Categoria",
"Category - Tooltip": "Categoria - Tooltip", "Category - Tooltip": "Categoria - Tooltip",
"Channel No.": "Channel No.", "Channel No.": "Channel No.",
@ -439,6 +517,8 @@
"Email Content - Tooltip": "Contenido del Email - Tooltip", "Email Content - Tooltip": "Contenido del Email - Tooltip",
"Email Title": "Titulo del Email", "Email Title": "Titulo del Email",
"Email Title - Tooltip": "Titulo del Email - Tooltip", "Email Title - Tooltip": "Titulo del Email - Tooltip",
"Enable QR code": "Enable QR code",
"Enable QR code - Tooltip": "Enable QR code - Tooltip",
"Endpoint": "Endpoint", "Endpoint": "Endpoint",
"Endpoint (Intranet)": "Endpoint (Intranet)", "Endpoint (Intranet)": "Endpoint (Intranet)",
"Host": "Host", "Host": "Host",
@ -456,14 +536,18 @@
"New Provider": "Nuevo Proveedor", "New Provider": "Nuevo Proveedor",
"Parse": "Parsear", "Parse": "Parsear",
"Parse Metadata successfully": "Metadata parseada con éxito", "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": "Puerto",
"Port - Tooltip": "Puerto - Tooltip", "Port - Tooltip": "Puerto - Tooltip",
"Prompted": "Prompted",
"Provider URL": "URL del Proveedor", "Provider URL": "URL del Proveedor",
"Provider URL - Tooltip": "URL del Proveedor - Tooltip", "Provider URL - Tooltip": "URL del Proveedor - Tooltip",
"Region ID": "Region ID", "Region ID": "Region ID",
"Region ID - Tooltip": "Region ID - Tooltip", "Region ID - Tooltip": "Region ID - Tooltip",
"Region endpoint for Internet": "Region endpoint for Internet", "Region endpoint for Internet": "Region endpoint for Internet",
"Region endpoint for Intranet": "Region endpoint for Intranet", "Region endpoint for Intranet": "Region endpoint for Intranet",
"Required": "Required",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)", "SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS account": "SMS account", "SMS account": "SMS account",
"SMS account - Tooltip": "SMS account - Tooltip", "SMS account - Tooltip": "SMS account - Tooltip",
@ -500,19 +584,16 @@
"Test Connection": "Probar Conexión", "Test Connection": "Probar Conexión",
"Test Email": "Probar Email", "Test Email": "Probar Email",
"Test Email - Tooltip": "Probar Email - Tooltip", "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": "Token URL",
"Token URL - Tooltip": "Token URL - Tooltip", "Token URL - Tooltip": "Token URL - Tooltip",
"Type": "Tipo", "Type": "Tipo",
"Type - Tooltip": "Tipo - Tooltip", "Type - Tooltip": "Tipo - Tooltip",
"UserInfo URL": "UserInfo URL", "UserInfo URL": "UserInfo URL",
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip", "UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
"alertType": "alertType", "Visible": "Visible",
"canSignIn": "canSignIn", "admin (share)": "admin (share)",
"canSignUp": "canSignUp", "alertType": "alertType"
"canUnlink": "canUnlink",
"prompted": "prompted",
"required": "required",
"visible": "visible"
}, },
"record": { "record": {
"Is Triggered": "Esta activado" "Is Triggered": "Esta activado"
@ -597,6 +678,18 @@
"Table primary key": "Clave primaria de la tabla", "Table primary key": "Clave primaria de la tabla",
"Table primary key - Tooltip": "Clave primaria de la tabla - Tooltip" "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": { "token": {
"Access token": "Token de acceso", "Access token": "Token de acceso",
"Authorization code": "Código de autorización", "Authorization code": "Código de autorización",
@ -623,6 +716,7 @@
"Country/Region": "Pais/Región", "Country/Region": "Pais/Región",
"Country/Region - Tooltip": "Pais/Región - Tooltip", "Country/Region - Tooltip": "Pais/Región - Tooltip",
"Edit User": "Editar usuario", "Edit User": "Editar usuario",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Campo requerido!", "Empty input!": "Campo requerido!",
"Homepage": "Página de Inicio", "Homepage": "Página de Inicio",
"Homepage - Tooltip": "Página de Inicio - Tooltip", "Homepage - Tooltip": "Página de Inicio - Tooltip",
@ -637,9 +731,11 @@
"Is forbidden - Tooltip": "Está prohibido - Tooltip", "Is forbidden - Tooltip": "Está prohibido - Tooltip",
"Is global admin": "Es admin global", "Is global admin": "Es admin global",
"Is global admin - Tooltip": "Es admin global - Tooltip", "Is global admin - Tooltip": "Es admin global - Tooltip",
"Keys": "Keys",
"Link": "Enlace", "Link": "Enlace",
"Location": "Ubicación", "Location": "Ubicación",
"Location - Tooltip": "Ubicación - Tooltip", "Location - Tooltip": "Ubicación - Tooltip",
"Managed accounts": "Managed accounts",
"Modify password...": "Cambiar contraseña...", "Modify password...": "Cambiar contraseña...",
"New Email": "Nuevo Email", "New Email": "Nuevo Email",
"New Password": "Nueva Contraseña", "New Password": "Nueva Contraseña",
@ -649,7 +745,10 @@
"Old Password": "Contraseña anterior", "Old Password": "Contraseña anterior",
"Password": "Contraseña", "Password": "Contraseña",
"Password Set": "Password Set", "Password Set": "Password Set",
"Phone cannot be empty": "Phone cannot be empty",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Propiedades", "Properties": "Propiedades",
"Properties - Tooltip": "Properties - Tooltip",
"Re-enter New": "Reingrese de nuevo", "Re-enter New": "Reingrese de nuevo",
"Reset Email...": "Cambiar Email...", "Reset Email...": "Cambiar Email...",
"Reset Phone...": "Cambiar Phone...", "Reset Phone...": "Cambiar Phone...",
@ -665,6 +764,7 @@
"Unlink": "Desvincular", "Unlink": "Desvincular",
"Upload (.xlsx)": "Subir archivo (.xlsx)", "Upload (.xlsx)": "Subir archivo (.xlsx)",
"Upload a photo": "Subir foto", "Upload a photo": "Subir foto",
"Values": "Values",
"WebAuthn credentials": "WebAuthn credentials", "WebAuthn credentials": "WebAuthn credentials",
"input password": "input password" "input password": "input password"
}, },

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