Compare commits

...

55 Commits

Author SHA1 Message Date
8698f4111a feat: add all remaining Goth providers to Casdoor OAuth login (#1484)
* feat: add Amazon support as OAuth 3rd-party login

* refactor: comebine the same URLs

* refactor: use hyper component to create login button

* feat: add all remaining Goth providers to Casdoor OAuth login

* refactor: remove redundant props

* fix: check provider auth url and params
2023-02-04 12:20:18 +08:00
fdccb8b22b feat: Test whether the page can be accessed (#1517)
* feat: add new line

* feat: Test whether the page can be accessed

* feat: Change the e2e order

* feat: add Test Retries

* feat: change yarn.lock

* feat: add new line
2023-02-03 19:59:28 +08:00
19e7d0b0bd refactor: improve code reuse rate (#1515) 2023-02-02 16:43:51 +08:00
f6a502f7ff feat: add user password in ldap server search result (#1513)
* fix: ldap server search return inconsistent cn attribute

* feat: add user password in ldap server search result
2023-02-02 15:33:44 +08:00
b34e16b145 fix: table do not have unique key (#1512) 2023-02-02 13:53:18 +08:00
11b56c340f Add refineUser() in generateJwtToken() 2023-02-02 00:34:56 +08:00
cc6ea1b60e feat: fix application edit page crash and language icon position (#1511)
* fix: widget position and color

* feat: fix applicationEdit crush
2023-02-01 23:11:48 +08:00
95b32d5ebf feat: support customize theme (#1500)
* refactor: simplify functions and improve variable naming

* feat: add themeEditor component

* feat: support customize theme

* chore: resolve conflict and add LICENCE

* chore: format code

* refactor: use icon replace background url

* feat: improve organization and application theme editor
2023-02-01 22:06:40 +08:00
b47baa06e1 fix: remove "Agreement" in edit application error (#1506) 2023-01-31 22:56:19 +08:00
wht
24a824d394 feat: return the correct error message in the Edit Model (#1504) 2023-01-30 22:19:42 +08:00
75b8357de8 Add properties to UserWithoutThirdIdp 2023-01-29 21:51:01 +08:00
087405dad2 Fix isAllowedInDemoMode() 2023-01-26 17:56:29 +08:00
6a6a1fa920 feat: fix missing phone number prefix in login screen (#1492)
fix: #1489
2023-01-24 23:19:44 +08:00
907d18d2e9 Fix missing roles and permissions in user table 2023-01-23 00:36:55 +08:00
a728e083eb feat: reduce the size of token's user object (#1487)
* fix: Reduce the size of token, especially the user object (#1170)

* fix: Reduce the size of token, especially the user object (#1170)

* fix: Reduce the size of token, especially the user object (#1170)

Co-authored-by: Zayn Xie <84443886+xiaoniuren99@users.noreply.github.com>
2023-01-21 09:30:23 +08:00
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
145 changed files with 6244 additions and 1931 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: [ go-tests ]
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

@ -160,7 +160,7 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool { func isAllowedInDemoMode(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
if method == "POST" { if method == "POST" {
if strings.HasPrefix(urlPath, "/api/login") || urlPath == "/api/logout" || urlPath == "/api/signup" || urlPath == "/api/send-verification-code" { if strings.HasPrefix(urlPath, "/api/login") || urlPath == "/api/logout" || urlPath == "/api/signup" || urlPath == "/api/send-verification-code" || urlPath == "/api/send-email" {
return true return true
} else if urlPath == "/api/update-user" { } else if urlPath == "/api/update-user" {
// Allow ordinary users to update their own information // Allow ordinary users to update their own information

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
} }
@ -170,13 +174,36 @@ func (c *ApiController) GetApplicationLogin() {
} }
func setHttpClient(idProvider idp.IdProvider, providerType string) { func setHttpClient(idProvider idp.IdProvider, providerType string) {
if providerType == "GitHub" || providerType == "Google" || providerType == "Facebook" || providerType == "LinkedIn" || providerType == "Steam" || providerType == "Line" { if isProxyProviderType(providerType) {
idProvider.SetHttpClient(proxy.ProxyHttpClient) idProvider.SetHttpClient(proxy.ProxyHttpClient)
} else { } else {
idProvider.SetHttpClient(proxy.DefaultHttpClient) idProvider.SetHttpClient(proxy.DefaultHttpClient)
} }
} }
func isProxyProviderType(providerType string) bool {
providerTypes := []string{
"GitHub",
"Google",
"Facebook",
"LinkedIn",
"Steam",
"Line",
"Amazon",
"Instagram",
"TikTok",
"Twitter",
"Uber",
"Yahoo",
}
for _, v := range providerTypes {
if strings.EqualFold(v, providerType) {
return true
}
}
return false
}
// Login ... // Login ...
// @Title Login // @Title Login
// @Tag Login API // @Tag Login API
@ -205,7 +232,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 +249,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 +266,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,12 +286,6 @@ 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 {
@ -401,54 +428,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)
@ -464,13 +498,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

@ -105,14 +105,34 @@ func handleSearch(w ldapserver.ResponseWriter, m *ldapserver.Message) {
} }
for i := 0; i < len(users); i++ { for i := 0; i < len(users); i++ {
user := users[i] user := users[i]
dn := fmt.Sprintf("cn=%s,%s", user.DisplayName, string(r.BaseObject())) dn := fmt.Sprintf("cn=%s,%s", user.Name, string(r.BaseObject()))
e := ldapserver.NewSearchResultEntry(dn) e := ldapserver.NewSearchResultEntry(dn)
e.AddAttribute("cn", message.AttributeValue(user.Name)) e.AddAttribute("cn", message.AttributeValue(user.Name))
e.AddAttribute("uid", message.AttributeValue(user.Name)) e.AddAttribute("uid", message.AttributeValue(user.Name))
e.AddAttribute("email", message.AttributeValue(user.Email)) e.AddAttribute("email", message.AttributeValue(user.Email))
e.AddAttribute("mobile", message.AttributeValue(user.Phone)) e.AddAttribute("mobile", message.AttributeValue(user.Phone))
e.AddAttribute("userPassword", message.AttributeValue(getUserPasswordWithType(user)))
// e.AddAttribute("postalAddress", message.AttributeValue(user.Address[0])) // e.AddAttribute("postalAddress", message.AttributeValue(user.Address[0]))
w.Write(e) w.Write(e)
} }
w.Write(res) w.Write(res)
} }
// get user password with hash type prefix
// TODO not handle salt yet
// @return {md5}5f4dcc3b5aa765d61d8327deb882cf99
func getUserPasswordWithType(user *object.User) string {
org := object.GetOrganizationByUser(user)
if org.PasswordType == "" || org.PasswordType == "plain" {
return user.Password
}
prefix := org.PasswordType
if prefix == "salt" {
prefix = "sha256"
} else if prefix == "md5-salt" {
prefix = "md5"
} else if prefix == "pbkdf2-salt" {
prefix = "pbkdf2"
}
return fmt.Sprintf("{%s}%s", prefix, user.Password)
}

View File

@ -80,7 +80,7 @@ func (c *ApiController) UpdateModel() {
return return
} }
c.Data["json"] = wrapActionResponse(object.UpdateModel(id, &model)) c.Data["json"] = wrapErrorResponse(object.UpdateModelWithCheck(id, &model))
c.ServeJSON() c.ServeJSON()
} }

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,23 +4,17 @@
"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)",
@ -29,7 +23,6 @@
"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"
@ -54,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",
@ -66,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",
@ -94,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"
@ -121,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"
@ -144,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.",
@ -166,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,23 +4,17 @@
"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)",
@ -29,7 +23,6 @@
"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"
@ -54,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",
@ -66,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",
@ -94,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"
@ -121,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"
@ -144,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.",
@ -166,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,23 +4,17 @@
"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)",
@ -29,7 +23,6 @@
"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"
@ -54,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",
@ -66,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",
@ -94,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"
@ -121,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"
@ -144,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.",
@ -166,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,23 +4,17 @@
"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)",
@ -29,7 +23,6 @@
"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"
@ -54,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",
@ -66,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",
@ -94,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"
@ -121,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"
@ -144,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.",
@ -166,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,23 +4,17 @@
"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)",
@ -29,7 +23,6 @@
"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"
@ -54,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",
@ -66,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",
@ -94,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"
@ -121,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"
@ -144,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.",
@ -166,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,23 +4,17 @@
"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)",
@ -29,7 +23,6 @@
"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"
@ -54,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",
@ -66,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",
@ -94,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"
@ -121,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"
@ -144,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.",
@ -166,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,23 +4,17 @@
"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)",
@ -29,7 +23,6 @@
"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"
@ -54,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",
@ -66,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",
@ -94,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"
@ -121,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"
@ -144,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.",
@ -166,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,171 +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 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": "不支持该类型的提供商: %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,30 +25,59 @@ import (
"github.com/markbates/goth" "github.com/markbates/goth"
"github.com/markbates/goth/providers/amazon" "github.com/markbates/goth/providers/amazon"
"github.com/markbates/goth/providers/apple" "github.com/markbates/goth/providers/apple"
"github.com/markbates/goth/providers/auth0"
"github.com/markbates/goth/providers/azureadv2" "github.com/markbates/goth/providers/azureadv2"
"github.com/markbates/goth/providers/battlenet"
"github.com/markbates/goth/providers/bitbucket" "github.com/markbates/goth/providers/bitbucket"
"github.com/markbates/goth/providers/box"
"github.com/markbates/goth/providers/cloudfoundry"
"github.com/markbates/goth/providers/dailymotion"
"github.com/markbates/goth/providers/deezer"
"github.com/markbates/goth/providers/digitalocean" "github.com/markbates/goth/providers/digitalocean"
"github.com/markbates/goth/providers/discord" "github.com/markbates/goth/providers/discord"
"github.com/markbates/goth/providers/dropbox" "github.com/markbates/goth/providers/dropbox"
"github.com/markbates/goth/providers/eveonline"
"github.com/markbates/goth/providers/facebook" "github.com/markbates/goth/providers/facebook"
"github.com/markbates/goth/providers/fitbit"
"github.com/markbates/goth/providers/gitea" "github.com/markbates/goth/providers/gitea"
"github.com/markbates/goth/providers/github" "github.com/markbates/goth/providers/github"
"github.com/markbates/goth/providers/gitlab" "github.com/markbates/goth/providers/gitlab"
"github.com/markbates/goth/providers/google" "github.com/markbates/goth/providers/google"
"github.com/markbates/goth/providers/heroku" "github.com/markbates/goth/providers/heroku"
"github.com/markbates/goth/providers/influxcloud"
"github.com/markbates/goth/providers/instagram" "github.com/markbates/goth/providers/instagram"
"github.com/markbates/goth/providers/intercom"
"github.com/markbates/goth/providers/kakao" "github.com/markbates/goth/providers/kakao"
"github.com/markbates/goth/providers/lastfm"
"github.com/markbates/goth/providers/line" "github.com/markbates/goth/providers/line"
"github.com/markbates/goth/providers/linkedin" "github.com/markbates/goth/providers/linkedin"
"github.com/markbates/goth/providers/mailru"
"github.com/markbates/goth/providers/meetup"
"github.com/markbates/goth/providers/microsoftonline" "github.com/markbates/goth/providers/microsoftonline"
"github.com/markbates/goth/providers/naver"
"github.com/markbates/goth/providers/nextcloud"
"github.com/markbates/goth/providers/onedrive"
"github.com/markbates/goth/providers/oura"
"github.com/markbates/goth/providers/patreon"
"github.com/markbates/goth/providers/paypal" "github.com/markbates/goth/providers/paypal"
"github.com/markbates/goth/providers/salesforce" "github.com/markbates/goth/providers/salesforce"
"github.com/markbates/goth/providers/shopify" "github.com/markbates/goth/providers/shopify"
"github.com/markbates/goth/providers/slack" "github.com/markbates/goth/providers/slack"
"github.com/markbates/goth/providers/soundcloud"
"github.com/markbates/goth/providers/spotify"
"github.com/markbates/goth/providers/steam" "github.com/markbates/goth/providers/steam"
"github.com/markbates/goth/providers/strava"
"github.com/markbates/goth/providers/stripe"
"github.com/markbates/goth/providers/tiktok"
"github.com/markbates/goth/providers/tumblr" "github.com/markbates/goth/providers/tumblr"
"github.com/markbates/goth/providers/twitter" "github.com/markbates/goth/providers/twitch"
"github.com/markbates/goth/providers/twitterv2"
"github.com/markbates/goth/providers/typetalk"
"github.com/markbates/goth/providers/uber"
"github.com/markbates/goth/providers/wepay"
"github.com/markbates/goth/providers/xero"
"github.com/markbates/goth/providers/yahoo" "github.com/markbates/goth/providers/yahoo"
"github.com/markbates/goth/providers/yammer"
"github.com/markbates/goth/providers/yandex" "github.com/markbates/goth/providers/yandex"
"github.com/markbates/goth/providers/zoom" "github.com/markbates/goth/providers/zoom"
"golang.org/x/oauth2" "golang.org/x/oauth2"
@ -77,11 +106,41 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: azureadv2.New(clientId, clientSecret, redirectUrl, azureadv2.ProviderOptions{Tenant: "common"}), Provider: azureadv2.New(clientId, clientSecret, redirectUrl, azureadv2.ProviderOptions{Tenant: "common"}),
Session: &azureadv2.Session{}, Session: &azureadv2.Session{},
} }
case "Auth0":
idp = GothIdProvider{
Provider: auth0.New(clientId, clientSecret, redirectUrl, "casdoor.auth0.com"),
Session: &auth0.Session{},
}
case "BattleNet":
idp = GothIdProvider{
Provider: battlenet.New(clientId, clientSecret, redirectUrl),
Session: &battlenet.Session{},
}
case "Bitbucket": case "Bitbucket":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: bitbucket.New(clientId, clientSecret, redirectUrl), Provider: bitbucket.New(clientId, clientSecret, redirectUrl),
Session: &bitbucket.Session{}, Session: &bitbucket.Session{},
} }
case "Box":
idp = GothIdProvider{
Provider: box.New(clientId, clientSecret, redirectUrl),
Session: &box.Session{},
}
case "CloudFoundry":
idp = GothIdProvider{
Provider: cloudfoundry.New("", clientId, clientSecret, redirectUrl),
Session: &cloudfoundry.Session{},
}
case "Dailymotion":
idp = GothIdProvider{
Provider: dailymotion.New(clientId, clientSecret, redirectUrl),
Session: &dailymotion.Session{},
}
case "Deezer":
idp = GothIdProvider{
Provider: deezer.New(clientId, clientSecret, redirectUrl),
Session: &deezer.Session{},
}
case "DigitalOcean": case "DigitalOcean":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: digitalocean.New(clientId, clientSecret, redirectUrl), Provider: digitalocean.New(clientId, clientSecret, redirectUrl),
@ -97,6 +156,16 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: dropbox.New(clientId, clientSecret, redirectUrl), Provider: dropbox.New(clientId, clientSecret, redirectUrl),
Session: &dropbox.Session{}, Session: &dropbox.Session{},
} }
case "EveOnline":
idp = GothIdProvider{
Provider: eveonline.New(clientId, clientSecret, redirectUrl),
Session: &eveonline.Session{},
}
case "Fitbit":
idp = GothIdProvider{
Provider: fitbit.New(clientId, clientSecret, redirectUrl),
Session: &fitbit.Session{},
}
case "Facebook": case "Facebook":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: facebook.New(clientId, clientSecret, redirectUrl), Provider: facebook.New(clientId, clientSecret, redirectUrl),
@ -127,16 +196,31 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: heroku.New(clientId, clientSecret, redirectUrl), Provider: heroku.New(clientId, clientSecret, redirectUrl),
Session: &heroku.Session{}, Session: &heroku.Session{},
} }
case "InfluxCloud":
idp = GothIdProvider{
Provider: influxcloud.New(clientId, clientSecret, redirectUrl),
Session: &influxcloud.Session{},
}
case "Instagram": case "Instagram":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: instagram.New(clientId, clientSecret, redirectUrl), Provider: instagram.New(clientId, clientSecret, redirectUrl),
Session: &instagram.Session{}, Session: &instagram.Session{},
} }
case "Intercom":
idp = GothIdProvider{
Provider: intercom.New(clientId, clientSecret, redirectUrl),
Session: &intercom.Session{},
}
case "Kakao": case "Kakao":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: kakao.New(clientId, clientSecret, redirectUrl), Provider: kakao.New(clientId, clientSecret, redirectUrl),
Session: &kakao.Session{}, Session: &kakao.Session{},
} }
case "Lastfm":
idp = GothIdProvider{
Provider: lastfm.New(clientId, clientSecret, redirectUrl),
Session: &lastfm.Session{},
}
case "Linkedin": case "Linkedin":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: linkedin.New(clientId, clientSecret, redirectUrl), Provider: linkedin.New(clientId, clientSecret, redirectUrl),
@ -147,11 +231,46 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: line.New(clientId, clientSecret, redirectUrl), Provider: line.New(clientId, clientSecret, redirectUrl),
Session: &line.Session{}, Session: &line.Session{},
} }
case "Mailru":
idp = GothIdProvider{
Provider: mailru.New(clientId, clientSecret, redirectUrl),
Session: &mailru.Session{},
}
case "Meetup":
idp = GothIdProvider{
Provider: meetup.New(clientId, clientSecret, redirectUrl),
Session: &meetup.Session{},
}
case "MicrosoftOnline": case "MicrosoftOnline":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: microsoftonline.New(clientId, clientSecret, redirectUrl), Provider: microsoftonline.New(clientId, clientSecret, redirectUrl),
Session: &microsoftonline.Session{}, Session: &microsoftonline.Session{},
} }
case "Naver":
idp = GothIdProvider{
Provider: naver.New(clientId, clientSecret, redirectUrl),
Session: &naver.Session{},
}
case "Nextcloud":
idp = GothIdProvider{
Provider: nextcloud.New(clientId, clientSecret, redirectUrl),
Session: &nextcloud.Session{},
}
case "OneDrive":
idp = GothIdProvider{
Provider: onedrive.New(clientId, clientSecret, redirectUrl),
Session: &onedrive.Session{},
}
case "Oura":
idp = GothIdProvider{
Provider: oura.New(clientId, clientSecret, redirectUrl),
Session: &oura.Session{},
}
case "Patreon":
idp = GothIdProvider{
Provider: patreon.New(clientId, clientSecret, redirectUrl),
Session: &patreon.Session{},
}
case "Paypal": case "Paypal":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: paypal.New(clientId, clientSecret, redirectUrl), Provider: paypal.New(clientId, clientSecret, redirectUrl),
@ -172,26 +291,81 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: slack.New(clientId, clientSecret, redirectUrl), Provider: slack.New(clientId, clientSecret, redirectUrl),
Session: &slack.Session{}, Session: &slack.Session{},
} }
case "Soundcloud":
idp = GothIdProvider{
Provider: soundcloud.New(clientId, clientSecret, redirectUrl),
Session: &soundcloud.Session{},
}
case "Spotify":
idp = GothIdProvider{
Provider: spotify.New(clientId, clientSecret, redirectUrl),
Session: &spotify.Session{},
}
case "Steam": case "Steam":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: steam.New(clientSecret, redirectUrl), Provider: steam.New(clientSecret, redirectUrl),
Session: &steam.Session{}, Session: &steam.Session{},
} }
case "Strava":
idp = GothIdProvider{
Provider: strava.New(clientId, clientSecret, redirectUrl),
Session: &strava.Session{},
}
case "Stripe":
idp = GothIdProvider{
Provider: stripe.New(clientId, clientSecret, redirectUrl),
Session: &stripe.Session{},
}
case "TikTok":
idp = GothIdProvider{
Provider: tiktok.New(clientId, clientSecret, redirectUrl),
Session: &tiktok.Session{},
}
case "Tumblr": case "Tumblr":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: tumblr.New(clientId, clientSecret, redirectUrl), Provider: tumblr.New(clientId, clientSecret, redirectUrl),
Session: &tumblr.Session{}, Session: &tumblr.Session{},
} }
case "Twitch":
idp = GothIdProvider{
Provider: twitch.New(clientId, clientSecret, redirectUrl),
Session: &twitch.Session{},
}
case "Twitter": case "Twitter":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: twitter.New(clientId, clientSecret, redirectUrl), Provider: twitterv2.New(clientId, clientSecret, redirectUrl),
Session: &twitter.Session{}, Session: &twitterv2.Session{},
}
case "Typetalk":
idp = GothIdProvider{
Provider: typetalk.New(clientId, clientSecret, redirectUrl),
Session: &typetalk.Session{},
}
case "Uber":
idp = GothIdProvider{
Provider: uber.New(clientId, clientSecret, redirectUrl),
Session: &uber.Session{},
}
case "Wepay":
idp = GothIdProvider{
Provider: wepay.New(clientId, clientSecret, redirectUrl),
Session: &wepay.Session{},
}
case "Xero":
idp = GothIdProvider{
Provider: xero.New(clientId, clientSecret, redirectUrl),
Session: &xero.Session{},
} }
case "Yahoo": case "Yahoo":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: yahoo.New(clientId, clientSecret, redirectUrl), Provider: yahoo.New(clientId, clientSecret, redirectUrl),
Session: &yahoo.Session{}, Session: &yahoo.Session{},
} }
case "Yammer":
idp = GothIdProvider{
Provider: yammer.New(clientId, clientSecret, redirectUrl),
Session: &yammer.Session{},
}
case "Yandex": case "Yandex":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: yandex.New(clientId, clientSecret, redirectUrl), Provider: yandex.New(clientId, clientSecret, redirectUrl),
@ -232,6 +406,9 @@ func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) {
// to call the function to obtain accessToken // to call the function to obtain accessToken
value = url.Values{} value = url.Values{}
value.Add("code", code) value.Add("code", code)
if idp.Provider.Name() == "twitterv2" || idp.Provider.Name() == "fitbit" {
value.Add("oauth_verifier", "casdoor-verifier")
}
} }
accessToken, err := idp.Session.Authorize(idp.Provider, value) accessToken, err := idp.Session.Authorize(idp.Provider, value)
if err != nil { if err != nil {

View File

@ -98,7 +98,61 @@ func GetIdProvider(typ string, subType string, clientId string, clientSecret str
return nil return nil
} }
var gothList = []string{"Apple", "AzureAD", "Slack", "Steam", "Line"} var gothList = []string{
"Apple",
"AzureAD",
"Slack",
"Steam",
"Line",
"Amazon",
"Auth0",
"BattleNet",
"Bitbucket",
"Box",
"CloudFoundry",
"Dailymotion",
"Deezer",
"DigitalOcean",
"Discord",
"Dropbox",
"EveOnline",
"Fitbit",
"Gitea",
"Heroku",
"InfluxCloud",
"Instagram",
"Intercom",
"Kakao",
"Lastfm",
"Mailru",
"Meetup",
"MicrosoftOnline",
"Naver",
"Nextcloud",
"OneDrive",
"Oura",
"Patreon",
"Paypal",
"SalesForce",
"Shopify",
"Soundcloud",
"Spotify",
"Strava",
"Stripe",
"TikTok",
"Tumblr",
"Twitch",
"Twitter",
"Typetalk",
"Uber",
"VK",
"Wepay",
"Xero",
"Yahoo",
"Yammer",
"Yandex",
"Zoom",
}
func isGothSupport(provider string) bool { func isGothSupport(provider string) bool {
for _, value := range gothList { for _, value := range gothList {

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,29 +50,31 @@ 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"`
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"` GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
OrganizationObj *Organization `xorm:"-" json:"organizationObj"` OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
ClientId string `xorm:"varchar(100)" json:"clientId"` ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"` ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"` RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"` TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
ExpireInHours int `json:"expireInHours"` ExpireInHours int `json:"expireInHours"`
RefreshExpireInHours int `json:"refreshExpireInHours"` RefreshExpireInHours int `json:"refreshExpireInHours"`
SignupUrl string `xorm:"varchar(200)" json:"signupUrl"` SignupUrl string `xorm:"varchar(200)" json:"signupUrl"`
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"` SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"` ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"` AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"`
TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"` TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"`
SignupHtml string `xorm:"mediumtext" json:"signupHtml"` SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
SigninHtml string `xorm:"mediumtext" json:"signinHtml"` SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
FormCss string `xorm:"text" json:"formCss"` ThemeData *ThemeData `xorm:"json" json:"themeData"`
FormOffset int `json:"formOffset"` FormCss string `xorm:"text" json:"formCss"`
FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"` FormOffset int `json:"formOffset"`
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"` FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"`
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
} }
func GetApplicationCount(owner, field, value string) int { func GetApplicationCount(owner, field, value string) int {
@ -286,7 +288,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 +304,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 +331,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
} }
@ -86,6 +86,17 @@ func GetModel(id string) *Model {
return getModel(owner, name) return getModel(owner, name)
} }
func UpdateModelWithCheck(id string, modelObj *Model) error {
// check model grammar
_, err := model.NewModelFromString(modelObj.ModelText)
if err != nil {
return err
}
UpdateModel(id, modelObj)
return nil
}
func UpdateModel(id string, modelObj *Model) bool { func UpdateModel(id string, modelObj *Model) bool {
owner, name := util.GetOwnerAndNameFromId(id) owner, name := util.GetOwnerAndNameFromId(id)
if getModel(owner, name) == nil { if getModel(owner, name) == nil {
@ -98,11 +109,6 @@ func UpdateModel(id string, modelObj *Model) bool {
return false return false
} }
} }
// check model grammar
_, err := model.NewModelFromString(modelObj.ModelText)
if err != nil {
panic(err)
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(modelObj) affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(modelObj)
if err != nil { if err != nil {
@ -152,3 +158,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

@ -31,25 +31,34 @@ type AccountItem struct {
ModifyRule string `json:"modifyRule"` ModifyRule string `json:"modifyRule"`
} }
type ThemeData struct {
ThemeType string `xorm:"varchar(30)" json:"themeType"`
ColorPrimary string `xorm:"varchar(10)" json:"colorPrimary"`
BorderRadius int `xorm:"int" json:"borderRadius"`
IsCompact bool `xorm:"bool" json:"isCompact"`
IsEnabled bool `xorm:"bool" json:"isEnabled"`
}
type Organization struct { type Organization 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"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"` CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"` DisplayName string `xorm:"varchar(100)" json:"displayName"`
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"` WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
Favicon string `xorm:"varchar(100)" json:"favicon"` Favicon string `xorm:"varchar(100)" json:"favicon"`
PasswordType string `xorm:"varchar(100)" json:"passwordType"` PasswordType string `xorm:"varchar(100)" json:"passwordType"`
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"` PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"` PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"`
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"` DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"` DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
Tags []string `xorm:"mediumtext" json:"tags"` Tags []string `xorm:"mediumtext" json:"tags"`
Languages []string `xorm:"varchar(255)" json:"languages"` Languages []string `xorm:"varchar(255)" json:"languages"`
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"` ThemeData *ThemeData `xorm:"json" json:"themeData"`
InitScore int `json:"initScore"` MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
EnableSoftDeletion bool `json:"enableSoftDeletion"` InitScore int `json:"initScore"`
IsProfilePublic bool `json:"isProfilePublic"` EnableSoftDeletion bool `json:"enableSoftDeletion"`
IsProfilePublic bool `json:"isProfilePublic"`
AccountItems []*AccountItem `xorm:"varchar(3000)" json:"accountItems"` AccountItems []*AccountItem `xorm:"varchar(3000)" json:"accountItems"`
} }
@ -204,7 +213,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

@ -36,6 +36,60 @@ type UserShort struct {
Name string `xorm:"varchar(100) notnull pk" json:"name"` Name string `xorm:"varchar(100) notnull pk" json:"name"`
} }
type UserWithoutThirdIdp 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"`
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
Id string `xorm:"varchar(100) index" json:"id"`
Type string `xorm:"varchar(100)" json:"type"`
Password string `xorm:"varchar(100)" json:"password"`
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
FirstName string `xorm:"varchar(100)" json:"firstName"`
LastName string `xorm:"varchar(100)" json:"lastName"`
Avatar string `xorm:"varchar(500)" json:"avatar"`
PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"`
Email string `xorm:"varchar(100) index" json:"email"`
EmailVerified bool `json:"emailVerified"`
Phone string `xorm:"varchar(100) index" json:"phone"`
Location string `xorm:"varchar(100)" json:"location"`
Address []string `json:"address"`
Affiliation string `xorm:"varchar(100)" json:"affiliation"`
Title string `xorm:"varchar(100)" json:"title"`
IdCardType string `xorm:"varchar(100)" json:"idCardType"`
IdCard string `xorm:"varchar(100) index" json:"idCard"`
Homepage string `xorm:"varchar(100)" json:"homepage"`
Bio string `xorm:"varchar(100)" json:"bio"`
Tag string `xorm:"varchar(100)" json:"tag"`
Region string `xorm:"varchar(100)" json:"region"`
Language string `xorm:"varchar(100)" json:"language"`
Gender string `xorm:"varchar(100)" json:"gender"`
Birthday string `xorm:"varchar(100)" json:"birthday"`
Education string `xorm:"varchar(100)" json:"education"`
Score int `json:"score"`
Karma int `json:"karma"`
Ranking int `json:"ranking"`
IsDefaultAvatar bool `json:"isDefaultAvatar"`
IsOnline bool `json:"isOnline"`
IsAdmin bool `json:"isAdmin"`
IsGlobalAdmin bool `json:"isGlobalAdmin"`
IsForbidden bool `json:"isForbidden"`
IsDeleted bool `json:"isDeleted"`
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
Hash string `xorm:"varchar(100)" json:"hash"`
PreHash string `xorm:"varchar(100)" json:"preHash"`
CreatedIp string `xorm:"varchar(100)" json:"createdIp"`
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
LastSigninIp string `xorm:"varchar(100)" json:"lastSigninIp"`
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
Properties map[string]string `json:"properties"`
Roles []*Role `xorm:"-" json:"roles"`
Permissions []*Permission `xorm:"-" json:"permissions"`
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"`
}
type ClaimsShort struct { type ClaimsShort struct {
*UserShort *UserShort
TokenType string `json:"tokenType,omitempty"` TokenType string `json:"tokenType,omitempty"`
@ -44,6 +98,15 @@ type ClaimsShort struct {
jwt.RegisteredClaims jwt.RegisteredClaims
} }
type ClaimsWithoutThirdIdp struct {
*UserWithoutThirdIdp
TokenType string `json:"tokenType,omitempty"`
Nonce string `json:"nonce,omitempty"`
Tag string `json:"tag,omitempty"`
Scope string `json:"scope,omitempty"`
jwt.RegisteredClaims
}
func getShortUser(user *User) *UserShort { func getShortUser(user *User) *UserShort {
res := &UserShort{ res := &UserShort{
Owner: user.Owner, Owner: user.Owner,
@ -52,6 +115,69 @@ func getShortUser(user *User) *UserShort {
return res return res
} }
func getUserWithoutThirdIdp(user *User) *UserWithoutThirdIdp {
res := &UserWithoutThirdIdp{
Owner: user.Owner,
Name: user.Name,
CreatedTime: user.CreatedTime,
UpdatedTime: user.UpdatedTime,
Id: user.Id,
Type: user.Type,
Password: user.Password,
PasswordSalt: user.PasswordSalt,
DisplayName: user.DisplayName,
FirstName: user.FirstName,
LastName: user.LastName,
Avatar: user.Avatar,
PermanentAvatar: user.PermanentAvatar,
Email: user.Email,
EmailVerified: user.EmailVerified,
Phone: user.Phone,
Location: user.Location,
Address: user.Address,
Affiliation: user.Affiliation,
Title: user.Title,
IdCardType: user.IdCardType,
IdCard: user.IdCard,
Homepage: user.Homepage,
Bio: user.Bio,
Tag: user.Tag,
Region: user.Region,
Language: user.Language,
Gender: user.Gender,
Birthday: user.Birthday,
Education: user.Education,
Score: user.Score,
Karma: user.Karma,
Ranking: user.Ranking,
IsDefaultAvatar: user.IsDefaultAvatar,
IsOnline: user.IsOnline,
IsAdmin: user.IsAdmin,
IsGlobalAdmin: user.IsGlobalAdmin,
IsForbidden: user.IsForbidden,
IsDeleted: user.IsDeleted,
SignupApplication: user.SignupApplication,
Hash: user.Hash,
PreHash: user.PreHash,
CreatedIp: user.CreatedIp,
LastSigninTime: user.LastSigninTime,
LastSigninIp: user.LastSigninIp,
Ldap: user.Ldap,
Properties: user.Properties,
Roles: user.Roles,
Permissions: user.Permissions,
LastSigninWrongTime: user.LastSigninWrongTime,
SigninWrongTimes: user.SigninWrongTimes,
}
return res
}
func getShortClaims(claims Claims) ClaimsShort { func getShortClaims(claims Claims) ClaimsShort {
res := ClaimsShort{ res := ClaimsShort{
UserShort: getShortUser(claims.User), UserShort: getShortUser(claims.User),
@ -63,12 +189,44 @@ func getShortClaims(claims Claims) ClaimsShort {
return res return res
} }
func getClaimsWithoutThirdIdp(claims Claims) ClaimsWithoutThirdIdp {
res := ClaimsWithoutThirdIdp{
UserWithoutThirdIdp: getUserWithoutThirdIdp(claims.User),
TokenType: claims.TokenType,
Nonce: claims.Nonce,
Tag: claims.Tag,
Scope: claims.Scope,
RegisteredClaims: claims.RegisteredClaims,
}
return res
}
func refineUser(user *User) *User {
user.Password = ""
if user.Address == nil {
user.Address = []string{}
}
if user.Properties == nil {
user.Properties = map[string]string{}
}
if user.Roles == nil {
user.Roles = []*Role{}
}
if user.Permissions == nil {
user.Permissions = []*Permission{}
}
return user
}
func generateJwtToken(application *Application, user *User, nonce string, scope string, host string) (string, string, string, error) { func generateJwtToken(application *Application, user *User, nonce string, scope string, host string) (string, string, string, error) {
nowTime := time.Now() nowTime := time.Now()
expireTime := nowTime.Add(time.Duration(application.ExpireInHours) * time.Hour) expireTime := nowTime.Add(time.Duration(application.ExpireInHours) * time.Hour)
refreshExpireTime := nowTime.Add(time.Duration(application.RefreshExpireInHours) * time.Hour) refreshExpireTime := nowTime.Add(time.Duration(application.RefreshExpireInHours) * time.Hour)
user.Password = "" user = refineUser(user)
_, originBackend := getOriginFromHost(host) _, originBackend := getOriginFromHost(host)
name := util.GenerateId() name := util.GenerateId()
@ -104,10 +262,12 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
claimsShort.TokenType = "refresh-token" claimsShort.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort) refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
} else { } else {
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claims) claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime) claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claims.TokenType = "refresh-token" claims.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claims) refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
} }
cert := getCertByApplication(application) cert := getCertByApplication(application)

View File

@ -78,32 +78,80 @@ type User struct {
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"` LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
LastSigninIp string `xorm:"varchar(100)" json:"lastSigninIp"` LastSigninIp string `xorm:"varchar(100)" json:"lastSigninIp"`
GitHub string `xorm:"github varchar(100)" json:"github"` GitHub string `xorm:"github varchar(100)" json:"github"`
Google string `xorm:"varchar(100)" json:"google"` Google string `xorm:"varchar(100)" json:"google"`
QQ string `xorm:"qq varchar(100)" json:"qq"` QQ string `xorm:"qq varchar(100)" json:"qq"`
WeChat string `xorm:"wechat varchar(100)" json:"wechat"` WeChat string `xorm:"wechat varchar(100)" json:"wechat"`
Facebook string `xorm:"facebook varchar(100)" json:"facebook"` Facebook string `xorm:"facebook varchar(100)" json:"facebook"`
DingTalk string `xorm:"dingtalk varchar(100)" json:"dingtalk"` DingTalk string `xorm:"dingtalk varchar(100)" json:"dingtalk"`
Weibo string `xorm:"weibo varchar(100)" json:"weibo"` Weibo string `xorm:"weibo varchar(100)" json:"weibo"`
Gitee string `xorm:"gitee varchar(100)" json:"gitee"` Gitee string `xorm:"gitee varchar(100)" json:"gitee"`
LinkedIn string `xorm:"linkedin varchar(100)" json:"linkedin"` LinkedIn string `xorm:"linkedin varchar(100)" json:"linkedin"`
Wecom string `xorm:"wecom varchar(100)" json:"wecom"` Wecom string `xorm:"wecom varchar(100)" json:"wecom"`
Lark string `xorm:"lark varchar(100)" json:"lark"` Lark string `xorm:"lark varchar(100)" json:"lark"`
Gitlab string `xorm:"gitlab varchar(100)" json:"gitlab"` Gitlab string `xorm:"gitlab varchar(100)" json:"gitlab"`
Adfs string `xorm:"adfs varchar(100)" json:"adfs"` Adfs string `xorm:"adfs varchar(100)" json:"adfs"`
Baidu string `xorm:"baidu varchar(100)" json:"baidu"` Baidu string `xorm:"baidu varchar(100)" json:"baidu"`
Alipay string `xorm:"alipay varchar(100)" json:"alipay"` Alipay string `xorm:"alipay varchar(100)" json:"alipay"`
Casdoor string `xorm:"casdoor varchar(100)" json:"casdoor"` Casdoor string `xorm:"casdoor varchar(100)" json:"casdoor"`
Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"` Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"`
Apple string `xorm:"apple varchar(100)" json:"apple"` Apple string `xorm:"apple varchar(100)" json:"apple"`
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"` AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
Slack string `xorm:"slack varchar(100)" json:"slack"` Slack string `xorm:"slack varchar(100)" json:"slack"`
Steam string `xorm:"steam varchar(100)" json:"steam"` Steam string `xorm:"steam varchar(100)" json:"steam"`
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"` Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
Okta string `xorm:"okta varchar(100)" json:"okta"` Okta string `xorm:"okta varchar(100)" json:"okta"`
Douyin string `xorm:"douyin varchar(100)" json:"douyin"` Douyin string `xorm:"douyin varchar(100)" json:"douyin"`
Line string `xorm:"line varchar(100)" json:"line"` Line string `xorm:"line varchar(100)" json:"line"`
Custom string `xorm:"custom varchar(100)" json:"custom"` Amazon string `xorm:"amazon varchar(100)" json:"amazon"`
Auth0 string `xorm:"auth0 varchar(100)" json:"auth0"`
BattleNet string `xorm:"battlenet varchar(100)" json:"battlenet"`
Bitbucket string `xorm:"bitbucket varchar(100)" json:"bitbucket"`
Box string `xorm:"box varchar(100)" json:"box"`
CloudFoundry string `xorm:"cloudfoundry varchar(100)" json:"cloudfoundry"`
Dailymotion string `xorm:"dailymotion varchar(100)" json:"dailymotion"`
Deezer string `xorm:"deezer varchar(100)" json:"deezer"`
DigitalOcean string `xorm:"digitalocean varchar(100)" json:"digitalocean"`
Discord string `xorm:"discord varchar(100)" json:"discord"`
Dropbox string `xorm:"dropbox varchar(100)" json:"dropbox"`
EveOnline string `xorm:"eveonline varchar(100)" json:"eveonline"`
Fitbit string `xorm:"fitbit varchar(100)" json:"fitbit"`
Gitea string `xorm:"gitea varchar(100)" json:"gitea"`
Heroku string `xorm:"heroku varchar(100)" json:"heroku"`
InfluxCloud string `xorm:"influxcloud varchar(100)" json:"influxcloud"`
Instagram string `xorm:"instagram varchar(100)" json:"instagram"`
Intercom string `xorm:"intercom varchar(100)" json:"intercom"`
Kakao string `xorm:"kakao varchar(100)" json:"kakao"`
Lastfm string `xorm:"lastfm varchar(100)" json:"lastfm"`
Mailru string `xorm:"mailru varchar(100)" json:"mailru"`
Meetup string `xorm:"meetup varchar(100)" json:"meetup"`
MicrosoftOnline string `xorm:"microsoftonline varchar(100)" json:"microsoftonline"`
Naver string `xorm:"naver varchar(100)" json:"naver"`
Nextcloud string `xorm:"nextcloud varchar(100)" json:"nextcloud"`
OneDrive string `xorm:"onedrive varchar(100)" json:"onedrive"`
Oura string `xorm:"oura varchar(100)" json:"oura"`
Patreon string `xorm:"patreon varchar(100)" json:"patreon"`
Paypal string `xorm:"paypal varchar(100)" json:"paypal"`
SalesForce string `xorm:"salesforce varchar(100)" json:"salesforce"`
Shopify string `xorm:"shopify varchar(100)" json:"shopify"`
Soundcloud string `xorm:"soundcloud varchar(100)" json:"soundcloud"`
Spotify string `xorm:"spotify varchar(100)" json:"spotify"`
Strava string `xorm:"strava varchar(100)" json:"strava"`
Stripe string `xorm:"stripe varchar(100)" json:"stripe"`
TikTok string `xorm:"tiktok varchar(100)" json:"tiktok"`
Tumblr string `xorm:"tumblr varchar(100)" json:"tumblr"`
Twitch string `xorm:"twitch varchar(100)" json:"twitch"`
Twitter string `xorm:"twitter varchar(100)" json:"twitter"`
Typetalk string `xorm:"typetalk varchar(100)" json:"typetalk"`
Uber string `xorm:"uber varchar(100)" json:"uber"`
VK string `xorm:"vk varchar(100)" json:"vk"`
Wepay string `xorm:"wepay varchar(100)" json:"wepay"`
Xero string `xorm:"xero varchar(100)" json:"xero"`
Yahoo string `xorm:"yahoo varchar(100)" json:"yahoo"`
Yammer string `xorm:"yammer varchar(100)" json:"yammer"`
Yandex string `xorm:"yandex varchar(100)" json:"yandex"`
Zoom string `xorm:"zoom varchar(100)" json:"zoom"`
Custom string `xorm:"custom varchar(100)" json:"custom"`
WebauthnCredentials []webauthn.Credential `xorm:"webauthnCredentials blob" json:"webauthnCredentials"` WebauthnCredentials []webauthn.Credential `xorm:"webauthnCredentials blob" json:"webauthnCredentials"`
@ -529,6 +577,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,

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

@ -0,0 +1,10 @@
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
"retries": {
"runMode": 2,
"openMode": 0
}
},
});

View File

@ -0,0 +1,16 @@
describe('Test adapter', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
const selector = {
add: ".ant-table-title > div > .ant-btn"
};
it("test adapter", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/adapters");
cy.url().should("eq", "http://localhost:7001/adapters");
cy.get(selector.add).click();
cy.url().should("include","http://localhost:7001/adapters/built-in/")
});
})

View File

@ -0,0 +1,13 @@
describe('Test aplication', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test aplication", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/applications");
cy.url().should("eq", "http://localhost:7001/applications");
cy.visit("http://localhost:7001/applications/built-in/app-built-in");
cy.url().should("eq", "http://localhost:7001/applications/built-in/app-built-in");
});
})

View File

@ -0,0 +1,13 @@
describe('Test certs', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test certs", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/certs");
cy.url().should("eq", "http://localhost:7001/certs");
cy.visit("http://localhost:7001/certs/cert-built-in");
cy.url().should("eq", "http://localhost:7001/certs/cert-built-in");
});
})

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,13 @@
describe('Test models', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test org", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/models");
cy.url().should("eq", "http://localhost:7001/models");
cy.visit("http://localhost:7001/models/built-in/model-built-in");
cy.url().should("eq", "http://localhost:7001/models/built-in/model-built-in");
});
})

View File

@ -0,0 +1,15 @@
describe('Test Orgnazition', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test org", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/organizations");
cy.url().should("eq", "http://localhost:7001/organizations");
cy.visit("http://localhost:7001/organizations/built-in");
cy.url().should("eq", "http://localhost:7001/organizations/built-in");
cy.visit("http://localhost:7001/organizations/built-in/users");
cy.url().should("eq", "http://localhost:7001/organizations/built-in/users");
});
})

View File

@ -0,0 +1,16 @@
describe('Test payments', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
const selector = {
add: ".ant-table-title > div > .ant-btn"
};
it("test payments", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/payments");
cy.url().should("eq", "http://localhost:7001/payments");
cy.get(selector.add).click();
cy.url().should("include","http://localhost:7001/payments/")
});
})

View File

@ -0,0 +1,13 @@
describe('Test permissions', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test permissions", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/permissions");
cy.url().should("eq", "http://localhost:7001/permissions");
cy.visit("http://localhost:7001/permissions/built-in/permission-built-in");
cy.url().should("eq", "http://localhost:7001/permissions/built-in/permission-built-in");
});
})

View File

@ -0,0 +1,16 @@
describe('Test products', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
const selector = {
add: ".ant-table-title > div > .ant-btn > span"
};
it("test products", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/products");
cy.url().should("eq", "http://localhost:7001/products");
cy.get(selector.add).click();
cy.url().should("include","http://localhost:7001/products/")
});
})

View File

@ -0,0 +1,13 @@
describe('Test providers', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test providers", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/providers");
cy.url().should("eq", "http://localhost:7001/providers");
cy.visit("http://localhost:7001/providers/admin/provider_captcha_default");
cy.url().should("eq", "http://localhost:7001/providers/admin/provider_captcha_default");
});
})

View File

@ -0,0 +1,11 @@
describe('Test records', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test records", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/records");
cy.url().should("eq", "http://localhost:7001/records");
});
})

View File

@ -0,0 +1,11 @@
describe('Test resource', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test resource", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/resources");
cy.url().should("eq", "http://localhost:7001/resources");
});
})

View File

@ -0,0 +1,11 @@
describe('Test roles', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test role", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/roles");
cy.url().should("eq", "http://localhost:7001/roles");
});
})

View File

@ -0,0 +1,11 @@
describe('Test sessions', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test sessions", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/sessions");
cy.url().should("eq", "http://localhost:7001/sessions");
});
})

View File

@ -0,0 +1,16 @@
describe('Test syncers', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
const selector = {
add: ".ant-table-title > div > .ant-btn"
};
it("test syncers", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/syncers");
cy.url().should("eq", "http://localhost:7001/syncers");
cy.get(selector.add).click();
cy.url().should("include","http://localhost:7001/syncers/")
});
})

View File

@ -0,0 +1,11 @@
describe('Test sysinfo', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test sysinfo", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/sysinfo");
cy.url().should("eq", "http://localhost:7001/sysinfo");
});
})

View File

@ -0,0 +1,16 @@
describe('Test tokens', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
const selector = {
add: ".ant-table-title > div > .ant-btn"
};
it("test records", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/tokens");
cy.url().should("eq", "http://localhost:7001/tokens");
cy.get(selector.add).click();
cy.url().should("include","http://localhost:7001/tokens/")
});
})

View File

@ -0,0 +1,13 @@
describe('Test User', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
it("test user", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/users");
cy.url().should("eq", "http://localhost:7001/users");
cy.visit("http://localhost:7001/users/built-in/admin");
cy.url().should("eq", "http://localhost:7001/users/built-in/admin");
});
})

View File

@ -0,0 +1,16 @@
describe('Test webhooks', () => {
beforeEach(()=>{
cy.visit("http://localhost:7001");
cy.login();
})
const selector = {
add: ".ant-table-title > div > .ant-btn"
};
it("test webhooks", () => {
cy.visit("http://localhost:7001");
cy.visit("http://localhost:7001/webhooks");
cy.url().should("eq", "http://localhost:7001/webhooks");
cy.get(selector.add).click();
cy.url().should("include","http://localhost:7001/webhooks/")
});
})

View File

@ -0,0 +1,42 @@
// ***********************************************
// 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) => { ... })
Cypress.Commands.add('login', ()=>{
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");
});
})

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

@ -6,10 +6,13 @@
"@ant-design/icons": "^4.7.0", "@ant-design/icons": "^4.7.0",
"@craco/craco": "^6.4.5", "@craco/craco": "^6.4.5",
"@crowdin/cli": "^3.7.10", "@crowdin/cli": "^3.7.10",
"@ctrl/tinycolor": "^3.5.0",
"@emotion/react": "^11.10.5",
"@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.6",
"antd-token-previewer": "^1.1.0-22",
"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",
@ -66,6 +69,7 @@
"@babel/eslint-parser": "^7.18.9", "@babel/eslint-parser": "^7.18.9",
"@babel/preset-react": "^7.18.6", "@babel/preset-react": "^7.18.6",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"cypress": "^12.5.1",
"eslint": "8.22.0", "eslint": "8.22.0",
"eslint-plugin-react": "^7.31.1", "eslint-plugin-react": "^7.31.1",
"husky": "^4.3.8", "husky": "^4.3.8",

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

@ -109,7 +109,10 @@ class AdapterEditPage 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.adapter.organization} onChange={(value => {this.updateAdapterField("organization", value);})}> <Select virtual={false} style={{width: "100%"}} value={this.state.adapter.organization} onChange={(value => {
this.getModels(value);
this.updateAdapterField("organization", value);
})}>
{ {
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>) this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
} }

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, theme} from "antd"; import {Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result} 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";
@ -70,6 +70,7 @@ 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 SelectThemeBox from "./SelectThemeBox";
import SessionListPage from "./SessionListPage";
const {Header, Footer, Content} = Layout; const {Header, Footer, Content} = Layout;
@ -82,8 +83,9 @@ class App extends Component {
account: undefined, account: undefined,
uri: null, uri: null,
menuVisible: false, menuVisible: false,
themeAlgorithm: null, themeAlgorithm: ["default"],
logo: null, themeData: Setting.ThemeDefault,
logo: this.getLogo(Setting.getAlgorithmNames(Setting.ThemeDefault)),
}; };
Setting.initServerUrl(); Setting.initServerUrl();
@ -98,16 +100,6 @@ 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;
@ -197,8 +189,12 @@ class App extends Component {
return ""; return "";
} }
getTheme() { getLogo(themes) {
return Setting.Themes.find(t => t.key === localStorage.getItem("theme"))["style"]; if (themes.includes("dark")) {
return `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256_dark.png`;
} else {
return `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`;
}
} }
setLanguage(account) { setLanguage(account) {
@ -208,6 +204,19 @@ class App extends Component {
} }
} }
setTheme = (theme, initThemeAlgorithm) => {
this.setState({
themeData: theme,
});
if (initThemeAlgorithm) {
this.setState({
logo: this.getLogo(Setting.getAlgorithmNames(theme)),
themeAlgorithm: Setting.getAlgorithmNames(theme),
});
}
};
getAccount() { getAccount() {
const params = new URLSearchParams(this.props.location.search); const params = new URLSearchParams(this.props.location.search);
@ -232,7 +241,9 @@ class App extends Component {
if (res.status === "ok") { if (res.status === "ok") {
account = res.data; account = res.data;
account.organization = res.data2; account.organization = res.data2;
this.setLanguage(account); this.setLanguage(account);
this.setTheme(Setting.getThemeData(account.organization), Conf.InitThemeAlgorithm);
} else { } else {
if (res.data !== "Please login first") { if (res.data !== "Please login first") {
Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
@ -258,6 +269,7 @@ class App extends Component {
this.setState({ this.setState({
account: null, account: null,
themeAlgorithm: ["default"],
}); });
Setting.showMessage("success", i18next.t("application:Logged out successfully")); Setting.showMessage("success", i18next.t("application:Logged out successfully"));
@ -281,14 +293,6 @@ class App extends Component {
}); });
} }
handleRightDropdownClick(e) {
if (e.key === "/account") {
this.props.history.push("/account");
} else if (e.key === "/logout") {
this.logout();
}
}
renderAvatar() { renderAvatar() {
if (this.state.account.avatar === "") { if (this.state.account.avatar === "") {
return ( return (
@ -312,55 +316,56 @@ class App extends Component {
)); ));
items.push(Setting.getItem(<><LogoutOutlined />&nbsp;&nbsp;{i18next.t("account:Logout")}</>, items.push(Setting.getItem(<><LogoutOutlined />&nbsp;&nbsp;{i18next.t("account:Logout")}</>,
"/logout")); "/logout"));
const onClick = this.handleRightDropdownClick.bind(this);
const onClick = (e) => {
if (e.key === "/account") {
this.props.history.push("/account");
} else if (e.key === "/logout") {
this.logout();
}
};
return ( return (
<Dropdown key="/rightDropDown" menu={{items, onClick}} className="rightDropDown"> <Dropdown key="/rightDropDown" menu={{items, onClick}} >
<div className="ant-dropdown-link" style={{float: "right", cursor: "pointer"}}> <div className="rightDropDown">
&nbsp;
&nbsp;
{ {
this.renderAvatar() this.renderAvatar()
} }
&nbsp; &nbsp;
&nbsp; &nbsp;
{Setting.isMobile() ? null : Setting.getShortName(this.state.account.displayName)} &nbsp; <DownOutlined /> {Setting.isMobile() ? null : Setting.getShortName(this.state.account.displayName)} &nbsp; <DownOutlined />
&nbsp; &nbsp;
&nbsp; &nbsp;
&nbsp; &nbsp;
</div> </div>
</Dropdown> </Dropdown>
); );
} }
renderAccount() { renderAccountMenu() {
const res = [];
if (this.state.account === undefined) { if (this.state.account === undefined) {
return null; return null;
} else if (this.state.account === null) { } else if (this.state.account === null) {
// res.push( return null;
// <Menu.Item key="/signup" style={{float: 'right', marginRight: '20px'}}>
// <Link to="/signup">
// {i18next.t("account:Sign Up")}
// </Link>
// </Menu.Item>
// );
// res.push(
// <Menu.Item key="/login" style={{float: 'right'}}>
// <Link to="/login">
// {i18next.t("account:Login")}
// </Link>
// </Menu.Item>
// );
} else { } else {
res.push(this.renderRightDropdown()); return (
<React.Fragment>
{this.renderRightDropdown()}
<SelectThemeBox
themeAlgorithm={this.state.themeAlgorithm}
onChange={(nextThemeAlgorithm) => {
this.setState({
themeAlgorithm: nextThemeAlgorithm,
logo: this.getLogo(nextThemeAlgorithm),
});
}} />
<SelectLanguageBox languages={this.state.account.organization.languages} />
</React.Fragment>
);
} }
return res;
} }
renderMenu() { getMenuItems() {
const res = []; const res = [];
if (this.state.account === null || this.state.account === undefined) { if (this.state.account === null || this.state.account === undefined) {
@ -421,6 +426,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"
)); ));
@ -476,68 +485,59 @@ class App extends Component {
isStartPages() { isStartPages() {
return window.location.pathname.startsWith("/login") || return window.location.pathname.startsWith("/login") ||
window.location.pathname.startsWith("/signup") || window.location.pathname.startsWith("/signup") ||
window.location.pathname === "/"; window.location.pathname === "/";
} }
renderRouter() { renderRouter() {
return ( return (
<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} onChangeTheme={this.setTheme} {...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="/sessions" render={(props) => this.renderLoginIfNotLoggedIn(<SessionListPage account={this.state.account} {...props} />)} />
<Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} /> <Route exact path="/tokens/:tokenName" render={(props) => this.renderLoginIfNotLoggedIn(<TokenEditPage 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" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookListPage account={this.state.account} {...props} />)} />
<Route exact path="/resources" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceListPage account={this.state.account} {...props} />)} /> <Route exact path="/webhooks/:webhookName" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookEditPage account={this.state.account} {...props} />)} />
{/* <Route exact path="/resources/:resourceName" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceEditPage account={this.state.account} {...props} />)}/>*/} <Route exact path="/syncers" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerListPage account={this.state.account} {...props} />)} />
<Route exact path="/ldap/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapEditPage account={this.state.account} {...props} />)} /> <Route exact path="/syncers/:syncerName" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerEditPage 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" render={(props) => this.renderLoginIfNotLoggedIn(<CertListPage account={this.state.account} {...props} />)} />
<Route exact path="/tokens" render={(props) => this.renderLoginIfNotLoggedIn(<TokenListPage account={this.state.account} {...props} />)} /> <Route exact path="/certs/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
<Route exact path="/tokens/:tokenName" render={(props) => this.renderLoginIfNotLoggedIn(<TokenEditPage account={this.state.account} {...props} />)} /> <Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} />
<Route exact path="/webhooks" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookListPage account={this.state.account} {...props} />)} /> <Route exact path="/products/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)} />
<Route exact path="/webhooks/:webhookName" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookEditPage account={this.state.account} {...props} />)} /> <Route exact path="/products/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage account={this.state.account} {...props} />)} />
<Route exact path="/syncers" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerListPage account={this.state.account} {...props} />)} /> <Route exact path="/payments" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentListPage account={this.state.account} {...props} />)} />
<Route exact path="/syncers/:syncerName" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerEditPage account={this.state.account} {...props} />)} /> <Route exact path="/payments/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} />
<Route exact path="/certs" render={(props) => this.renderLoginIfNotLoggedIn(<CertListPage account={this.state.account} {...props} />)} /> <Route exact path="/payments/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
<Route exact path="/certs/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} /> <Route exact path="/records" render={(props) => this.renderLoginIfNotLoggedIn(<RecordListPage account={this.state.account} {...props} />)} />
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} /> <Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
<Route exact path="/products/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)} /> <Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} />
<Route exact path="/products/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage 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="/payments" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentListPage account={this.state.account} {...props} />)} /> extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
<Route exact path="/payments/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} /> </Switch>
<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>
); );
} }
@ -554,85 +554,56 @@ class App extends Component {
}; };
renderContent() { renderContent() {
if (!Setting.isMobile()) { return (
return ( <Layout id="parent-area">
<Layout id="parent-area"> {/* https://github.com/ant-design/ant-design/issues/40394 ant design bug. If it will be fixed, we can delete the code for control the color of Header*/}
<Header style={{marginBottom: "3px", paddingInline: 0, backgroundColor: this.state.themeAlgorithm === theme.darkAlgorithm ? "black" : "white"}}> <Header style={{padding: "0", marginBottom: "3px", backgroundColor: this.state.themeAlgorithm.includes("dark") ? "black" : "white"}}>
{ {Setting.isMobile() ? null : (
Setting.isMobile() ? null : ( <Link to={"/"}>
<Link to={"/"}> <div className="logo" style={{background: `url(${this.state.logo})`}} />
<div className="logo" style={{background: `url(${this.state.logo})`}} /> </Link>
</Link> )}
) {Setting.isMobile() ?
} <React.Fragment>
<Drawer title={i18next.t("general:Close")} placement="left" visible={this.state.menuVisible} onClose={this.onClose}>
<Menu
items={this.getMenuItems()}
mode={"inline"}
selectedKeys={[this.state.selectedMenuKey]}
style={{lineHeight: "64px"}}
onClick={this.onClose}
>
</Menu>
</Drawer>
<Button icon={<BarsOutlined />} onClick={this.showMenu} type="text">
{i18next.t("general:Menu")}
</Button>
</React.Fragment> :
<Menu <Menu
items={this.renderMenu()} items={this.getMenuItems()}
mode={(Setting.isMobile() && this.isStartPages()) ? "inline" : "horizontal"} mode={"horizontal"}
selectedKeys={[`${this.state.selectedMenuKey}`]} selectedKeys={[this.state.selectedMenuKey]}
style={{position: "absolute", left: "145px", backgroundColor: this.state.themeAlgorithm === theme.darkAlgorithm ? "black" : "white"}} style={{position: "absolute", left: "145px", right: "260px"}}
/> />
{
this.renderAccount()
}
{this.state.account && <SelectThemeBox themes={this.state.account.organization.themes} />}
{this.state.account && <SelectLanguageBox languages={this.state.account.organization.languages} />}
</Header>
<Content style={{alignItems: "stretch", display: "flex", flexDirection: "column"}}>
<Card className="content-warp-card">
{
this.renderRouter()
}
</Card>
</Content>
{
this.renderFooter()
} }
</Layout> {
); this.renderAccountMenu()
} else { }
return ( </Header>
<Layout> <Content style={{display: "flex", flexDirection: "column"}} >
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: this.state.themeAlgorithm === theme.darkAlgorithm ? "black" : "white"}}> {Setting.isMobile() ?
{ this.renderRouter() :
Setting.isMobile() ? null : ( <Card className="content-warp-card">
<Link to={"/"}> {this.renderRouter()}
<div className="logo" style={{background: `url(${this.state.logo})`}} /> </Card>
</Link> }
) </Content>
} {this.renderFooter()}
<Drawer title={i18next.t("general:Close")} placement="left" visible={this.state.menuVisible} onClose={this.onClose}> </Layout>
<Menu );
// theme="dark"
items={this.renderMenu()}
mode={(Setting.isMobile()) ? "inline" : "horizontal"}
selectedKeys={[`${this.state.selectedMenuKey}`]}
style={{lineHeight: "64px"}}
onClick={this.onClose}
>
</Menu>
</Drawer>
<Button icon={<BarsOutlined />} onClick={this.showMenu} type="text">
{i18next.t("general:Menu")}
</Button>
{
this.renderAccount()
}
{this.state.account && <SelectThemeBox themes={this.state.account.organization.themes} />}
{this.state.account && <SelectLanguageBox languages={this.state.account.organization.languages} />}
</Header>
<Content style={{display: "flex", flexDirection: "column"}} >{
this.renderRouter()}
</Content>
{this.renderFooter()}
</Layout>
);
}
} }
renderFooter() { renderFooter() {
// How to keep your footer where it belongs ?
// https://www.freecodecamp.org/neyarnws/how-to-keep-your-footer-where-it-belongs-59c6aa05c59c/
return ( return (
<React.Fragment> <React.Fragment>
{!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} />}
@ -641,7 +612,7 @@ class App extends Component {
textAlign: "center", textAlign: "center",
} }
}> }>
Powered by <a target="_blank" href="https://casdoor.org" rel="noreferrer"><img style={{paddingBottom: "3px"}} height={"20px"} alt={"Casdoor"} src={this.state.logo} /></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>
); );
@ -653,36 +624,40 @@ class App extends Component {
isEntryPages() { isEntryPages() {
return window.location.pathname.startsWith("/signup") || return window.location.pathname.startsWith("/signup") ||
window.location.pathname.startsWith("/login") || window.location.pathname.startsWith("/login") ||
window.location.pathname.startsWith("/forget") || window.location.pathname.startsWith("/forget") ||
window.location.pathname.startsWith("/prompt") || window.location.pathname.startsWith("/prompt") ||
window.location.pathname.startsWith("/cas") || window.location.pathname.startsWith("/cas") ||
window.location.pathname.startsWith("/auto-signup"); window.location.pathname.startsWith("/auto-signup");
} }
renderPage() { renderPage() {
if (this.isDoorPages()) { if (this.isDoorPages()) {
return ( return (
<React.Fragment> <Layout id="parent-area">
<Layout id="parent-area"> <Content style={{display: "flex", justifyContent: "center"}}>
<Content style={{display: "flex", justifyContent: "center"}}>
{
this.isEntryPages() ?
<EntryPage account={this.state.account} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />
:
<Switch>
<Route exact path="/callback" component={AuthCallback} />
<Route exact path="/callback/saml" component={SamlCallback} />
<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>
}
</Content>
{ {
this.renderFooter() this.isEntryPages() ?
<EntryPage
account={this.state.account}
theme={this.state.themeData}
onUpdateAccount={(account) => {
this.onUpdateAccount(account);
}}
updataThemeData={this.setTheme}
/> :
<Switch>
<Route exact path="/callback" component={AuthCallback} />
<Route exact path="/callback/saml" component={SamlCallback} />
<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>
} }
</Layout> </Content>
</React.Fragment> {
this.renderFooter()
}
</Layout>
); );
} }
@ -698,40 +673,24 @@ class App extends Component {
} }
render() { render() {
if (this.state.account === undefined || this.state.account === null) {
return (
<React.Fragment>
<Helmet>
<link rel="icon" href={"https://cdn.casdoor.com/static/favicon.png"} />
</Helmet>
<ConfigProvider theme={{
token: {
colorPrimary: "rgb(89,54,213)",
colorInfo: "rgb(89,54,213)",
},
algorithm: this.state.themeAlgorithm,
}}>
{
this.renderPage()
}
</ConfigProvider>
</React.Fragment>
);
}
const organization = this.state.account.organization;
return ( return (
<React.Fragment> <React.Fragment>
<Helmet> {(this.state.account === undefined || this.state.account === null) ?
<title>{organization.displayName}</title> <Helmet>
<link rel="icon" href={organization.favicon} /> <link rel="icon" href={"https://cdn.casdoor.com/static/favicon.png"} />
</Helmet> </Helmet> :
<Helmet>
<title>{this.state.account.organization?.displayName}</title>
<link rel="icon" href={this.state.account.organization?.favicon} />
</Helmet>
}
<ConfigProvider theme={{ <ConfigProvider theme={{
token: { token: {
colorPrimary: "rgb(89,54,213)", colorPrimary: this.state.themeData.colorPrimary,
colorInfo: "rgb(89,54,213)", colorInfo: this.state.themeData.colorPrimary,
borderRadius: this.state.themeData.borderRadius,
}, },
algorithm: this.state.themeAlgorithm, algorithm: Setting.getAlgorithm(this.state.themeAlgorithm),
}}> }}>
{ {
this.renderPage() this.renderPage()

View File

@ -1,8 +1,6 @@
/* stylelint-disable at-rule-name-case */ /* stylelint-disable at-rule-name-case */
/* stylelint-disable selector-class-pattern */ /* stylelint-disable selector-class-pattern */
@StaticBaseUrl: "https://cdn.casbin.org";
.App { .App {
text-align: center; text-align: center;
} }
@ -45,34 +43,13 @@ img {
margin-bottom: 30px; margin-bottom: 30px;
} }
.language-box { .select-box {
background: url("@{StaticBaseUrl}/img/muti_language.svg"); display: flex;
background-size: 25px, 25px; align-items: center;
background-position: center; justify-content: center;
background-repeat: no-repeat;
border-radius: 5px; border-radius: 5px;
width: 45px; width: 45px;
height: 100%; height: 64px;
float: right;
cursor: pointer;
&:hover {
background-color: #f5f5f5;
}
}
.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; float: right;
cursor: pointer; cursor: pointer;
@ -82,7 +59,12 @@ img {
} }
.rightDropDown { .rightDropDown {
display: flex;
align-items: center;
justify-content: center;
border-radius: 7px; border-radius: 7px;
float: right;
cursor: pointer;
&:hover { &:hover {
background-color: #f5f5f5; background-color: #f5f5f5;
@ -151,6 +133,4 @@ img {
.ant-menu-horizontal { .ant-menu-horizontal {
border-bottom: none !important; 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, ConfigProvider, 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";
@ -32,6 +32,7 @@ import copy from "copy-to-clipboard";
import {Controlled as CodeMirror} from "react-codemirror2"; import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css"; import "codemirror/lib/codemirror.css";
import ThemeEditor from "./common/theme/ThemeEditor";
require("codemirror/theme/material-darker.css"); require("codemirror/theme/material-darker.css");
require("codemirror/mode/htmlmixed/htmlmixed"); require("codemirror/mode/htmlmixed/htmlmixed");
@ -103,6 +104,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 +131,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 +344,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 +604,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"))} :
@ -695,6 +710,31 @@ class ApplicationEditPage extends React.Component {
: null} : null}
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("theme:Theme"), i18next.t("theme:Theme - Tooltip"))} :
</Col>
<Col span={22} style={{marginTop: "5px"}}>
<Row>
<Radio.Group value={this.state.application.themeData?.isEnabled ?? false} onChange={e => {
const {_, ...theme} = this.state.application.themeData ?? {...Setting.ThemeDefault, isEnabled: false};
this.updateApplicationField("themeData", {...theme, isEnabled: e.target.value});
}} >
<Radio.Button value={false}>{i18next.t("application:Follow organization theme")}</Radio.Button>
<Radio.Button value={true}>{i18next.t("theme:Customize theme")}</Radio.Button>
</Radio.Group>
</Row>
{
this.state.application.themeData?.isEnabled ?
<Row style={{marginTop: "20px"}}>
<ThemeEditor themeData={this.state.application.themeData} onThemeChange={(_, nextThemeData) => {
const {isEnabled} = this.state.application.themeData ?? {...Setting.ThemeDefault, isEnabled: false};
this.updateApplicationField("themeData", {...nextThemeData, isEnabled});
}} />
</Row> : null
}
</Col>
</Row>
{ {
!this.state.application.enableSignUp ? null : ( !this.state.application.enableSignUp ? null : (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
@ -724,6 +764,7 @@ class ApplicationEditPage extends React.Component {
} }
renderSignupSigninPreview() { renderSignupSigninPreview() {
const themeData = this.state.application.themeData ?? Setting.ThemeDefault;
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: "97%", 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)"};
@ -742,20 +783,28 @@ 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", overflow: "auto"}}> <ConfigProvider theme={{
{ token: {
this.state.application.enablePassword ? ( colorPrimary: themeData.colorPrimary,
<div className="loginBackground" style={{backgroundImage: `url(${this.state.application?.formBackgroundUrl})`, overflow: "auto"}}> colorInfo: themeData.colorPrimary,
<SignupPage application={this.state.application} preview = "auto" /> borderRadius: themeData.borderRadius,
</div> },
) : ( }}>
<div className="loginBackground" style={{backgroundImage: `url(${this.state.application?.formBackgroundUrl})`, overflow: "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={"signup"} application={this.state.application} preview = "auto" /> {
</div> this.state.application.enablePassword ? (
) <div className="loginBackground" style={{backgroundImage: `url(${this.state.application?.formBackgroundUrl})`, overflow: "auto"}}>
} <SignupPage application={this.state.application} preview = "auto" />
<div style={{overflow: "auto", ...maskStyle}} /> </div>
</div> ) : (
<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={{overflow: "auto", ...maskStyle}} />
</div>
</ConfigProvider>
</Col> </Col>
<Col span={previewGrid}> <Col span={previewGrid}>
<Button style={{marginBottom: "10px", marginTop: Setting.isMobile() ? "15px" : "0"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => { <Button style={{marginBottom: "10px", marginTop: Setting.isMobile() ? "15px" : "0"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
@ -766,18 +815,27 @@ 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", overflow: "auto"}}> <ConfigProvider theme={{
<div className="loginBackground" style={{backgroundImage: `url(${this.state.application?.formBackgroundUrl})`, overflow: "auto"}}> token: {
<LoginPage type={"login"} mode={"signin"} application={this.state.application} preview = "auto" /> colorPrimary: themeData.colorPrimary,
colorInfo: themeData.colorPrimary,
borderRadius: themeData.borderRadius,
},
}}>
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", overflow: "auto"}}>
<div className="loginBackground" style={{backgroundImage: `url(${this.state.application?.formBackgroundUrl})`, overflow: "auto"}}>
<LoginPage type={"login"} mode={"signin"} application={this.state.application} preview = "auto" />
</div>
<div style={{overflow: "auto", ...maskStyle}} />
</div> </div>
<div style={{overflow: "auto", ...maskStyle}} /> </ConfigProvider>
</div>
</Col> </Col>
</React.Fragment> </React.Fragment>
); );
} }
renderPromptPreview() { renderPromptPreview() {
const themeData = this.state.application.themeData ?? Setting.ThemeDefault;
const promptUrl = `/prompt/${this.state.application.name}`; const promptUrl = `/prompt/${this.state.application.name}`;
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: "100%", width: "100%", background: "rgba(0,0,0,0.4)"};
return ( return (
@ -790,10 +848,18 @@ class ApplicationEditPage extends React.Component {
{i18next.t("application:Copy prompt page URL")} {i18next.t("application:Copy prompt page URL")}
</Button> </Button>
<br /> <br />
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", flexDirection: "column", flex: "auto"}}> <ConfigProvider theme={{
<PromptPage application={this.state.application} account={this.props.account} /> token: {
<div style={maskStyle} /> colorPrimary: themeData.colorPrimary,
</div> colorInfo: themeData.colorPrimary,
borderRadius: themeData.borderRadius,
},
}}>
<div style={{position: "relative", width: previewWidth, border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", flexDirection: "column", flex: "auto"}}>
<PromptPage application={this.state.application} account={this.props.account} />
<div style={maskStyle} />
</div>
</ConfigProvider>
</Col> </Col>
); );
} }
@ -838,6 +904,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

@ -25,18 +25,12 @@ import BaseListPage from "./BaseListPage";
class ApplicationListPage extends BaseListPage { class ApplicationListPage extends BaseListPage {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { }
classes: props,
organizationName: props.account.owner, componentDidMount() {
data: [], this.setState({
pagination: { organizationName: this.props.account.owner,
current: 1, });
pageSize: 10,
},
loading: false,
searchText: "",
searchedColumn: "",
};
} }
newApplication() { newApplication() {
@ -292,6 +286,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

@ -17,5 +17,6 @@ export const GithubRepo = "https://github.com/casdoor/casdoor";
export const ForceLanguage = ""; export const ForceLanguage = "";
export const DefaultLanguage = "en"; export const DefaultLanguage = "en";
export const InitThemeAlgorithm = true;
export const EnableExtraPages = true; export const EnableExtraPages = true;

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

@ -52,32 +52,41 @@ class EntryPage extends React.Component {
} }
} }
getApplicationObj() {
return this.state.application || null;
}
render() { render() {
const onUpdateApplication = (application) => { const onUpdateApplication = (application) => {
this.setState({ this.setState({
application: application, application: application,
}); });
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Setting.ThemeDefault;
this.props.updataThemeData(themeData);
}; };
return <div className="loginBackground" style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}> return (
<Spin spinning={this.state.application === undefined} tip={i18next.t("login:Loading")} style={{margin: "0 auto"}} /> <div className="loginBackground" style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}>
<Switch> <Spin spinning={this.state.application === undefined} tip={i18next.t("login:Loading")} style={{margin: "0 auto"}} />
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Switch>
<Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/auto-signup/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signup"} onUpdateApplication={onUpdateApplication}{...props} />} /> <Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} /> <Route exact path="/auto-signup/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signup"} onUpdateApplication={onUpdateApplication}{...props} />} />
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} /> <Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"saml"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} /> <Route exact path="/login/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"saml"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} {...props} />)} /> <Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage {...this.props} application={this.state.application} type={"cas"} mode={"signup"} {...props} />);}} /> <Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} {...props} />)} />
</Switch> <Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage {...this.props} application={this.state.application} type={"cas"} mode={"signup"} {...props} />);}} />
</div>; </Switch>
</div>
);
} }
} }

View File

@ -25,11 +25,25 @@ class ManagedAccountTable extends React.Component {
super(props); super(props);
this.state = { this.state = {
classes: props, classes: props,
managedAccounts: this.props.table !== null ? this.props.table.map((item, index) => {
item.key = index;
return item;
}) : [],
}; };
} }
count = this.props.table?.length ?? 0;
updateTable(table) { updateTable(table) {
this.props.onUpdateTable(table); this.setState({
managedAccounts: table,
});
this.props.onUpdateTable([...table].map((item) => {
const newItem = Setting.deepCopy(item);
delete newItem.key;
return newItem;
}));
} }
updateField(table, index, key, value) { updateField(table, index, key, value) {
@ -38,10 +52,12 @@ class ManagedAccountTable extends React.Component {
} }
addRow(table) { addRow(table) {
const row = {application: "", username: "", password: ""}; const row = {key: this.count, application: "", username: "", password: ""};
if (table === undefined || table === null) { if (table === undefined || table === null) {
table = []; table = [];
} }
this.count += 1;
table = Setting.addRow(table, row); table = Setting.addRow(table, row);
this.updateTable(table); this.updateTable(table);
} }
@ -131,7 +147,7 @@ class ManagedAccountTable extends React.Component {
]; ];
return ( return (
<Table scroll={{x: "max-content"}} rowKey="name" columns={columns} dataSource={table} size="middle" bordered pagination={false} <Table scroll={{x: "max-content"}} rowKey="key" columns={columns} dataSource={table} size="middle" bordered pagination={false}
title={() => ( title={() => (
<div> <div>
{this.props.title}&nbsp;&nbsp;&nbsp;&nbsp; {this.props.title}&nbsp;&nbsp;&nbsp;&nbsp;
@ -148,7 +164,7 @@ class ManagedAccountTable extends React.Component {
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col span={24}> <Col span={24}>
{ {
this.renderTable(this.props.table) this.renderTable(this.state.managedAccounts)
} }
</Col> </Col>
</Row> </Row>

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

@ -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, InputNumber, Row, Select, Switch} from "antd"; import {Button, Card, Col, Input, InputNumber, Radio, Row, Select, Switch} from "antd";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as ApplicationBackend from "./backend/ApplicationBackend"; import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as LdapBackend from "./backend/LdapBackend"; import * as LdapBackend from "./backend/LdapBackend";
@ -22,6 +22,7 @@ import i18next from "i18next";
import {LinkOutlined} from "@ant-design/icons"; import {LinkOutlined} from "@ant-design/icons";
import LdapTable from "./LdapTable"; import LdapTable from "./LdapTable";
import AccountTable from "./AccountTable"; import AccountTable from "./AccountTable";
import ThemeEditor from "./common/theme/ThemeEditor";
const {Option} = Select; const {Option} = Select;
@ -165,12 +166,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 +223,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"}} >
@ -321,6 +317,31 @@ class OrganizationEditPage extends React.Component {
/> />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("theme:Theme"), i18next.t("theme:Theme - Tooltip"))} :
</Col>
<Col span={22} style={{marginTop: "5px"}}>
<Row>
<Radio.Group value={this.state.organization.themeData?.isEnabled ?? false} onChange={e => {
const {_, ...theme} = this.state.organization.themeData ?? {...Setting.ThemeDefault, isEnabled: false};
this.updateOrganizationField("themeData", {...theme, isEnabled: e.target.value});
}} >
<Radio.Button value={false}>{i18next.t("organization:Follow global theme")}</Radio.Button>
<Radio.Button value={true}>{i18next.t("theme:Customize theme")}</Radio.Button>
</Radio.Group>
</Row>
{
this.state.organization.themeData?.isEnabled ?
<Row style={{marginTop: "20px"}}>
<ThemeEditor themeData={this.state.organization.themeData} onThemeChange={(_, nextThemeData) => {
const {isEnabled} = this.state.organization.themeData ?? {...Setting.ThemeDefault, isEnabled: false};
this.updateOrganizationField("themeData", {...nextThemeData, isEnabled});
}} />
</Row> : null
}
</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:LDAPs"), i18next.t("general:LDAPs - Tooltip"))} : {Setting.getLabel(i18next.t("general:LDAPs"), i18next.t("general:LDAPs - Tooltip"))} :
@ -346,6 +367,11 @@ class OrganizationEditPage extends React.Component {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully saved")); Setting.showMessage("success", i18next.t("general:Successfully saved"));
if (this.props.account.organization.name === this.state.organizationName) {
this.props.onChangeTheme(Setting.getThemeData(this.state.organization));
}
this.setState({ this.setState({
organizationName: this.state.organization.name, organizationName: this.state.organization.name,
}); });

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, Result, Switch, Table} from "antd"; import {Button, Popconfirm, Switch, Table} from "antd";
import moment from "moment"; import moment from "moment";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
@ -246,17 +246,6 @@ class OrganizationListPage 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.")}
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={organizations} rowKey="name" size="middle" bordered pagination={paginationProps} <Table scroll={{x: "max-content"}} columns={columns} dataSource={organizations} rowKey="name" size="middle" bordered pagination={paginationProps}

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,
});
}
} }
}); });
}; };

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