Compare commits

...

129 Commits

Author SHA1 Message Date
543b316942 feat: update swagger parameter id description (#1532) 2023-02-10 10:42:16 +08:00
e2b6e8ee6e chore: unify migrate database way (#1530) 2023-02-09 19:28:15 +08:00
wht
e7e0518517 feat: fix the upload file name contains space problem (#1527) 2023-02-07 23:26:17 +08:00
943aa61869 feat: add provider icons and menus (#1522)
* fix: add provider icons(email and captcha) and menus

* fix: add provider icons and menus
2023-02-06 20:28:40 +08:00
wht
fcc75dd3be feat: fix the Unicode filename encoding bug in storage provider (#1518) 2023-02-04 18:09:18 +08:00
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
f465fc6ce0 feat: support changing theme in antd 5 (#1430)
* feat: add global theme change function

* feat: add icons

* feat: in app theme changer

* feat: use antd built-in themes

* fix: multiple styling problem

* fix: theme init from localstorage

* feat: dark mode footer

* feat: casdoor logo color theme

* feat: select theme box icon adaptive to theme

* fix: menu bar style

* fix: language box style

* feat: translation

* feat: update translation of select theme box without reloading

* fix: mobile view

* fix: better structured select theme box

* feat: add compact icon

* fix: redundant theme fetch

* fix: redundant theme fetch

* fix: various styling problems
2022-12-29 22:30:37 +08:00
c952c2f2f4 feat: fix login with password bug when feature is disabled (#1428) 2022-12-27 14:46:57 +08:00
86ae97d1e5 feat: fix the bug that spin is always showing when response error (#1424) 2022-12-24 17:55:36 +08:00
6ea73e3eca fix: show background image in preview (#1425) 2022-12-24 17:47:05 +08:00
a71a190db5 feat: fix bug in redirectToLoginPage() (#1422) 2022-12-24 01:10:02 +08:00
da69d94445 feat: fix the bug that spin in oauth is always showing (#1421) 2022-12-23 15:06:51 +08:00
b8b915abe1 feat: check AccessPermission in multiple permissions (#1420) 2022-12-23 14:06:02 +08:00
5d1548e989 feat: fix absolute URL redirection (#1419)
* fix: redirect to absolute url

* fix: original jump
2022-12-23 11:05:15 +08:00
a0dc6e06cd feat: add EntryPage for login, signup pages to fix background flashing issue (#1416)
* feat: fix flush in login Pages

* fix: code format

* fix: improve code

* Update App.js

* Update EntryPage.js

* fix: optimize api request

* Update App.js

* Update App.js

* fix: fix css

* fix: css and getApllicationObj

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-12-22 23:39:02 +08:00
ae130788ec feat: add Line support as OAuth 3rd-party login (#1413) 2022-12-21 02:25:58 +08:00
f075d0fd74 Refactor out application.IsRedirectUriValid() 2022-12-21 00:35:33 +08:00
65d4946042 feat: add valid key for creating token (#1411) 2022-12-20 22:05:00 +08:00
Liu
26acece8af feat: add all other missing objects to init_data (#1407)
* Add all other missing objects to init_data.json

* Format golang code

* feat: add all other missing objects to init_data

* feat: add all other missing objects to init_data
2022-12-18 01:49:42 +08:00
48a0c8473f Improve README 2022-12-18 01:41:12 +08:00
082ae3c91e fix: fix undefined owner bug in AdapterEditPage (#1406) 2022-12-17 21:21:39 +08:00
1ee2ff1d30 feat: now dingtalk OAuth returns all error messages to frontend (#1405) 2022-12-17 21:10:20 +08:00
c0d9969013 Add description to product 2022-12-16 23:35:30 +08:00
1bdee13150 Fix bug in renderQrCodeModal() 2022-12-16 23:28:43 +08:00
d668022af0 feat: fix length of policy and [policy_define] in model inconsistent (#1400) 2022-12-15 20:42:55 +08:00
e227875c2b feat: add post methed for saml response (#1399) 2022-12-13 22:32:45 +08:00
e473de3162 feat: fix sign in error via webauthn (#1398)
* fix: fix sign in error via webauthn

* fix review problems
2022-12-13 16:57:42 +08:00
c5ef841d3f Disable isValidIdCard() 2022-12-12 01:07:31 +08:00
d46288b591 Add renderQrCodeModal() 2022-12-12 00:42:45 +08:00
b968bf033c fix: case insensitive country name and country abbreviation search in region selection (#1394) 2022-12-11 18:14:25 +08:00
eca2527bc0 feat: fix bug in signup and reset phone and email (#1396)
* fix: fix bug in signup and reset phone and email

* delete useless addition
2022-12-11 15:52:36 +08:00
ef836acfe9 fix: login page flag icon preload (#1393) 2022-12-11 11:22:58 +08:00
a51f0d7c08 feat: init score in organization (#1388)
* feat: init score in organization

* Update OrganizationEditPage.js

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-12-10 22:27:12 +08:00
e3c36beaf4 fix: the link button with disabled style but can click (#1390) 2022-12-10 22:14:20 +08:00
19dce838d1 fix: fix invalid url in applications page (#1389) 2022-12-10 22:06:21 +08:00
b41d8652f0 feat: fix showing wrong error messages (#1385) 2022-12-09 15:11:13 +08:00
e705eecffe feat: response with status in casbin_adapter.go (#1384)
* fix: response standardized information with status in `casbin_adapter.go`

* fix: remove redundant statements
2022-12-08 10:22:59 +08:00
2bb2c36f22 fix: add crowdin action env (#1381) 2022-12-07 14:05:21 +08:00
1bb3d2dea9 feat: refactor backend i18n (#1373)
* fix: handle the dataSourceName when DB changes

* reduce duplication of code

* feat: refactor translation error message

* feat: use json intsead of ini file

* remove useless translation

* fix translate problems

* remove useless addition

* fix pr problems

* fix pr problems

* fix split problem

* use gofumpt to fmt code

* use crowdin to execute backend translation

* fix pr problems

* refactor: change translation file structure same as frontend

* delete useless output

* update go.mod
2022-12-07 13:13:23 +08:00
96566a626b Increase Detail field size 2022-12-07 01:53:03 +08:00
042e52bd16 feat: replace casdoor/goth with markbates/goth (#1374) 2022-12-06 17:18:29 +08:00
e207fd243b feat: fix CSS issue that login error pages are not centered (#1371) 2022-12-06 14:00:17 +08:00
30b7fd963f Reduce Resource key size 2022-12-06 11:30:42 +08:00
ca314bbfb5 feat: refactor layout and fix footer CSS (#1370) 2022-12-06 00:50:17 +08:00
812c44e070 feat: add and load policy within a specific permission (#1357)
* fix: add and load policy with a specific permission

* fix: use a clear variable name
2022-12-05 17:07:10 +08:00
78e45d07cf fix: support RBAC With Domains/Tenants (#1333)
* feat: support RBAC With Domains/Tenants

* fix: add verify for `UpdatePermission`

* Update permission.go

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-12-05 16:08:17 +08:00
0856977b92 feat: update to antd 5.0 (#1362)
* feat: update to ant5.X

* fix: incompatible styles

* fix: adjust the style
2022-12-04 23:05:30 +08:00
a44a4b0300 feat: fix React incorrect usage to fix issue that verification code must be submitted twice to succeed (#1348)
* fix: synchronized user login fields saving

* fix: synchronized user login fields saving

* recover changes

* fix: save username in step 2

* fix: format
2022-12-04 20:58:07 +08:00
4b29dd8c41 fix: responsive certs page editor (#1360)
* fix: responsive editor width

* fix: format
2022-12-04 16:04:04 +08:00
165e2e33e3 fix: disable formcss in mobile (#1359) 2022-12-04 15:53:46 +08:00
d13a307ad5 Allow org admin to access GetResources() 2022-12-03 01:10:45 +08:00
27bd771fed feat: handle the dataSourceName when DB changes (#1352)
* fix: handle the dataSourceName when DB changes

* reduce duplication of code
2022-12-02 22:20:18 +08:00
9f3ee275a8 feat: reformat frontend alert texts with correct i18n (#1341)
* fix: add i18

* fix: standard prompt message
2022-12-02 00:06:28 +08:00
fcda64ad7d fix: provider sort alphabetical order (#1347) 2022-12-01 22:51:10 +08:00
d815bf92bd fix: handle add message in frontend (#1340) 2022-11-29 20:32:47 +08:00
7867060b71 feat: add quota limitation to organizations, users, providers and applications (#1339) 2022-11-29 11:01:41 +08:00
8890d1d7c7 fix: check credential existence when signing via WebAuthn (#1336)
* fix: check credential existence when signing via WebAuthn

* fix review problem
2022-11-28 21:47:17 +08:00
6e6a0a074a fix: application edit mobile view (#1331)
* fix: application edit mobile view

* fix: decompose elements

* fix: decomposition

* fix: remove space component

* Update ApplicationEditPage.js

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-11-28 21:10:49 +08:00
cff3007992 feat: add get-permissions-by-role API (#1335) 2022-11-28 15:30:46 +08:00
fe448cbcf4 feat: check user existence when signing in via verification code (#1334)
* fix:check user existence when logining by verification code

* fix review problems

* Update verification.go

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-11-28 00:11:33 +08:00
2ab25df950 fix: prompt page translation (#1330)
* fix: prompt page translation

* add multiple translations

* fix: translation consistency

* fix: translation consistency

* fix: add translation

* fix: add translation

* Update data.json

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-11-27 21:04:45 +08:00
b895926754 feat: use another filename when uploading a duplicated file instead of replacing it (#1329)
* fix: upload a file with the same name, not replace

* Update resource.go

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-11-27 17:32:15 +08:00
5bb7a4153f feat: add cloudflare turnstile captcha (#1327)
* feat: add cloudflare turnstile captcha

* fix: rename turnstile to cloudflare turnstile
2022-11-26 17:17:49 +08:00
b7cd598ee8 fix: fail to return after flush the page (#1325)
* fix: fail to return after flush the page 

Old methed just get the url path parameter when click the butten. But when the page flushed, the returnUrl will disappear, so can not return to the specified page.

* Update UserEditPage.js

* Update UserEditPage.js

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-11-25 23:08:45 +08:00
b10fb97c92 feat: finish policy list management (#1317) 2022-11-25 16:02:20 +08:00
b337b908ea feat: fix the bug that admin cannot upload avatar for other users (#1323) 2022-11-25 09:36:47 +08:00
ba9d1e2388 fix: fix bug in GetAcceptLanguage() (#1322) 2022-11-24 20:43:35 +08:00
29ec1d2d9c feat: update Xorm to v1.0.5 to fix the PostgreSQL bug in Xorm (#1321)
* fix:update xorm version

* fix pr problems

* update xorm version to 1.2.0

* update xorm version to 1.0.5

* fix pr problems

* generate go.sum file
2022-11-24 19:28:51 +08:00
84a03f6c8e feat: add webhook for add/update org/provider (#1316) 2022-11-24 00:29:15 +08:00
56ff06bbea feat: add parameter v0 for Casbin APIs (#1315) 2022-11-23 22:39:17 +08:00
7e756b8ee2 feat: manager applications in organization scope (#1290)
* feat: manager applications in organization scope(front end)

* fix: application can use own organization and admin provider

* fix: improve methed to get provider

* fix: modify provider methods by convention
2022-11-21 01:17:55 +08:00
19ba37e0c2 feat: can specify available UI languages for an organization (#1306) 2022-11-19 22:11:19 +08:00
b98ce19211 feat: fix bug in GetDefaultApplication() that caused login error for other orgs (#1299)
* fix:fix bug in GetDefaultApplication

* fix:fix bug in GetDefaultApplication
2022-11-16 00:39:05 +08:00
37d1a73c0c feat: encode redirectUri (#1297) 2022-11-15 19:05:59 +08:00
727877cf54 fix: illegal user when new a permission (#1298) 2022-11-15 14:19:20 +08:00
228 changed files with 11018 additions and 4094 deletions

5
.gitattributes vendored
View File

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

View File

@ -76,11 +76,59 @@ jobs:
version: latest
args: --disable-all -c dummy.yml -E=gofumpt --max-same-issues=0 --timeout 5m --modules-download-mode=mod
e2e:
name: e2e-test
runs-on: ubuntu-latest
needs: [ 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:
name: Release And Push
runs-on: ubuntu-latest
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push'
needs: [ frontend, backend, linter ]
needs: [ frontend, backend, linter, e2e ]
steps:
- name: Checkout
uses: actions/checkout@v2

View File

@ -33,3 +33,24 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: '463556'
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
- name: crowdin backend action
uses: crowdin/github-action@1.4.8
with:
upload_translations: true
download_translations: true
push_translations: true
commit_message: 'refactor: New Crowdin Backend translations by Github Action'
localization_branch_name: l10n_crowdin_action
create_pull_request: true
pull_request_title: 'refactor: New Crowdin Backend translations'
crowdin_branch_name: l10n_branch
config: './crowdin.yml'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: '463556'
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View File

@ -44,14 +44,12 @@
## Online demo
- International: https://door.casdoor.org (read-only)
- Asian mirror: https://door.casdoor.com (read-only)
- Asian mirror: https://demo.casdoor.com (read-write, will restore for every 5 minutes)
- Read-only site: https://door.casdoor.com (any modification operation will fail)
- Writable site: https://demo.casdoor.com (original data will be restored for every 5 minutes)
## Documentation
- International: https://casdoor.org
- Asian mirror: https://casdoor.cn
https://casdoor.org
## Install

View File

@ -32,7 +32,9 @@ func InitAuthz() {
var err error
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
a, err := xormadapter.NewAdapterWithTableName(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName()+conf.GetConfigString("dbName"), "casbin_rule", tableNamePrefix, true)
driverName := conf.GetConfigString("driverName")
dataSourceName := conf.GetConfigRealDataSourceName(driverName)
a, err := xormadapter.NewAdapterWithTableName(driverName, dataSourceName, "casbin_rule", tableNamePrefix, true)
if err != nil {
panic(err)
}
@ -158,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 {
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
} else if urlPath == "/api/update-user" {
// Allow ordinary users to update their own information

View File

@ -31,6 +31,8 @@ func GetCaptchaProvider(captchaType string) CaptchaProvider {
return NewAliyunCaptchaProvider()
} else if captchaType == "GEETEST" {
return NewGEETESTCaptchaProvider()
} else if captchaType == "Cloudflare Turnstile" {
return NewCloudflareTurnstileProvider()
}
return nil
}

66
captcha/turnstile.go Normal file
View File

@ -0,0 +1,66 @@
// 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 captcha
import (
"encoding/json"
"errors"
"io"
"net/http"
"net/url"
"strings"
)
const CloudflareTurnstileVerifyUrl = "https://challenges.cloudflare.com/turnstile/v0/siteverify"
type CloudflareTurnstileProvider struct{}
func NewCloudflareTurnstileProvider() *CloudflareTurnstileProvider {
captcha := &CloudflareTurnstileProvider{}
return captcha
}
func (captcha *CloudflareTurnstileProvider) VerifyCaptcha(token, clientSecret string) (bool, error) {
reqData := url.Values{
"secret": {clientSecret},
"response": {token},
}
resp, err := http.PostForm(CloudflareTurnstileVerifyUrl, reqData)
if err != nil {
return false, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return false, err
}
type captchaResponse struct {
Success bool `json:"success"`
ErrorCodes []string `json:"error-codes"`
}
captchaResp := &captchaResponse{}
err = json.Unmarshal(body, captchaResp)
if err != nil {
return false, err
}
if len(captchaResp.ErrorCodes) > 0 {
return false, errors.New(strings.Join(captchaResp.ErrorCodes, ","))
}
return captchaResp.Success, nil
}

View File

@ -21,3 +21,4 @@ isDemoMode = false
batchSize = 100
ldapServerPort = 389
languages = en,zh,es,fr,de,ja,ko,ru
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}

View File

@ -15,6 +15,7 @@
package conf
import (
"encoding/json"
"fmt"
"os"
"runtime"
@ -24,6 +25,15 @@ import (
"github.com/beego/beego"
)
type Quota struct {
Organization int `json:"organization"`
User int `json:"user"`
Application int `json:"application"`
Provider int `json:"provider"`
}
var quota = &Quota{-1, -1, -1, -1}
func init() {
// this array contains the beego configuration items that may be modified via env
presetConfigItems := []string{"httpport", "appname"}
@ -35,6 +45,17 @@ func init() {
}
}
}
initQuota()
}
func initQuota() {
res := beego.AppConfig.String("quota")
if res != "" {
err := json.Unmarshal([]byte(res), quota)
if err != nil {
panic(err)
}
}
}
func GetConfigString(key string) string {
@ -95,3 +116,17 @@ func GetConfigBatchSize() int {
}
return res
}
func GetConfigQuota() *Quota {
return quota
}
func GetConfigRealDataSourceName(driverName string) string {
var dataSourceName string
if driverName != "mysql" {
dataSourceName = GetConfigDataSourceName()
} else {
dataSourceName = GetConfigDataSourceName() + GetConfigString("dbName")
}
return dataSourceName
}

View File

@ -93,3 +93,19 @@ func TestGetConfBool(t *testing.T) {
})
}
}
func TestGetConfigQuota(t *testing.T) {
scenarios := []struct {
description string
expected *Quota
}{
{"default", &Quota{-1, -1, -1, -1}},
}
err := beego.LoadAppConfig("ini", "app.conf")
assert.Nil(t, err)
for _, scenery := range scenarios {
quota := GetConfigQuota()
assert.Equal(t, scenery.expected, quota)
}
}

View File

@ -17,6 +17,7 @@ package controllers
import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
@ -102,7 +103,7 @@ type Captcha struct {
// @router /signup [post]
func (c *ApiController) Signup() {
if c.GetSessionUsername() != "" {
c.ResponseError(c.T("SignUpErr.SignOutFirst"), c.GetSessionUsername())
c.ResponseError(c.T("account:Please sign out first before signing up"), c.GetSessionUsername())
return
}
@ -115,7 +116,7 @@ func (c *ApiController) Signup() {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if !application.EnableSignUp {
c.ResponseError(c.T("SignUpErr.DoNotAllowSignUp"))
c.ResponseError(c.T("account:The application does not allow to sign up new account"))
return
}
@ -129,7 +130,7 @@ func (c *ApiController) Signup() {
if application.IsSignupItemVisible("Email") && application.GetSignupItemRule("Email") != "No verification" && form.Email != "" {
checkResult := object.CheckVerificationCode(form.Email, form.EmailCode, c.GetAcceptLanguage())
if len(checkResult) != 0 {
c.ResponseError(c.T("EmailErr.EmailCheckResult"), checkResult)
c.ResponseError(c.T("account:Email: %s"), checkResult)
return
}
}
@ -139,7 +140,7 @@ func (c *ApiController) Signup() {
checkPhone = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Phone)
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode, c.GetAcceptLanguage())
if len(checkResult) != 0 {
c.ResponseError(c.T("PhoneErr.PhoneCheckResult"), checkResult)
c.ResponseError(c.T("account:Phone: %s"), checkResult)
return
}
}
@ -161,9 +162,9 @@ func (c *ApiController) Signup() {
username = id
}
initScore, err := getInitScore()
initScore, err := getInitScore(organization)
if err != nil {
c.ResponseError(fmt.Errorf(c.T("InitErr.InitScoreFailed"), err).Error())
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
return
}
@ -209,7 +210,7 @@ func (c *ApiController) Signup() {
affected := object.AddUser(user)
if !affected {
c.ResponseError(c.T("UserErr.InvalidInformation"), util.StructToJson(user))
c.ResponseError(c.T("account:Invalid information"), util.StructToJson(user))
return
}
@ -238,20 +239,67 @@ func (c *ApiController) Signup() {
// @Title Logout
// @Tag Login API
// @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
// @router /logout [get,post]
func (c *ApiController) Logout() {
user := c.GetSessionUsername()
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
application := c.GetSessionApplication()
c.ClearUserSession()
// https://openid.net/specs/openid-connect-rpinitiated-1_0-final.html
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 == "" {
c.ResponseOk(user)
if accessToken == "" && redirectUri == "" {
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
} 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
@ -271,12 +319,17 @@ func (c *ApiController) GetAccount() {
user = object.ExtendManagedAccountsWithUser(user)
}
object.ExtendUserWithRolesAndPermissions(user)
user.Permissions = object.GetMaskedPermissions(user.Permissions)
user.Roles = object.GetMaskedRoles(user.Roles)
organization := object.GetMaskedOrganization(object.GetOrganizationByUser(user))
resp := Response{
Status: "ok",
Sub: user.Id,
Name: user.Name,
Data: user,
Data: object.GetMaskedUser(user),
Data2: organization,
}
c.Data["json"] = resp

View File

@ -46,7 +46,7 @@ func (c *ApiController) GetApplications() {
if organization == "" {
applications = object.GetApplications(owner)
} else {
applications = object.GetApplicationsByOrganizationName(owner, organization)
applications = object.GetOrganizationApplications(owner, organization)
}
c.Data["json"] = object.GetMaskedApplications(applications, userId)
@ -63,7 +63,7 @@ func (c *ApiController) GetApplications() {
// @Title GetApplication
// @Tag Application API
// @Description get the detail of an application
// @Param id query string true "The id of the application."
// @Param id query string true "The id ( owner/name ) of the application."
// @Success 200 {object} object.Application The Response object
// @router /get-application [get]
func (c *ApiController) GetApplication() {
@ -78,7 +78,7 @@ func (c *ApiController) GetApplication() {
// @Title GetUserApplication
// @Tag Application API
// @Description get the detail of the user's application
// @Param id query string true "The id of the user"
// @Param id query string true "The id ( owner/name ) of the user"
// @Success 200 {object} object.Application The Response object
// @router /get-user-application [get]
func (c *ApiController) GetUserApplication() {
@ -86,7 +86,7 @@ func (c *ApiController) GetUserApplication() {
id := c.Input().Get("id")
user := object.GetUser(id)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExist"), id))
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), id))
return
}
@ -103,24 +103,38 @@ func (c *ApiController) GetUserApplication() {
// @router /get-organization-applications [get]
func (c *ApiController) GetOrganizationApplications() {
userId := c.GetSessionUsername()
owner := c.Input().Get("owner")
organization := c.Input().Get("organization")
owner := c.Input().Get("owner")
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")
if organization == "" {
c.ResponseError(c.T("ParameterErr.OrgMissingErr"))
c.ResponseError(c.T("general:Missing parameter") + ": organization")
return
}
applications := object.GetApplicationsByOrganizationName(owner, organization)
c.Data["json"] = object.GetMaskedApplications(applications, userId)
c.ServeJSON()
if limit == "" || page == "" {
var applications []*object.Application
applications = object.GetOrganizationApplications(owner, organization)
c.Data["json"] = object.GetMaskedApplications(applications, userId)
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetOrganizationApplicationCount(owner, organization, field, value)))
applications := object.GetMaskedApplications(object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder), userId)
c.ResponseOk(applications, paginator.Nums())
}
}
// UpdateApplication
// @Title UpdateApplication
// @Tag Application API
// @Description update an application
// @Param id query string true "The id of the application"
// @Param id query string true "The id ( owner/name ) of the application"
// @Param body body object.Application true "The details of the application"
// @Success 200 {object} controllers.Response The Response object
// @router /update-application [post]
@ -153,6 +167,12 @@ func (c *ApiController) AddApplication() {
return
}
count := object.GetApplicationCount("", "", "")
if err := checkQuotaForApplication(count); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddApplication(&application))
c.ServeJSON()
}

View File

@ -52,7 +52,7 @@ func tokenToResponse(token *object.Token) *Response {
if 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 ...
@ -65,7 +65,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
return
}
if !allowed {
c.ResponseError(c.T("AuthErr.Unauthorized"))
c.ResponseError(c.T("auth:Unauthorized operation"))
return
}
@ -84,7 +84,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
codeChallenge := c.Input().Get("code_challenge")
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
c.ResponseError(c.T("AuthErr.ChallengeMethodErr"))
c.ResponseError(c.T("auth:Challenge method should be S256"))
return
}
code := object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce, codeChallenge, c.Ctx.Request.Host, c.GetAcceptLanguage())
@ -103,12 +103,12 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
resp = tokenToResponse(token)
}
} else if form.Type == ResponseTypeSaml { // saml flow
res, redirectUrl, err := object.GetSamlResponse(application, user, form.SamlRequest, c.Ctx.Request.Host)
res, redirectUrl, method, err := object.GetSamlResponse(application, user, form.SamlRequest, c.Ctx.Request.Host)
if err != nil {
c.ResponseError(err.Error(), nil)
return
}
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: redirectUrl}
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]string{"redirectUrl": redirectUrl, "method": method}}
} else if form.Type == ResponseTypeCas {
// not oauth but CAS SSO protocol
service := c.Input().Get("service")
@ -139,6 +139,10 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
})
}
if resp.Status == "ok" {
object.SetSession(user.GetId(), c.Ctx.Input.CruSession.SessionID())
}
return resp
}
@ -170,13 +174,36 @@ func (c *ApiController) GetApplicationLogin() {
}
func setHttpClient(idProvider idp.IdProvider, providerType string) {
if providerType == "GitHub" || providerType == "Google" || providerType == "Facebook" || providerType == "LinkedIn" || providerType == "Steam" {
if isProxyProviderType(providerType) {
idProvider.SetHttpClient(proxy.ProxyHttpClient)
} else {
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 ...
// @Title Login
// @Tag Login API
@ -205,7 +232,7 @@ func (c *ApiController) Login() {
if form.Username != "" {
if form.Type == ResponseTypeLogin {
if c.GetSessionUsername() != "" {
c.ResponseError(c.T("LoginErr.SignOutFirst"), c.GetSessionUsername())
c.ResponseError(c.T("account:Please sign out first before signing in"), c.GetSessionUsername())
return
}
}
@ -222,27 +249,33 @@ func (c *ApiController) Login() {
}
// check result through Email or Phone
var checkDest string
if strings.Contains(form.Username, "@") {
verificationCodeType = "email"
if user != nil && util.GetMaskedEmail(user.Email) == form.Username {
form.Username = user.Email
}
checkResult = object.CheckVerificationCode(form.Username, form.Code, c.GetAcceptLanguage())
checkDest = form.Username
} else {
verificationCodeType = "phone"
if len(form.PhonePrefix) == 0 {
responseText := fmt.Sprintf(c.T("PhoneErr.NoPrefix"), verificationCodeType)
responseText := fmt.Sprintf(c.T("auth:%s No phone prefix"), verificationCodeType)
c.ResponseError(responseText)
return
}
if user != nil && util.GetMaskedPhone(user.Phone) == form.Username {
form.Username = user.Phone
}
checkPhone := fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)
checkResult = object.CheckVerificationCode(checkPhone, form.Code, c.GetAcceptLanguage())
checkDest = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)
}
user = object.GetUserByFields(form.Organization, form.Username)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(form.Organization, form.Username)))
return
}
checkResult = object.CheckSigninCode(user, checkDest, form.Code, c.GetAcceptLanguage())
if len(checkResult) != 0 {
responseText := fmt.Sprintf("%s%s", verificationCodeType, checkResult)
responseText := fmt.Sprintf("%s - %s", verificationCodeType, checkResult)
c.ResponseError(responseText)
return
}
@ -253,16 +286,14 @@ func (c *ApiController) Login() {
} else {
object.DisableVerificationCode(fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username))
}
user = object.GetUserByFields(form.Organization, form.Username)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("LoginErr.UserDoNotExist"), form.Organization, form.Username))
return
}
} else {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil {
c.ResponseError(fmt.Sprintf("The application: %s does not exist", form.Application))
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
return
}
if !application.EnablePassword {
c.ResponseError(c.T("auth:The login method: login with password is not enabled for the application"))
return
}
@ -274,7 +305,7 @@ func (c *ApiController) Login() {
}
if !isHuman {
c.ResponseError("Turing test failed.")
c.ResponseError(c.T("auth:Turing test failed."))
return
}
}
@ -288,7 +319,7 @@ func (c *ApiController) Login() {
} else {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("LoginErr.AppDoNotExist"), form.Application))
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
return
}
@ -302,15 +333,15 @@ func (c *ApiController) Login() {
} else if form.Provider != "" {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("LoginErr.AppDoNotExist"), form.Application))
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
return
}
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", application.Organization))
provider := object.GetProvider(fmt.Sprintf("admin/%s", form.Provider))
provider := object.GetProvider(util.GetId("admin", form.Provider))
providerItem := application.GetProviderItem(provider.Name)
if !providerItem.IsProviderVisible() {
c.ResponseError(fmt.Sprintf(c.T("ProviderErr.ProviderNotEnabled"), provider.Name))
c.ResponseError(fmt.Sprintf(c.T("auth:The provider: %s is not enabled for the application"), provider.Name))
return
}
@ -334,14 +365,14 @@ func (c *ApiController) Login() {
idProvider := idp.GetIdProvider(provider.Type, provider.SubType, clientId, clientSecret, provider.AppId, form.RedirectUri, provider.Domain, provider.CustomAuthUrl, provider.CustomTokenUrl, provider.CustomUserInfoUrl)
if idProvider == nil {
c.ResponseError(fmt.Sprintf(c.T("ProviderErr.ProviderNotSupported"), provider.Type))
c.ResponseError(fmt.Sprintf(c.T("auth:The provider type: %s is not supported"), provider.Type))
return
}
setHttpClient(idProvider, provider.Type)
if form.State != conf.GetConfigString("authState") && form.State != application.Name {
c.ResponseError(fmt.Sprintf(c.T("AuthErr.AuthStateWrong"), conf.GetConfigString("authState"), form.State))
c.ResponseError(fmt.Sprintf(c.T("auth:State expected: %s, but got: %s"), conf.GetConfigString("authState"), form.State))
return
}
@ -353,13 +384,13 @@ func (c *ApiController) Login() {
}
if !token.Valid() {
c.ResponseError(c.T("TokenErr.InvalidToken"))
c.ResponseError(c.T("auth:Invalid token"))
return
}
userInfo, err = idProvider.GetUserInfo(token)
if err != nil {
c.ResponseError(fmt.Sprintf(c.T("LoginErr.LoginFail"), err.Error()))
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to login in: %s"), err.Error()))
return
}
}
@ -376,7 +407,7 @@ func (c *ApiController) Login() {
// Sign in via OAuth (want to sign up but already have account)
if user.IsForbidden {
c.ResponseError(c.T("LoginErr.UserIsForbidden"))
c.ResponseError(c.T("auth:The user is forbidden to sign in, please contact the administrator"))
}
resp = c.HandleLoggedIn(application, user, &form)
@ -388,63 +419,70 @@ func (c *ApiController) Login() {
} else if provider.Category == "OAuth" {
// Sign up via OAuth
if !application.EnableSignUp {
c.ResponseError(fmt.Sprintf(c.T("LoginErr.AppNotEnableSignUp"), provider.Type, userInfo.Username, userInfo.DisplayName))
c.ResponseError(fmt.Sprintf(c.T("auth: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"), provider.Type, userInfo.Username, userInfo.DisplayName))
return
}
if !providerItem.CanSignUp {
c.ResponseError(fmt.Sprintf(c.T("LoginErr.ProviderCanNotSignUp"), provider.Type, userInfo.Username, userInfo.DisplayName, provider.Type))
c.ResponseError(fmt.Sprintf(c.T("auth: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"), provider.Type, userInfo.Username, userInfo.DisplayName, provider.Type))
return
}
// Handle username conflicts
tmpUser := object.GetUser(fmt.Sprintf("%s/%s", application.Organization, userInfo.Username))
if tmpUser != nil {
uid, err := uuid.NewRandom()
if application.EnableLinkWithEmail {
// find user that has the same email
user = object.GetUserByField(application.Organization, "email", userInfo.Email)
}
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 {
c.ResponseError(err.Error())
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
return
}
uidStr := strings.Split(uid.String(), "-")
userInfo.Username = fmt.Sprintf("%s_%s", userInfo.Username, uidStr[1])
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,
}
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()
if err != nil {
c.ResponseError(fmt.Errorf(c.T("InitErr.InitScoreFailed"), 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
object.SetUserOAuthProperties(organization, user, provider.Type, userInfo)
affected := object.AddUser(user)
if !affected {
c.ResponseError(fmt.Sprintf(c.T("LoginErr.InvalidUserInformation"), util.StructToJson(user)))
return
}
object.LinkUserAccount(user, provider.Type, userInfo.Id)
resp = c.HandleLoggedIn(application, user, &form)
@ -460,19 +498,19 @@ func (c *ApiController) Login() {
record2.User = user.Name
util.SafeGoroutine(func() { object.AddRecord(record2) })
} else if provider.Category == "SAML" {
resp = &Response{Status: "error", Msg: "The account does not exist"}
resp = &Response{Status: "error", Msg: fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(application.Organization, userInfo.Id))}
}
// resp = &Response{Status: "ok", Msg: "", Data: res}
} else { // form.Method != "signup"
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("LoginErr.AccountDoNotExist"), userInfo)
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(application.Organization, userInfo.Id)), userInfo)
return
}
oldUser := object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
if oldUser != nil {
c.ResponseError(fmt.Sprintf(c.T("LoginErr.OldUser"), provider.Type, userInfo.Username, userInfo.DisplayName, oldUser.Name, oldUser.DisplayName))
c.ResponseError(fmt.Sprintf(c.T("auth:The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)"), provider.Type, userInfo.Username, userInfo.DisplayName, oldUser.Name, oldUser.DisplayName))
return
}
@ -493,7 +531,7 @@ func (c *ApiController) Login() {
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("LoginErr.AppDoNotExist"), form.Application))
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
return
}
@ -505,7 +543,7 @@ func (c *ApiController) Login() {
record.User = user.Name
util.SafeGoroutine(func() { object.AddRecord(record) })
} else {
c.ResponseError(fmt.Sprintf(c.T("LoginErr.UnknownAuthentication"), util.StructToJson(form)))
c.ResponseError(fmt.Sprintf(c.T("auth:Unknown authentication type (not password or provider), form = %s"), util.StructToJson(form)))
return
}
}

View File

@ -210,7 +210,7 @@ func (c *RootController) SamlValidate() {
}
if !strings.HasPrefix(target, service) {
c.ResponseError(fmt.Sprintf(c.T("CasErr.ServiceDoNotMatch"), target, service))
c.ResponseError(fmt.Sprintf(c.T("cas:Service %s and %s do not match"), target, service))
return
}

View File

@ -18,6 +18,7 @@ import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -31,8 +32,8 @@ func (c *ApiController) GetCasbinAdapters() {
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
if limit == "" || page == "" {
c.Data["json"] = object.GetCasbinAdapters(owner)
c.ServeJSON()
adapters := object.GetCasbinAdapters(owner)
c.ResponseOk(adapters)
} else {
limit := util.ParseInt(limit)
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetCasbinAdapterCount(owner, field, value)))
@ -43,8 +44,8 @@ func (c *ApiController) GetCasbinAdapters() {
func (c *ApiController) GetCasbinAdapter() {
id := c.Input().Get("id")
c.Data["json"] = object.GetCasbinAdapter(id)
c.ServeJSON()
adapter := object.GetCasbinAdapter(id)
c.ResponseOk(adapter)
}
func (c *ApiController) UpdateCasbinAdapter() {
@ -89,6 +90,68 @@ func (c *ApiController) SyncPolicies() {
id := c.Input().Get("id")
adapter := object.GetCasbinAdapter(id)
c.Data["json"] = object.SyncPolicies(adapter)
policies, err := object.SyncPolicies(adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(policies)
}
func (c *ApiController) UpdatePolicy() {
id := c.Input().Get("id")
adapter := object.GetCasbinAdapter(id)
var policies []xormadapter.CasbinRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policies)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.UpdatePolicy(util.CasbinToSlice(policies[0]), util.CasbinToSlice(policies[1]), adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}
func (c *ApiController) AddPolicy() {
id := c.Input().Get("id")
adapter := object.GetCasbinAdapter(id)
var policy xormadapter.CasbinRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.AddPolicy(util.CasbinToSlice(policy), adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}
func (c *ApiController) RemovePolicy() {
id := c.Input().Get("id")
adapter := object.GetCasbinAdapter(id)
var policy xormadapter.CasbinRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.RemovePolicy(util.CasbinToSlice(policy), adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}

View File

@ -52,7 +52,7 @@ func (c *ApiController) GetCerts() {
// @Title GetCert
// @Tag Cert API
// @Description get cert
// @Param id query string true "The id of the cert"
// @Param id query string true "The id ( owner/name ) of the cert"
// @Success 200 {object} object.Cert The Response object
// @router /get-cert [get]
func (c *ApiController) GetCert() {
@ -66,7 +66,7 @@ func (c *ApiController) GetCert() {
// @Title UpdateCert
// @Tag Cert API
// @Description update cert
// @Param id query string true "The id of the cert"
// @Param id query string true "The id ( owner/name ) of the cert"
// @Param body body object.Cert true "The details of the cert"
// @Success 200 {object} controllers.Response The Response object
// @router /update-cert [post]

View File

@ -21,12 +21,6 @@ import (
)
func (c *ApiController) Enforce() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("EnforcerErr.SignInFirst"))
return
}
var permissionRule object.PermissionRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permissionRule)
if err != nil {
@ -34,17 +28,11 @@ func (c *ApiController) Enforce() {
return
}
c.Data["json"] = object.Enforce(userId, &permissionRule)
c.Data["json"] = object.Enforce(&permissionRule)
c.ServeJSON()
}
func (c *ApiController) BatchEnforce() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("EnforcerErr.SignInFirst"))
return
}
var permissionRules []object.PermissionRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &permissionRules)
if err != nil {
@ -52,14 +40,14 @@ func (c *ApiController) BatchEnforce() {
return
}
c.Data["json"] = object.BatchEnforce(userId, permissionRules)
c.Data["json"] = object.BatchEnforce(permissionRules)
c.ServeJSON()
}
func (c *ApiController) GetAllObjects() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("EnforcerErr.SignInFirst"))
c.ResponseError(c.T("general:Please login first"))
return
}
@ -70,7 +58,7 @@ func (c *ApiController) GetAllObjects() {
func (c *ApiController) GetAllActions() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("EnforcerErr.SignInFirst"))
c.ResponseError(c.T("general:Please login first"))
return
}
@ -81,7 +69,7 @@ func (c *ApiController) GetAllActions() {
func (c *ApiController) GetAllRoles() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("EnforcerErr.SignInFirst"))
c.ResponseError(c.T("general:Please login first"))
return
}

View File

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

View File

@ -105,14 +105,34 @@ func handleSearch(w ldapserver.ResponseWriter, m *ldapserver.Message) {
}
for i := 0; i < len(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.AddAttribute("cn", message.AttributeValue(user.Name))
e.AddAttribute("uid", message.AttributeValue(user.Name))
e.AddAttribute("email", message.AttributeValue(user.Email))
e.AddAttribute("mobile", message.AttributeValue(user.Phone))
e.AddAttribute("userPassword", message.AttributeValue(getUserPasswordWithType(user)))
// e.AddAttribute("postalAddress", message.AttributeValue(user.Address[0]))
w.Write(e)
}
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

@ -47,7 +47,7 @@ func (c *ApiController) Unlink() {
if user.Id != unlinkedUser.Id && !user.IsGlobalAdmin {
// if the user is not the same as the one we are unlinking, we need to make sure the user is the global admin.
c.ResponseError(c.T("AuthErr.CanNotUnlinkUsers"))
c.ResponseError(c.T("link:You are not the global admin, you can't unlink other users"))
return
}
@ -55,23 +55,23 @@ func (c *ApiController) Unlink() {
// if the user is unlinking themselves, should check the provider can be unlinked, if not, we should return an error.
application := object.GetApplicationByUser(user)
if application == nil {
c.ResponseError(c.T("AuthErr.CanNotLinkMySelf"))
c.ResponseError(c.T("link:You can't unlink yourself, you are not a member of any application"))
return
}
if len(application.Providers) == 0 {
c.ResponseError(c.T("ApplicationErr.HasNoProviders"))
c.ResponseError(c.T("link:This application has no providers"))
return
}
provider := application.GetProviderItemByType(providerType)
if provider == nil {
c.ResponseError(c.T("ApplicationErr.HasNoProvidersOfType") + providerType)
c.ResponseError(c.T("link:This application has no providers of type") + providerType)
return
}
if !provider.CanUnlink {
c.ResponseError(c.T("ProviderErr.CanNotBeUnlinked"))
c.ResponseError(c.T("link:This provider can't be unlinked"))
return
}
@ -84,7 +84,7 @@ func (c *ApiController) Unlink() {
value := object.GetUserField(&unlinkedUser, providerType)
if value == "" {
c.ResponseError(c.T("ProviderErr.LinkFirstErr"), value)
c.ResponseError(c.T("link:Please link first"), value)
return
}

View File

@ -52,7 +52,7 @@ func (c *ApiController) GetModels() {
// @Title GetModel
// @Tag Model API
// @Description get model
// @Param id query string true "The id of the model"
// @Param id query string true "The id ( owner/name ) of the model"
// @Success 200 {object} object.Model The Response object
// @router /get-model [get]
func (c *ApiController) GetModel() {
@ -66,7 +66,7 @@ func (c *ApiController) GetModel() {
// @Title UpdateModel
// @Tag Model API
// @Description update model
// @Param id query string true "The id of the model"
// @Param id query string true "The id ( owner/name ) of the model"
// @Param body body object.Model true "The details of the model"
// @Success 200 {object} controllers.Response The Response object
// @router /update-model [post]
@ -80,7 +80,7 @@ func (c *ApiController) UpdateModel() {
return
}
c.Data["json"] = wrapActionResponse(object.UpdateModel(id, &model))
c.Data["json"] = wrapErrorResponse(object.UpdateModelWithCheck(id, &model))
c.ServeJSON()
}

View File

@ -66,7 +66,7 @@ func (c *ApiController) GetOrganization() {
// @Title UpdateOrganization
// @Tag Organization API
// @Description update organization
// @Param id query string true "The id of the organization"
// @Param id query string true "The id ( owner/name ) of the organization"
// @Param body body object.Organization true "The details of the organization"
// @Success 200 {object} controllers.Response The Response object
// @router /update-organization [post]
@ -99,6 +99,12 @@ func (c *ApiController) AddOrganization() {
return
}
count := object.GetOrganizationCount("", "", "")
if err := checkQuotaForOrganization(count); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddOrganization(&organization))
c.ServeJSON()
}

View File

@ -71,7 +71,7 @@ func (c *ApiController) GetUserPayments() {
// @Title GetPayment
// @Tag Payment API
// @Description get payment
// @Param id query string true "The id of the payment"
// @Param id query string true "The id ( owner/name ) of the payment"
// @Success 200 {object} object.Payment The Response object
// @router /get-payment [get]
func (c *ApiController) GetPayment() {
@ -85,7 +85,7 @@ func (c *ApiController) GetPayment() {
// @Title UpdatePayment
// @Tag Payment API
// @Description update payment
// @Param id query string true "The id of the payment"
// @Param id query string true "The id ( owner/name ) of the payment"
// @Param body body object.Payment true "The details of the payment"
// @Success 200 {object} controllers.Response The Response object
// @router /update-payment [post]
@ -172,7 +172,7 @@ func (c *ApiController) NotifyPayment() {
// @Title InvoicePayment
// @Tag Payment API
// @Description invoice payment
// @Param id query string true "The id of the payment"
// @Param id query string true "The id ( owner/name ) of the payment"
// @Success 200 {object} controllers.Response The Response object
// @router /invoice-payment [post]
func (c *ApiController) InvoicePayment() {

View File

@ -65,11 +65,25 @@ func (c *ApiController) GetPermissionsBySubmitter() {
return
}
// GetPermissionsByRole
// @Title GetPermissionsByRole
// @Tag Permission API
// @Description get permissions by role
// @Param id query string true "The id ( owner/name ) of the role"
// @Success 200 {array} object.Permission The Response object
// @router /get-permissions-by-role [get]
func (c *ApiController) GetPermissionsByRole() {
id := c.Input().Get("id")
permissions := object.GetPermissionsByRole(id)
c.ResponseOk(permissions, len(permissions))
return
}
// GetPermission
// @Title GetPermission
// @Tag Permission API
// @Description get permission
// @Param id query string true "The id of the permission"
// @Param id query string true "The id ( owner/name ) of the permission"
// @Success 200 {object} object.Permission The Response object
// @router /get-permission [get]
func (c *ApiController) GetPermission() {
@ -83,7 +97,7 @@ func (c *ApiController) GetPermission() {
// @Title UpdatePermission
// @Tag Permission API
// @Description update permission
// @Param id query string true "The id of the permission"
// @Param id query string true "The id ( owner/name ) of the permission"
// @Param body body object.Permission true "The details of the permission"
// @Success 200 {object} controllers.Response The Response object
// @router /update-permission [post]

View File

@ -53,7 +53,7 @@ func (c *ApiController) GetProducts() {
// @Title GetProduct
// @Tag Product API
// @Description get product
// @Param id query string true "The id of the product"
// @Param id query string true "The id ( owner/name ) of the product"
// @Success 200 {object} object.Product The Response object
// @router /get-product [get]
func (c *ApiController) GetProduct() {
@ -70,7 +70,7 @@ func (c *ApiController) GetProduct() {
// @Title UpdateProduct
// @Tag Product API
// @Description update product
// @Param id query string true "The id of the product"
// @Param id query string true "The id ( owner/name ) of the product"
// @Param body body object.Product true "The details of the product"
// @Success 200 {object} controllers.Response The Response object
// @router /update-product [post]
@ -130,7 +130,7 @@ func (c *ApiController) DeleteProduct() {
// @Title BuyProduct
// @Tag Product API
// @Description buy product
// @Param id query string true "The id of the product"
// @Param id query string true "The id ( owner/name ) of the product"
// @Param providerName query string true "The name of the provider"
// @Success 200 {object} controllers.Response The Response object
// @router /buy-product [post]
@ -141,13 +141,13 @@ func (c *ApiController) BuyProduct() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("LoginErr.LoginFirst"))
c.ResponseError(c.T("general:Please login first"))
return
}
user := object.GetUser(userId)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExist"), userId))
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
return
}

View File

@ -76,12 +76,11 @@ func (c *ApiController) GetGlobalProviders() {
// @Title GetProvider
// @Tag Provider API
// @Description get provider
// @Param id query string true "The id of the provider"
// @Param id query string true "The id ( owner/name ) of the provider"
// @Success 200 {object} object.Provider The Response object
// @router /get-provider [get]
func (c *ApiController) GetProvider() {
id := c.Input().Get("id")
c.Data["json"] = object.GetMaskedProvider(object.GetProvider(id))
c.ServeJSON()
}
@ -90,7 +89,7 @@ func (c *ApiController) GetProvider() {
// @Title UpdateProvider
// @Tag Provider API
// @Description update provider
// @Param id query string true "The id of the provider"
// @Param id query string true "The id ( owner/name ) of the provider"
// @Param body body object.Provider true "The details of the provider"
// @Success 200 {object} controllers.Response The Response object
// @router /update-provider [post]
@ -123,6 +122,12 @@ func (c *ApiController) AddProvider() {
return
}
count := object.GetProviderCount("", "", "")
if err := checkQuotaForProvider(count); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddProvider(&provider))
c.ServeJSON()
}

View File

@ -40,6 +40,15 @@ func (c *ApiController) GetResources() {
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
userObj, ok := c.RequireSignedInUser()
if !ok {
return
}
if userObj.IsAdmin {
user = ""
}
if limit == "" || page == "" {
c.Data["json"] = object.GetResources(owner, user)
c.ServeJSON()
@ -145,7 +154,7 @@ func (c *ApiController) UploadResource() {
defer file.Close()
if username == "" || fullFilePath == "" {
c.ResponseError(fmt.Sprintf(c.T("ResourceErr.UsernameOrFilePathEmpty"), username, fullFilePath))
c.ResponseError(fmt.Sprintf(c.T("resource:Username or fullFilePath is empty: username = %s, fullFilePath = %s"), username, fullFilePath))
return
}
@ -156,7 +165,7 @@ func (c *ApiController) UploadResource() {
return
}
provider, user, ok := c.GetProviderFromContext("Storage")
provider, _, ok := c.GetProviderFromContext("Storage")
if !ok {
return
}
@ -171,6 +180,20 @@ func (c *ApiController) UploadResource() {
fileType, _ = util.GetOwnerAndNameFromId(mimeType)
}
if tag != "avatar" && tag != "termsOfUse" {
ext := filepath.Ext(filepath.Base(fullFilePath))
index := len(fullFilePath) - len(ext)
for i := 1; ; i++ {
_, objectKey := object.GetUploadFileUrl(provider, fullFilePath, true)
if object.GetResourceCount(owner, username, "name", objectKey) == 0 {
break
}
// duplicated fullFilePath found, change it
fullFilePath = fullFilePath[:index] + fmt.Sprintf("-%d", i) + ext
}
}
fileUrl, objectKey, err := object.UploadFileSafe(provider, fullFilePath, fileBuffer)
if err != nil {
c.ResponseError(err.Error())
@ -202,12 +225,10 @@ func (c *ApiController) UploadResource() {
switch tag {
case "avatar":
user := object.GetUserNoCheck(util.GetId(owner, username))
if user == nil {
user = object.GetUserNoCheck(username)
if user == nil {
c.ResponseError(c.T("ResourceErr.UserIsNil"))
return
}
c.ResponseError(c.T("resource:User is nil for tag: avatar"))
return
}
user.Avatar = fileUrl

View File

@ -52,7 +52,7 @@ func (c *ApiController) GetRoles() {
// @Title GetRole
// @Tag Role API
// @Description get role
// @Param id query string true "The id of the role"
// @Param id query string true "The id ( owner/name ) of the role"
// @Success 200 {object} object.Role The Response object
// @router /get-role [get]
func (c *ApiController) GetRole() {
@ -66,7 +66,7 @@ func (c *ApiController) GetRole() {
// @Title UpdateRole
// @Tag Role API
// @Description update role
// @Param id query string true "The id of the role"
// @Param id query string true "The id ( owner/name ) of the role"
// @Param body body object.Role true "The details of the role"
// @Success 200 {object} controllers.Response The Response object
// @router /update-role [post]

View File

@ -25,7 +25,7 @@ func (c *ApiController) GetSamlMeta() {
paramApp := c.Input().Get("application")
application := object.GetApplication(paramApp)
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("ApplicationErr.AppNotFound"), paramApp))
c.ResponseError(fmt.Sprintf(c.T("saml:Application %s not found"), paramApp))
return
}
metadata, _ := object.GetSamlMeta(application, host)

View File

@ -60,7 +60,7 @@ func (c *ApiController) SendEmail() {
var provider *object.Provider
if emailForm.Provider != "" {
// called by frontend's TestEmailWidget, provider name is set by frontend
provider = object.GetProvider(fmt.Sprintf("admin/%s", emailForm.Provider))
provider = object.GetProvider(util.GetId("admin", emailForm.Provider))
} else {
// called by Casdoor SDK via Client ID & Client Secret, so the used Email provider will be the application' Email provider or the default Email provider
var ok bool
@ -81,7 +81,7 @@ func (c *ApiController) SendEmail() {
}
if util.IsStrsEmpty(emailForm.Title, emailForm.Content, emailForm.Sender) {
c.ResponseError(fmt.Sprintf(c.T("EmailErr.EmptyParam"), emailForm))
c.ResponseError(fmt.Sprintf(c.T("service:Empty parameters for emailForm: %v"), emailForm))
return
}
@ -93,7 +93,7 @@ func (c *ApiController) SendEmail() {
}
if len(invalidReceivers) != 0 {
c.ResponseError(fmt.Sprintf(c.T("EmailErr.InvalidReceivers"), invalidReceivers))
c.ResponseError(fmt.Sprintf(c.T("service:Invalid Email receivers: %s"), invalidReceivers))
return
}
@ -141,7 +141,7 @@ func (c *ApiController) SendSms() {
}
if len(invalidReceivers) != 0 {
c.ResponseError(fmt.Sprintf(c.T("PhoneErr.InvalidReceivers"), invalidReceivers))
c.ResponseError(fmt.Sprintf(c.T("service:Invalid phone receivers: %s"), invalidReceivers))
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 )(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

@ -52,7 +52,7 @@ func (c *ApiController) GetSyncers() {
// @Title GetSyncer
// @Tag Syncer API
// @Description get syncer
// @Param id query string true "The id of the syncer"
// @Param id query string true "The id ( owner/name ) of the syncer"
// @Success 200 {object} object.Syncer The Response object
// @router /get-syncer [get]
func (c *ApiController) GetSyncer() {
@ -66,7 +66,7 @@ func (c *ApiController) GetSyncer() {
// @Title UpdateSyncer
// @Tag Syncer API
// @Description update syncer
// @Param id query string true "The id of the syncer"
// @Param id query string true "The id ( owner/name ) of the syncer"
// @Param body body object.Syncer true "The details of the syncer"
// @Success 200 {object} controllers.Response The Response object
// @router /update-syncer [post]

View File

@ -29,7 +29,7 @@ type SystemInfo struct {
// @Title GetSystemInfo
// @Tag System API
// @Description get user's system info
// @Param id query string true "The id of the user"
// @Param id query string true "The id ( owner/name ) of the user"
// @Success 200 {object} object.SystemInfo The Response object
// @router /get-system-info [get]
func (c *ApiController) GetSystemInfo() {
@ -40,7 +40,7 @@ func (c *ApiController) GetSystemInfo() {
user := object.GetUser(id)
if user == nil || !user.IsGlobalAdmin {
c.ResponseError(c.T("ResourceErr.NotAuthorized"))
c.ResponseError(c.T("auth:Unauthorized operation"))
return
}

View File

@ -16,7 +16,6 @@ package controllers
import (
"encoding/json"
"net/http"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
@ -55,7 +54,7 @@ func (c *ApiController) GetTokens() {
// @Title GetToken
// @Tag Token API
// @Description get token
// @Param id query string true "The id of token"
// @Param id query string true "The id ( owner/name ) of token"
// @Success 200 {object} object.Token The Response object
// @router /get-token [get]
func (c *ApiController) GetToken() {
@ -69,7 +68,7 @@ func (c *ApiController) GetToken() {
// @Title UpdateToken
// @Tag Token API
// @Description update token
// @Param id query string true "The id of token"
// @Param id query string true "The id ( owner/name ) of token"
// @Param body body object.Token true "Details of the token"
// @Success 200 {object} controllers.Response The Response object
// @router /update-token [post]
@ -129,7 +128,7 @@ func (c *ApiController) DeleteToken() {
// @Title GetOAuthCode
// @Tag Token API
// @Description get OAuth code
// @Param user_id query string true "The id of user"
// @Param id query string true "The id ( owner/name ) of user"
// @Param client_id query string true "OAuth client id"
// @Param response_type query string true "OAuth response type"
// @Param redirect_uri query string true "OAuth redirect URI"
@ -150,7 +149,7 @@ func (c *ApiController) GetOAuthCode() {
codeChallenge := c.Input().Get("code_challenge")
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
c.ResponseError(c.T("AuthErr.ChallengeMethodErr"))
c.ResponseError(c.T("auth:Challenge method should be S256"))
return
}
host := c.Ctx.Request.Host
@ -247,28 +246,6 @@ func (c *ApiController) RefreshToken() {
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 && object.CheckRedirectUriValid(application, redirectUri) {
c.Ctx.Redirect(http.StatusFound, redirectUri+"?state="+state)
return
}
c.Data["json"] = wrapActionResponse(flag)
c.ServeJSON()
}
// IntrospectToken
// @Title IntrospectToken
// @Description The introspection endpoint is an OAuth 2.0 endpoint that takes a
@ -290,7 +267,7 @@ func (c *ApiController) IntrospectToken() {
clientId = c.Input().Get("client_id")
clientSecret = c.Input().Get("client_secret")
if clientId == "" || clientSecret == "" {
c.ResponseError(c.T("TokenErr.EmptyClientID"))
c.ResponseError(c.T("token:Empty clientId or clientSecret"))
c.Data["json"] = &object.TokenError{
Error: object.InvalidRequest,
}
@ -301,7 +278,7 @@ func (c *ApiController) IntrospectToken() {
}
application := object.GetApplicationByClientId(clientId)
if application == nil || application.ClientSecret != clientSecret {
c.ResponseError(c.T("TokenErr.InvalidAppOrWrongClientSecret"))
c.ResponseError(c.T("token:Invalid application or wrong clientSecret"))
c.Data["json"] = &object.TokenError{
Error: object.InvalidClient,
}

View File

@ -80,7 +80,7 @@ func (c *ApiController) GetUsers() {
// @Title GetUser
// @Tag User API
// @Description get user
// @Param id query string true "The id of the user"
// @Param id query string true "The id ( owner/name ) of the user"
// @Param owner query string false "The owner of the user"
// @Param email query string false "The email of the user"
// @Param phone query string false "The phone of the user"
@ -129,7 +129,7 @@ func (c *ApiController) GetUser() {
// @Title UpdateUser
// @Tag User API
// @Description update user
// @Param id query string true "The id of the user"
// @Param id query string true "The id ( owner/name ) of the user"
// @Param body body object.User true "The details of the user"
// @Success 200 {object} controllers.Response The Response object
// @router /update-user [post]
@ -148,8 +148,8 @@ func (c *ApiController) UpdateUser() {
return
}
if user.DisplayName == "" {
c.ResponseError(c.T("UserErr.DisplayNameCanNotBeEmpty"))
if msg := object.CheckUpdateUser(object.GetUser(id), &user, c.GetAcceptLanguage()); msg != "" {
c.ResponseError(msg)
return
}
@ -159,6 +159,12 @@ func (c *ApiController) UpdateUser() {
}
isGlobalAdmin := c.IsGlobalAdmin()
if pass, err := checkPermissionForUpdateUser(id, user, c); !pass {
c.ResponseError(err)
return
}
affected := object.UpdateUser(id, &user, columns, isGlobalAdmin)
if affected {
object.UpdateUserToOriginalDatabase(&user)
@ -183,6 +189,12 @@ func (c *ApiController) AddUser() {
return
}
count := object.GetUserCount("", "", "")
if err := checkQuotaForUser(count); err != nil {
c.ResponseError(err.Error())
return
}
msg := object.CheckUsername(user.Name, c.GetAcceptLanguage())
if msg != "" {
c.ResponseError(msg)
@ -230,7 +242,7 @@ func (c *ApiController) GetEmailAndPhone() {
user := object.GetUserByFields(form.Organization, form.Username)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExistInOrg"), form.Organization, form.Username))
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(form.Organization, form.Username)))
return
}
@ -288,12 +300,12 @@ func (c *ApiController) SetPassword() {
}
if strings.Contains(newPassword, " ") {
c.ResponseError(c.T("SetPasswordErr.CanNotContainBlank"))
c.ResponseError(c.T("user:New password cannot contain blank space."))
return
}
if len(newPassword) <= 5 {
c.ResponseError(c.T("SetPasswordErr.LessThanSixCharacters"))
c.ResponseError(c.T("user:New password must have at least 6 characters"))
return
}

View File

@ -61,6 +61,6 @@ func (c *ApiController) UploadUsers() {
if affected {
c.ResponseOk()
} else {
c.ResponseError(c.T("UserErr.FailToImportUsers"))
c.ResponseError(c.T("user_upload:Failed to import users"))
}
}

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

@ -17,6 +17,7 @@ package controllers
import (
"fmt"
"strconv"
"strings"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
@ -56,7 +57,7 @@ func (c *ApiController) T(error string) string {
// GetAcceptLanguage ...
func (c *ApiController) GetAcceptLanguage() string {
lang := c.Ctx.Request.Header.Get("Accept-Language")
if lang == "" {
if lang == "" || !strings.Contains(conf.GetConfigString("languages"), lang[0:2]) {
lang = "en"
}
return lang[0:2]
@ -83,7 +84,7 @@ func (c *ApiController) SetTokenErrorHttpStatus() {
func (c *ApiController) RequireSignedIn() (string, bool) {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("LoginErr.LoginFirst"), "Please login first")
c.ResponseError(c.T("general:Please login first"), "Please login first")
return "", false
}
return userId, true
@ -99,7 +100,7 @@ func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
user := object.GetUser(userId)
if user == nil {
c.ClearUserSession()
c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExist"), userId))
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
return nil, false
}
return user, true
@ -118,16 +119,20 @@ func (c *ApiController) RequireAdmin() (string, bool) {
return user.Owner, true
}
func getInitScore() (int, error) {
return strconv.Atoi(conf.GetConfigString("initScore"))
func getInitScore(organization *object.Organization) (int, error) {
if organization != nil {
return organization.InitScore, nil
} else {
return strconv.Atoi(conf.GetConfigString("initScore"))
}
}
func (c *ApiController) GetProviderFromContext(category string) (*object.Provider, *object.User, bool) {
providerName := c.Input().Get("provider")
if providerName != "" {
provider := object.GetProvider(util.GetId(providerName))
provider := object.GetProvider(util.GetId("admin", providerName))
if provider == nil {
c.ResponseError(c.T("ProviderErr.ProviderNotFound"), providerName)
c.ResponseError(c.T("util:The provider: %s is not found"), providerName)
return nil, nil, false
}
return provider, nil, true
@ -140,15 +145,59 @@ func (c *ApiController) GetProviderFromContext(category string) (*object.Provide
application, user := object.GetApplicationByUserId(userId)
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("ApplicationErr.AppNotFoundForUserID"), userId))
c.ResponseError(fmt.Sprintf(c.T("util:No application is found for userId: %s"), userId))
return nil, nil, false
}
provider := application.GetProviderByCategory(category)
if provider == nil {
c.ResponseError(fmt.Sprintf(c.T("ProviderErr.ProviderNotFoundForCategory"), category, application.Name))
c.ResponseError(fmt.Sprintf(c.T("util:No provider for category: %s is found for application: %s"), category, application.Name))
return nil, nil, false
}
return provider, user, true
}
func checkQuotaForApplication(count int) error {
quota := conf.GetConfigQuota().Application
if quota == -1 {
return nil
}
if count >= quota {
return fmt.Errorf("application quota is exceeded")
}
return nil
}
func checkQuotaForOrganization(count int) error {
quota := conf.GetConfigQuota().Organization
if quota == -1 {
return nil
}
if count >= quota {
return fmt.Errorf("organization quota is exceeded")
}
return nil
}
func checkQuotaForProvider(count int) error {
quota := conf.GetConfigQuota().Provider
if quota == -1 {
return nil
}
if count >= quota {
return fmt.Errorf("provider quota is exceeded")
}
return nil
}
func checkQuotaForUser(count int) error {
quota := conf.GetConfigQuota().User
if quota == -1 {
return nil
}
if count >= quota {
return fmt.Errorf("user quota is exceeded")
}
return nil
}

View File

@ -47,26 +47,27 @@ func (c *ApiController) SendVerificationCode() {
checkKey := c.Ctx.Request.Form.Get("checkKey")
checkUser := c.Ctx.Request.Form.Get("checkUser")
applicationId := c.Ctx.Request.Form.Get("applicationId")
method := c.Ctx.Request.Form.Get("method")
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
if destType == "" {
c.ResponseError(c.T("ParameterErr.Missing") + ": type.")
c.ResponseError(c.T("general:Missing parameter") + ": type.")
return
}
if dest == "" {
c.ResponseError(c.T("ParameterErr.Missing") + ": dest.")
c.ResponseError(c.T("general:Missing parameter") + ": dest.")
return
}
if applicationId == "" {
c.ResponseError(c.T("ParameterErr.Missing") + ": applicationId.")
c.ResponseError(c.T("general:Missing parameter") + ": applicationId.")
return
}
if !strings.Contains(applicationId, "/") {
c.ResponseError(c.T("ParameterErr.Wrong") + ": applicationId.")
c.ResponseError(c.T("verification:Wrong parameter") + ": applicationId.")
return
}
if checkType == "" {
c.ResponseError(c.T("ParameterErr.Missing") + ": checkType.")
c.ResponseError(c.T("general:Missing parameter") + ": checkType.")
return
}
@ -74,7 +75,7 @@ func (c *ApiController) SendVerificationCode() {
if captchaProvider != nil {
if checkKey == "" {
c.ResponseError(c.T("ParameterErr.Missing") + ": checkKey.")
c.ResponseError(c.T("general:Missing parameter") + ": checkKey.")
return
}
isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId)
@ -84,7 +85,7 @@ func (c *ApiController) SendVerificationCode() {
}
if !isHuman {
c.ResponseError(c.T("AuthErr.NotHuman"))
c.ResponseError(c.T("verification:Turing test failed."))
return
}
}
@ -92,9 +93,13 @@ func (c *ApiController) SendVerificationCode() {
user := c.getCurrentUser()
application := object.GetApplication(applicationId)
organization := object.GetOrganization(fmt.Sprintf("%s/%s", application.Owner, application.Organization))
if organization == nil {
c.ResponseError(c.T("verification:Organization does not exist"))
return
}
if checkUser == "true" && user == nil && object.GetUserByFields(organization.Name, dest) == nil {
c.ResponseError(c.T("LoginErr.LoginFirst"))
c.ResponseError(c.T("general:Please login first"))
return
}
@ -110,7 +115,13 @@ func (c *ApiController) SendVerificationCode() {
dest = user.Email
}
if !util.IsEmailValid(dest) {
c.ResponseError(c.T("EmailErr.EmailInvalid"))
c.ResponseError(c.T("verification:Email is invalid"))
return
}
userByEmail := object.GetUserByEmail(organization.Name, dest)
if userByEmail == nil && method != "signup" && method != "reset" {
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
return
}
@ -121,11 +132,13 @@ func (c *ApiController) SendVerificationCode() {
dest = user.Phone
}
if !util.IsPhoneCnValid(dest) {
c.ResponseError(c.T("PhoneErr.NumberInvalid"))
c.ResponseError(c.T("verification:Phone number is invalid"))
return
}
if organization == nil {
c.ResponseError(c.T("OrgErr.DoNotExist"))
userByPhone := object.GetUserByPhone(organization.Name, dest)
if userByPhone == nil && method != "signup" && method != "reset" {
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
return
}
@ -157,16 +170,21 @@ func (c *ApiController) ResetEmailOrPhone() {
dest := c.Ctx.Request.Form.Get("dest")
code := c.Ctx.Request.Form.Get("code")
if len(dest) == 0 || len(code) == 0 || len(destType) == 0 {
c.ResponseError(c.T("ParameterErr.Missing"))
c.ResponseError(c.T("general:Missing parameter"))
return
}
checkDest := dest
org := object.GetOrganizationByUser(user)
organization := object.GetOrganizationByUser(user)
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 {
c.ResponseError(c.T("PhoneErr.UnableGetModifyRule"))
c.ResponseError(c.T("verification:Unable to get the phone modify rule."))
return
}
@ -176,14 +194,19 @@ func (c *ApiController) ResetEmailOrPhone() {
}
phonePrefix := "86"
if org != nil && org.PhonePrefix != "" {
phonePrefix = org.PhonePrefix
if organization != nil && organization.PhonePrefix != "" {
phonePrefix = organization.PhonePrefix
}
checkDest = fmt.Sprintf("+%s%s", phonePrefix, dest)
} 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 {
c.ResponseError(c.T("EmailErr.UnableGetModifyRule"))
c.ResponseError(c.T("verification:Unable to get the email modify rule."))
return
}
@ -205,7 +228,7 @@ func (c *ApiController) ResetEmailOrPhone() {
user.Phone = dest
object.SetUserField(user, "phone", user.Phone)
default:
c.ResponseError(c.T("ParameterErr.UnknownType"))
c.ResponseError(c.T("verification:Unknown type"))
return
}
@ -224,17 +247,17 @@ func (c *ApiController) VerifyCaptcha() {
captchaToken := c.Ctx.Request.Form.Get("captchaToken")
clientSecret := c.Ctx.Request.Form.Get("clientSecret")
if captchaToken == "" {
c.ResponseError(c.T("ParameterErr.Missing") + ": captchaToken.")
c.ResponseError(c.T("general:Missing parameter") + ": captchaToken.")
return
}
if clientSecret == "" {
c.ResponseError(c.T("ParameterErr.Missing") + ": clientSecret.")
c.ResponseError(c.T("general:Missing parameter") + ": clientSecret.")
return
}
provider := captcha.GetCaptchaProvider(captchaType)
if provider == nil {
c.ResponseError(c.T("ProviderErr.InvalidProvider"))
c.ResponseError(c.T("verification:Invalid captcha provider."))
return
}

View File

@ -35,7 +35,7 @@ func (c *ApiController) WebAuthnSignupBegin() {
webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host)
user := c.getCurrentUser()
if user == nil {
c.ResponseError(c.T("LoginErr.LoginFirst"))
c.ResponseError(c.T("general:Please login first"))
return
}
@ -66,13 +66,13 @@ func (c *ApiController) WebAuthnSignupFinish() {
webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host)
user := c.getCurrentUser()
if user == nil {
c.ResponseError(c.T("LoginErr.LoginFirst"))
c.ResponseError(c.T("general:Please login first"))
return
}
sessionObj := c.GetSession("registration")
sessionData, ok := sessionObj.(webauthn.SessionData)
if !ok {
c.ResponseError(c.T("AuthErr.CallWebAuthnSigninBegin"))
c.ResponseError(c.T("webauthn:Please call WebAuthnSigninBegin first"))
return
}
c.Ctx.Request.Body = io.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody))
@ -101,9 +101,14 @@ func (c *ApiController) WebAuthnSigninBegin() {
userName := c.Input().Get("name")
user := object.GetUserByFields(userOwner, userName)
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExistInOrg"), userOwner, userName))
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), util.GetId(userOwner, userName)))
return
}
if len(user.WebauthnCredentials) == 0 {
c.ResponseError(c.T("webauthn:Found no credentials for this user"))
return
}
options, sessionData, err := webauthnObj.BeginLogin(user)
if err != nil {
c.ResponseError(err.Error())
@ -127,7 +132,7 @@ func (c *ApiController) WebAuthnSigninFinish() {
sessionObj := c.GetSession("authentication")
sessionData, ok := sessionObj.(webauthn.SessionData)
if !ok {
c.ResponseError(c.T("AuthErr.CallWebAuthnSigninBegin"))
c.ResponseError(c.T("webauthn:Please call WebAuthnSigninBegin first"))
return
}
c.Ctx.Request.Body = io.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody))

View File

@ -52,7 +52,7 @@ func (c *ApiController) GetWebhooks() {
// @Title GetWebhook
// @Tag Webhook API
// @Description get webhook
// @Param id query string true "The id of the webhook"
// @Param id query string true "The id ( owner/name ) of the webhook"
// @Success 200 {object} object.Webhook The Response object
// @router /get-webhook [get]
func (c *ApiController) GetWebhook() {
@ -66,7 +66,7 @@ func (c *ApiController) GetWebhook() {
// @Title UpdateWebhook
// @Tag Webhook API
// @Description update webhook
// @Param id query string true "The id of the webhook"
// @Param id query string true "The id ( owner/name ) of the webhook"
// @Param body body object.Webhook true "The details of the webhook"
// @Success 200 {object} controllers.Response The Response object
// @router /update-webhook [post]

10
crowdin.yml Normal file
View File

@ -0,0 +1,10 @@
project_id: '491513'
api_token_env: 'CROWDIN_PERSONAL_TOKEN'
preserve_hierarchy: true
files: [
# JSON translation files
{
source: '/i18n/locales/en/data.json',
translation: '/i18n/locales/%two_letters_code%/data.json',
},
]

View File

@ -21,9 +21,10 @@ import (
"testing"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
func TestDeployStaticFiles(t *testing.T) {
provider := object.GetProvider("admin/provider_storage_aliyun_oss")
provider := object.GetProvider(util.GetId("admin", "provider_storage_aliyun_oss"))
deployStaticFiles(provider)
}

19
go.mod
View File

@ -4,7 +4,6 @@ go 1.16
require (
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b
github.com/Unknwon/goconfig v1.0.0
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
github.com/aws/aws-sdk-go v1.44.4
github.com/beego/beego v1.12.11
@ -12,10 +11,9 @@ require (
github.com/casbin/casbin/v2 v2.30.1
github.com/casbin/xorm-adapter/v3 v3.0.1
github.com/casdoor/go-sms-sender v0.5.1
github.com/casdoor/goth v1.69.0-FIX2
github.com/casdoor/oss v1.2.0
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/forestmgy/ldapserver v1.1.0
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df
@ -23,12 +21,14 @@ require (
github.com/go-pay/gopay v1.5.72
github.com/go-sql-driver/mysql v1.5.0
github.com/golang-jwt/jwt/v4 v4.2.0
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-cmp v0.5.8 // indirect
github.com/google/uuid v1.2.0
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/lestrrat-go/jwx v0.9.0
github.com/lestrrat-go/jwx v1.2.21
github.com/lib/pq v1.8.0
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
github.com/markbates/goth v1.75.2
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/qiangmzsx/string-adapter/v2 v2.1.0
github.com/robfig/cron/v3 v3.0.1
@ -36,22 +36,23 @@ require (
github.com/russellhaering/goxmldsig v1.1.1
github.com/satori/go.uuid v1.2.0
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stretchr/testify v1.8.0
github.com/tealeg/xlsx v1.0.5
github.com/thanhpk/randstr v1.0.4
github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.0.0-20220208233918-bba287dce954
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
gopkg.in/ini.v1 v1.67.0
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0
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/core v0.7.2
xorm.io/xorm v1.0.4
xorm.io/xorm v1.1.2
)

199
go.sum
View File

@ -20,23 +20,18 @@ cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNF
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
@ -50,7 +45,6 @@ github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZ
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
@ -60,36 +54,26 @@ github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzU
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b h1:EgJ6N2S0h1WfFIjU5/VVHWbMSVYXAluop97Qxpr/lfQ=
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b/go.mod h1:3SAoF0F5EbcOuBD5WT9nYkbIJieBS84cUQXADbXeBsU=
github.com/Unknwon/goconfig v1.0.0 h1:9IAu/BYbSLQi8puFjUQApZTxIHqSwrj5d8vpP8vTq4A=
github.com/Unknwon/goconfig v1.0.0/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4 h1:Hs82Z41s6SdL1CELW+XaDYmOH4hkBN4/N9og/AsOv7E=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387 h1:loy0fjI90vF44BPW4ZYOkE3tDkGTy7yHURusOJimt+I=
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387/go.mod h1:GuR5j/NW7AU7tDAQUDGCtpiPxWIOy/c3kiRDnlwiCHc=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1075 h1:Z0SzZttfYI/raZ5O9WF3cezZJTSW4Yz4Kow9uWdyRwg=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1075/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible h1:9gWa46nstkJ9miBReJcN8Gq34cBFbzSpQZVVT9N09TM=
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.44.4 h1:ePN0CVJMdiz2vYUcJH96eyxRrtKGSDMgyhP6rah2OgE=
github.com/aws/aws-sdk-go v1.44.4/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
@ -97,9 +81,7 @@ github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
github.com/beego/beego v1.12.11 h1:MWKcnpavb7iAIS0m6uuEq6pHKkYvGNw/5umIUKqL7jM=
github.com/beego/beego v1.12.11/go.mod h1:QURFL1HldOcCZAxnc1cZ7wrplsYR5dKPHFjmk6WkLAs=
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd h1:jZtX5jh5IOMu0fpOTC3ayh6QGSPJ/KWOv1lgPvbRw1M=
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542 h1:nYXb+3jF6Oq/j8R/y90XrKpreCxIalBWfeyeKymgOPk=
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
@ -107,7 +89,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737 h1:rRISKWyXfVxvoa702s91Zl5oREZTrR3yv+tXrrX7G/g=
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
github.com/casbin/casbin v1.7.0 h1:PuzlE8w0JBg/DhIqnkF1Dewf3z+qmUZMVN07PonvVUQ=
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
@ -119,110 +100,86 @@ github.com/casbin/xorm-adapter/v3 v3.0.1 h1:0l0zkYxo6cNuIdrBZgFxlje1TRvmheYa/zIp
github.com/casbin/xorm-adapter/v3 v3.0.1/go.mod h1:1BL7rHEDXrxO+vQdSo/ZaWKRivXl7YTos67GdMYcd20=
github.com/casdoor/go-sms-sender v0.5.1 h1:1/Wp1OLkVAVY4lEGQhekSNetSAWhnPcxYPV7xpCZgC0=
github.com/casdoor/go-sms-sender v0.5.1/go.mod h1:kBykbqwgRDXbXdMAIxmZKinVM1WjdqEbej5LAbUbcfI=
github.com/casdoor/goth v1.69.0-FIX2 h1:RgfIMkL9kekylgxHHK2ZY8ASAwOGns2HVlaBwLu7Bcs=
github.com/casdoor/goth v1.69.0-FIX2/go.mod h1:Om55nRo8CkeDkPSNBbzXW4G5uI28ZUkSk5S69dPek3s=
github.com/casdoor/oss v1.2.0 h1:ozLAE+nnNdFQBWbzH8U9spzaO8h8NrB57lBcdyMUUQ8=
github.com/casdoor/oss v1.2.0/go.mod h1:qii35VBuxnR/uEuYSKpS0aJ8htQFOcCVsZ4FHgHLuss=
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 h1:Puu1hUwfps3+1CUzYdAZXijuvLuRMirgiXdf3zsM2Ig=
github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/couchbase/go-couchbase v0.0.0-20201216133707-c04035124b17 h1:1ZELwRDUvpBpmgKSIUP6VMW1jIehzD0sCdWxRyejegw=
github.com/couchbase/go-couchbase v0.0.0-20201216133707-c04035124b17/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A=
github.com/couchbase/gomemcached v0.1.2-0.20201224031647-c432ccf49f32 h1:xnKbM9umdDcpWfEsJzVqRf5PGnIMbiZj2OmDYbleQjM=
github.com/couchbase/gomemcached v0.1.2-0.20201224031647-c432ccf49f32/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo=
github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401 h1:4KDlx3vjalrHD/EfsjCpV91HNX3JPaIqRtt83zZ7x+Y=
github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 h1:Lgdd/Qp96Qj8jqLpq2cI1I1X7BJnu06efS+XkhRoLUQ=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
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/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
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/go.mod h1:EYSpSkwoEcryMmQGfhol2IiB3IMN9IIIaNd/wcAQMGQ=
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8=
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/elastic/go-elasticsearch/v6 v6.8.5 h1:U2HtkBseC1FNBmDr0TR2tKltL6FxoY+niDAlj5M8TK8=
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/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/forestmgy/ldapserver v1.1.0 h1:gvil4nuLhqPEL8SugCkFhRyA0/lIvRdwZSqlrw63ll4=
github.com/forestmgy/ldapserver v1.1.0/go.mod h1:1RZ8lox1QSY7rmbjdmy+sYQXY4Lp7SpGzpdE3+j3IyM=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fxamacker/cbor/v2 v2.2.0 h1:6eXqdDDe588rSYAi1HfZKbx6YYQO4mxQ9eC6xYpU/JQ=
github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c h1:iRTj5SRYwbvsygdwVp+y9kZT145Y1s6xOPpeOEIeGc4=
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df h1:Bao6dhmbTA1KFVxmJ6nBoMuOJit2yjEgLJpIMYpop0E=
github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-pay/gopay v1.5.72 h1:3zm64xMBhJBa8rXbm//q5UiGgOa4WO5XYEnU394N2Zw=
github.com/go-pay/gopay v1.5.72/go.mod h1:0qOGIJuFW7PKDOjmecwKyW0mgsVImgwB9yPJj0ilpn8=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.8.0 h1:1kAa0fCrnpv+QYdkdcRzrRM7AyYs5o8+jZdJCz9xj6k=
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
github.com/go-redis/redis v6.14.2+incompatible h1:UE9pLhzmWf+xHNmZsoccjXosPicuiNaInPgym8nzfg0=
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/goccy/go-json v0.9.6 h1:5/4CtRQdtsX0sal8fdVhTaiMN01Ri8BExZZ8iRmHQ6E=
github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d h1:lBXNCxVENCipq4D1Is42JVOP4eQjlB8TQ6H69Yx5J9Q=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
@ -231,11 +188,9 @@ github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
@ -262,12 +217,12 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
@ -279,13 +234,11 @@ 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.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.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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -294,17 +247,13 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7 h1:k+KkMRk8mGOu1xG38StS7dQ+Z6oW1i9n3dgrAVU9Q/E=
github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
@ -312,9 +261,7 @@ github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.1.1 h1:YMDmfaK68mUixINzY/XjscuJ47uXFWSSHzFbBQM0PrE=
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@ -322,9 +269,7 @@ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+l
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk=
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko=
github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
@ -338,39 +283,43 @@ github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9q
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.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.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.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+9HbQbYf7g=
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/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
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/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
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 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6 h1:wxyqOzKxsRJ6vVRL9sXQ64Z45wmBuQ+OTH9sLsC5rKc=
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lestrrat-go/jwx v0.9.0 h1:Fnd0EWzTm0kFrBPzE/PEPp9nzllES5buMkksPMjEKpM=
github.com/lestrrat-go/jwx v0.9.0/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/YNA/UnBk=
github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4=
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
github.com/lestrrat-go/httpcc v1.0.0 h1:FszVC6cKfDvBKcJv646+lkh4GydQg2Z29scgUfkOpYc=
github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=
github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A=
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
github.com/lestrrat-go/jwx v1.2.21 h1:n+yG95UMm5ZFsDdvsZmui+bqat4Cj/di4ys6XbgSlE8=
github.com/lestrrat-go/jwx v1.2.21/go.mod h1:9cfxnOH7G1gN75CaJP2hKGcxFEx5sPh1abRIA/ZJVh4=
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg=
@ -379,12 +328,17 @@ github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 h1:wIONC+HMNRqmWBjuM
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3/go.mod h1:37YR9jabpiIxsb8X9VCIx8qFOjTDIIrIHHODa8C4gz0=
github.com/markbates/going v1.0.0 h1:DQw0ZP7NbNlFGcKbcE/IVSOAFzScxRtLpd0rLMzLhq0=
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
github.com/markbates/goth v1.75.2 h1:C7KloBMMk50JyXaHhzfqWYLW6+bDcSVIvUGHXneLWro=
github.com/markbates/goth v1.75.2/go.mod h1:X6xdNgpapSENS0O35iTBBcMHoJDQDfI9bJl+APCkYMc=
github.com/mattermost/xml-roundtrip-validator v0.0.0-20201208211235-fe770d50d911 h1:erppMjjp69Rertg1zlgRbLJH1u+eCmRPxKjMZ5I8/Ro=
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/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.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/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
@ -399,9 +353,7 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA=
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 h1:F9x/1yl3T2AeKLr2AMdilSD8+f9bvMnNN8VS5iDtovc=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
@ -412,11 +364,8 @@ github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/pelletier/go-toml v1.0.1 h1:0nx4vKBl23+hEaCOV1mFhKS9vhhBtFYWC7rQY0vJAyE=
github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233 h1:jmJndGFBPjNWW+MAYarU/Nl8QrQVzbw4B/AYE0LzETo=
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -442,12 +391,11 @@ github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFB
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/qiangmzsx/string-adapter/v2 v2.1.0 h1:q0y8TPa/sTwtriJPRe8gWL++PuZ+XbOUuvKU+hvtTYs=
github.com/qiangmzsx/string-adapter/v2 v2.1.0/go.mod h1:PElPB7b7HnGKTsuADAffFpOQXHqjEGJz1+U1a6yR5wA=
github.com/qiniu/dyn v1.3.0 h1:s+xPTeV0H8yikgM4ZMBc7Rrefam8UNI3asBlkaOQg5o=
github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdkk=
github.com/qiniu/go-sdk/v7 v7.12.1 h1:FZG5dhs2MZBV/mHVhmHnsgsQ+j1gSE0RqIoA2WwEDwY=
github.com/qiniu/go-sdk/v7 v7.12.1/go.mod h1:btsaOc8CA3hdVloULfFdDgDc+g4f3TDZEFsDY0BLE+w=
github.com/qiniu/x v1.10.5 h1:7V/CYWEmo9axJULvrJN6sMYh2FdY+esN5h8jwDkA4b0=
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/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -465,27 +413,18 @@ github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0 h1:QIF48X1cihydXibm+4wfAc0r/qyPyuFiPFRNphdMpEE=
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400 h1:091wFNQB3PXcL5+me0joH7EiyqQaI0wGMpEjVCkK04U=
github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d h1:NVwnfyR3rENtlz62bcrkXME3INVUa4lcdGt+opvxExs=
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec h1:q6XVwXmKvCRHRqesF3cSv6lNqqHi0QWOvgDlSohg8UA=
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@ -511,11 +450,9 @@ github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/twilio/twilio-go v0.26.0 h1:wFW4oTe3/LKt6bvByP7eio8JsjtaLHjMQKOUEzQry7U=
github.com/twilio/twilio-go v0.26.0/go.mod h1:lz62Hopu4vicpQ056H5TJ0JE4AP0rS3sQ35/ejmgOwE=
github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83 h1:9AUN7+NK4IV+A11igqjQM5i8obiOAQo4SXgjaxe+orI=
github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/volcengine/volc-sdk-golang v1.0.19 h1:jJp+aJgK0e//rZ9I0K2Y7ufJwvuZRo/AQsYDynXMNgA=
github.com/volcengine/volc-sdk-golang v1.0.19/go.mod h1:+GGi447k4p1I5PNdbpG2GLaF0Ui9vIInTojMM0IfSS4=
github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec h1:bua919NvciYmjqfeZMsVkXTny1QvXMrri0X6NlqILRs=
github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
@ -523,19 +460,15 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5 h1:dPmz1Snjq0kmkz159iL7S6WzdahUTHnHB5M56WFVifs=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973 h1:iCnkJ/qjKZGdZnlcj1N55AxPDan814kpc3s1cDpQKd8=
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -552,8 +485,9 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220208233918-bba287dce954 h1:BkypuErRT9A9I/iljuaG3/zdMjd/J6m8tKKJQtGfSdA=
golang.org/x/crypto v0.0.0-20220208233918-bba287dce954/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -563,10 +497,8 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -577,10 +509,8 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
@ -622,7 +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-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-20200930145003-4acb6c075d10/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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@ -645,8 +575,8 @@ 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-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-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-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -670,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-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-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-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -684,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-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-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-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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -697,7 +631,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbuf
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -755,6 +688,7 @@ 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-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-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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -778,7 +712,6 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.32.0 h1:Le77IccnTqEa8ryp9wIpX5W3zYm7Gf9LhOp9PHcwFts=
google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -818,7 +751,6 @@ google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200929141702-51c3e5b607fe h1:6SgESkjJknFUnsfQ2yxQbmTAi37BxhwS/riq+VdLo9c=
google.golang.org/genproto v0.0.0-20200929141702-51c3e5b607fe/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@ -833,7 +765,6 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@ -846,7 +777,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
@ -856,7 +786,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
@ -865,7 +794,6 @@ gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkp
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
@ -888,18 +816,43 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
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.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE=
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/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI=
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/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
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.4 h1:UBXA4I3NhiyjXfPqxXUkS2t5hMta9SSPATeMMaZg9oA=
xorm.io/xorm v1.0.4/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
xorm.io/xorm v1.1.2 h1:bje+1KZvK3m5AHtZNfUDlKEEyuw/IRHT+an0CLIG5TU=
xorm.io/xorm v1.1.2/go.mod h1:Cb0DKYTHbyECMaSfgRnIZp5aiUgQozxcJJ0vzcLGJSg=

View File

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

View File

@ -1,122 +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 (
"log"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/Unknwon/goconfig"
"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.Split(match[1], ",")
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]bool {
ErrName := make(map[string]bool)
for i := 0; i < len(paths); i++ {
content := util.ReadStringFromPath(paths[i])
words := GetAllI18nStrings(content, paths[i])
for i := 0; i < len(words); i++ {
ErrName[words[i]] = true
}
}
return ErrName
}
func writeToAllLanguageFiles(errName map[string]bool) {
languages := "en,zh,es,fr,de,ja,ko,ru"
languageArr := strings.Split(languages, ",")
var c [10]*goconfig.ConfigFile
for i := 0; i < len(languageArr); i++ {
var err error
c[i], err = goconfig.LoadConfigFile("../i18n/languages/" + "locale_" + languageArr[i] + ".ini")
if err != nil {
log.Println(err.Error())
}
for j := range errName {
parts := strings.Split(j, ".")
_, err := c[i].GetValue(parts[0], parts[1])
if err != nil {
c[i].SetValue(parts[0], parts[1], parts[1])
}
}
c[i].SetPrettyFormat(true)
err = goconfig.SaveConfigFile(c[i], "../i18n/languages/"+"locale_"+languageArr[i]+".ini")
if err != nil {
log.Println(err)
}
}
}

View File

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

View File

@ -1,137 +0,0 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -1,137 +0,0 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
WrongPasswordManyTimes = You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again
Unauthorized = Unauthorized operation
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -1,137 +0,0 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -1,137 +0,0 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -1,137 +0,0 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -1,137 +0,0 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -1,137 +0,0 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -1,137 +0,0 @@
[ApplicationErr]
AppNotFound = 应用 %%s 未找到
AppNotFoundForUserID = 找不到该用户的应用程序 %s
GrantTypeNotSupport = 此应用中不支持此授权类型
HasNoProviders = 该应用无提供商
HasNoProvidersOfType = 应用没有该类型的提供商
InvalidID = 无效的Application ID
[AuthErr]
AuthStateWrong = 期望状态位: %s, 实际状态为: %s
ChallengeMethodErr = Challenge 方法应该为 S256
CanNotUnlinkUsers = 您不是全局管理员,无法取消链接其他用户
CanNotLinkMySelf = 您无法取消链接,您不是任何应用程序的成员
CallWebAuthnSigninBegin = 请先调用WebAuthnSigninBegin
NotHuman = 真人验证失败
Unauthorized = 未授权的操作
WrongPasswordManyTimes = 输入密码错误次数已达上限,请在 %d 分 %d 秒后重试
[CasErr]
ServiceDoNotMatch = 服务 %s 与 %s 不匹配
[EmailErr]
ExistedErr = 该邮箱已存在
EmptyErr = 邮箱不可为空
EmailInvalid = 无效邮箱
EmailCheckResult = Email: %s
EmptyParam = 邮件参数为空: %v
InvalidReceivers = 无效的邮箱接收者: %%s
UnableGetModifyRule = 无法得到Email修改规则
[EnforcerErr]
SignInFirst = 请先登录
[InitErr]
InitScoreFailed = 初始化分数失败: %w
[LdapErr]
MultipleAccounts = 多个帐户具有相同的uid请检查您的 ldap 服务器
PasswordWrong = Ldap密码错误
ServerExisted = Ldap服务器已存在
[LoginErr]
AppDoNotExist = 应用不存在: %s
AppNotEnableSignUp = 提供商账户: %s 与用户名: %s (%s) 不存在且 不允许注册新账户, 请联系IT支持
AccountDoNotExist = 账户不存在
InvalidUserInformation = 创建用户失败,用户信息无效: %%s
LoginFirst = 请先登录
LoginFail = 无法登录: %s
NoPermission = 您没有权限执行此操作
OldUser = 提供商账户: %s 与用户名: %s (%s) 已经与其他账户绑定: %s (%s)
ProviderCanNotSignUp = 提供商账户: %s 与用户名: %s (%s) 不存在且 不允许通过 %s 注册新账户, 请使用其他方式注册
SignOutFirst = 请在登录前登出
SessionOutdated = Session已过期请重新登陆
UserDoNotExist = 用户不存在: %s/%s
UserIsForbidden = 该用户被禁止登陆,请联系管理员
UnknownAuthentication = 未知的认证类型 (非密码或提供商认证), form = %s
UnsupportedPasswordType = 不支持此密码类型
[OrgErr]
DoNotExist = 组织不存在
Immutable = %s是不可变的
OnlyAdmin = 只有管理员用户有此权限
UnknownModifyRule = 未知的修改规则
[ParameterErr]
Missing = 参数丢失
OrgMissingErr = Organization参数丢失
UnknownType = 未知类型
Wrong = 参数错误
[PhoneErr]
CodeNotSent = 验证码还未发送
CodeTimeOut = 验证码过期
ExistedErr = 该电话已存在
EmptyErr = 电话不可为空
InvalidReceivers = 无效的电话接收者: %s
NumberInvalid = 无效电话
PhoneCheckResult = 电话: %s
UnableGetModifyRule = 无法得到电话修改规则
NoPrefix = %s 无此电话前缀
[ProviderErr]
CanNotBeUnlinked = 该提供商不可被链接
InvalidProvider = 无效的验证码提供商
LinkFirstErr = 请先绑定
ProviderNotEnabled = 提供商: %s 未被启用
ProviderNotSupported = 不支持该类型的提供商: %s
ProviderNotFound = 该提供商未找到: %s
ProviderNotFoundForCategory = 该类型的提供商: %s 在应用中未找到: %s
DoNotExist = 提供商: %s 不存在
CategoryNotSAML = 提供商 %s类型不是SAML
[ResourceErr]
NotAuthorized = 您无权获取此资源
UserIsNil = 用户头像标签为空
UsernameOrFilePathEmpty = username或FilePath为空: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = 新密码不可以包含空客
LessThanSixCharacters = 新密码至少为6位
[SignUpErr]
DoNotAllowSignUp = 该应用不允许注册新账户
SignOutFirst = 请在登陆前登出
[TokenErr]
EmptyClientID = clientId或clientSecret为空
InvalidAppOrWrongClientSecret = 无效应用或错误的clientSecret
InvalidToken = 无效token
InvalidClientId = 无效的ClientId
RedirectURIDoNotExist = 重定向 URI%s 在可列表中未找到
[UserErr]
AffiliationBlankErr = 联系方式不可为空
DisplayNameBlankErr = 展示名称不可为空
DisplayNameInvalid = 展示名称无效
DisplayNameCanNotBeEmpty = 展示名称不可为空
DoNotExist = 用户不存在: %s
DoNotExistInOrg = 用户不存在: %s/%s
FirstNameBlankErr = 名不可以为空
FailToImportUsers = 导入用户失败
LastNameBlankErr = 姓不可以为空
NameLessThanTwoCharacters = 用户名至少要有2个字符
NameStartWithADigitErr = 用户名禁止使用数字作为第一个字符
NameIsEmailErr = 用户名不可以是邮箱地址
NameCantainWhitSpaceErr = 用户名不可以包含空格
NameExistedErr = 用户名已存在
NameEmptyErr = 用户名不可为空
NameTooLang = 用户名过长最大长度为39个字符
NameFormatErr = 用户名只能包含字母数字字符、下划线或连字符,不能有连续的连字符或下划线,也不能以连字符或下划线开头或结尾
PasswordLessThanSixCharacters = 密码至少为6字符
DoNotExistSignUp = 用户不存在,请先注册
InvalidInformation = 无效信息
[StorageErr]
ObjectKeyNotAllowed = object key :%s 不被允许

146
i18n/locales/de/data.json Normal file
View File

@ -0,0 +1,146 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"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"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LastName cannot be blank": "LastName cannot be blank",
"Ldap user name or password incorrect": "Ldap user name or password incorrect",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"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!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

146
i18n/locales/en/data.json Normal file
View File

@ -0,0 +1,146 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"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"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LastName cannot be blank": "LastName cannot be blank",
"Ldap user name or password incorrect": "Ldap user name or password incorrect",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"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!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

146
i18n/locales/es/data.json Normal file
View File

@ -0,0 +1,146 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"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"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LastName cannot be blank": "LastName cannot be blank",
"Ldap user name or password incorrect": "Ldap user name or password incorrect",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"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!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

146
i18n/locales/fr/data.json Normal file
View File

@ -0,0 +1,146 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"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"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LastName cannot be blank": "LastName cannot be blank",
"Ldap user name or password incorrect": "Ldap user name or password incorrect",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"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!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

146
i18n/locales/ja/data.json Normal file
View File

@ -0,0 +1,146 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"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"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LastName cannot be blank": "LastName cannot be blank",
"Ldap user name or password incorrect": "Ldap user name or password incorrect",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"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!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

146
i18n/locales/ko/data.json Normal file
View File

@ -0,0 +1,146 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"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"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LastName cannot be blank": "LastName cannot be blank",
"Ldap user name or password incorrect": "Ldap user name or password incorrect",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"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!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

146
i18n/locales/ru/data.json Normal file
View File

@ -0,0 +1,146 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "Invalid token",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"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"
},
"cas": {
"Service %s and %s do not match": "Service %s and %s do not match"
},
"check": {
"Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "Email already exists",
"Email cannot be empty": "Email cannot be empty",
"Email is invalid": "Email is invalid",
"Empty username.": "Empty username.",
"FirstName cannot be blank": "FirstName cannot be blank",
"LastName cannot be blank": "LastName cannot be blank",
"Ldap user name or password incorrect": "Ldap user name or password incorrect",
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "Organization does not exist",
"Password must have at least 6 characters": "Password must have at least 6 characters",
"Phone already exists": "Phone already exists",
"Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "Phone number is invalid",
"Session outdated, please login again": "Session outdated, please login again",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "unsupported password type: %s"
},
"general": {
"Missing parameter": "Missing parameter",
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"ldap": {
"Ldap server exist": "Ldap server exist"
},
"link": {
"Please link first": "Please link first",
"This application has no providers": "This application has no providers",
"This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
},
"organization": {
"Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s."
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"resource": {
"User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
},
"saml": {
"Application %s not found": "Application %s not found"
},
"saml_sp": {
"provider %s's category is not SAML": "provider %s's category is not SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
},
"storage": {
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "The provider type: %s is not supported"
},
"token": {
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters"
},
"user_upload": {
"Failed to import users": "Failed to import users"
},
"util": {
"No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "The provider: %s is not found"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"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!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},
"webauthn": {
"Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
}
}

146
i18n/locales/zh/data.json Normal file
View File

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

View File

@ -17,27 +17,26 @@ package i18n
import (
"embed"
"fmt"
"log"
"strings"
"github.com/casdoor/casdoor/util"
"gopkg.in/ini.v1"
)
//go:embed languages/*.ini
//go:embed locales/*/data.json
var f embed.FS
var (
langMapConfig = make(map[string]*ini.File)
isNotFirstLoad = make(map[string]bool)
)
var langMap = make(map[string]map[string]map[string]string) // for example : langMap[en][account][Invalid information] = Invalid information
func getI18nFilePath(language string) string {
return fmt.Sprintf("../web/src/locales/%s/data.json", language)
func getI18nFilePath(category string, language string) string {
if category == "backend" {
return fmt.Sprintf("../i18n/locales/%s/data.json", language)
} else {
return fmt.Sprintf("../web/src/locales/%s/data.json", language)
}
}
func readI18nFile(language string) *I18nData {
s := util.ReadStringFromPath(getI18nFilePath(language))
func readI18nFile(category string, language string) *I18nData {
s := util.ReadStringFromPath(getI18nFilePath(category, language))
data := &I18nData{}
err := util.JsonToStruct(s, data)
@ -47,13 +46,13 @@ func readI18nFile(language string) *I18nData {
return data
}
func writeI18nFile(language string, data *I18nData) {
func writeI18nFile(category string, language string, data *I18nData) {
s := util.StructToJsonFormatted(data)
s = strings.ReplaceAll(s, "\\u0026", "&")
s += "\n"
println(s)
util.WriteStringToPath(s, getI18nFilePath(language))
util.WriteStringToPath(s, getI18nFilePath(category, language))
}
func applyData(data1 *I18nData, data2 *I18nData) {
@ -75,18 +74,20 @@ func applyData(data1 *I18nData, data2 *I18nData) {
}
func Translate(lang string, error string) string {
parts := strings.Split(error, ".")
if !strings.Contains(error, ".") || len(parts) != 2 {
log.Println("Invalid Error Name")
return ""
parts := strings.SplitN(error, ":", 2)
if !strings.Contains(error, ":") || len(parts) != 2 {
return "Translate Error: " + error
}
if isNotFirstLoad[lang] {
return langMapConfig[lang].Section(parts[0]).Key(parts[1]).String()
if langMap[lang] != nil {
return langMap[lang][parts[0]][parts[1]]
} else {
file, _ := f.ReadFile("languages/locale_" + lang + ".ini")
langMapConfig[lang], _ = ini.Load(file)
isNotFirstLoad[lang] = true
return langMapConfig[lang].Section(parts[0]).Key(parts[1]).String()
file, _ := f.ReadFile("locales/" + lang + "/data.json")
data := I18nData{}
err := util.JsonToStruct(string(file), &data)
if err != nil {
panic(err)
}
langMap[lang] = data
return langMap[lang][parts[0]][parts[1]]
}
}

View File

@ -15,11 +15,11 @@
package idp
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"time"
@ -118,13 +118,14 @@ func (idp *AdfsIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
if err != nil {
return nil, err
}
keyset, err := jwk.Parse(resp.Body)
body, err := ioutil.ReadAll(resp.Body)
keyset, err := jwk.ParseKey(body)
if err != nil {
return nil, err
}
tokenSrc := []byte(token.AccessToken)
publicKey, _ := keyset.Keys[0].Materialize()
idToken, _ := jwt.Parse(bytes.NewReader(tokenSrc), jwt.WithVerify(jwa.RS256, publicKey))
publicKey, _ := keyset.PublicKey()
idToken, _ := jwt.Parse(tokenSrc, jwt.WithVerify(jwa.RS256, publicKey))
sid, _ := idToken.Get("sid")
upn, _ := idToken.Get("upn")
name, _ := idToken.Get("unique_name")

View File

@ -256,6 +256,8 @@ func (idp *DingTalkIdProvider) isUserInOrg(unionId string) (bool, error) {
}
if data.ErrCode == 60121 {
return false, fmt.Errorf("the user is not found in the organization where clientId and clientSecret belong")
} else if data.ErrCode != 0 {
return false, fmt.Errorf(data.ErrMessage)
}
return true, nil
}

View File

@ -22,35 +22,64 @@ import (
"time"
"github.com/casdoor/casdoor/util"
"github.com/casdoor/goth"
"github.com/casdoor/goth/providers/amazon"
"github.com/casdoor/goth/providers/apple"
"github.com/casdoor/goth/providers/azuread"
"github.com/casdoor/goth/providers/bitbucket"
"github.com/casdoor/goth/providers/digitalocean"
"github.com/casdoor/goth/providers/discord"
"github.com/casdoor/goth/providers/dropbox"
"github.com/casdoor/goth/providers/facebook"
"github.com/casdoor/goth/providers/gitea"
"github.com/casdoor/goth/providers/github"
"github.com/casdoor/goth/providers/gitlab"
"github.com/casdoor/goth/providers/google"
"github.com/casdoor/goth/providers/heroku"
"github.com/casdoor/goth/providers/instagram"
"github.com/casdoor/goth/providers/kakao"
"github.com/casdoor/goth/providers/line"
"github.com/casdoor/goth/providers/linkedin"
"github.com/casdoor/goth/providers/microsoftonline"
"github.com/casdoor/goth/providers/paypal"
"github.com/casdoor/goth/providers/salesforce"
"github.com/casdoor/goth/providers/shopify"
"github.com/casdoor/goth/providers/slack"
"github.com/casdoor/goth/providers/steam"
"github.com/casdoor/goth/providers/tumblr"
"github.com/casdoor/goth/providers/twitter"
"github.com/casdoor/goth/providers/yahoo"
"github.com/casdoor/goth/providers/yandex"
"github.com/casdoor/goth/providers/zoom"
"github.com/markbates/goth"
"github.com/markbates/goth/providers/amazon"
"github.com/markbates/goth/providers/apple"
"github.com/markbates/goth/providers/auth0"
"github.com/markbates/goth/providers/azureadv2"
"github.com/markbates/goth/providers/battlenet"
"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/discord"
"github.com/markbates/goth/providers/dropbox"
"github.com/markbates/goth/providers/eveonline"
"github.com/markbates/goth/providers/facebook"
"github.com/markbates/goth/providers/fitbit"
"github.com/markbates/goth/providers/gitea"
"github.com/markbates/goth/providers/github"
"github.com/markbates/goth/providers/gitlab"
"github.com/markbates/goth/providers/google"
"github.com/markbates/goth/providers/heroku"
"github.com/markbates/goth/providers/influxcloud"
"github.com/markbates/goth/providers/instagram"
"github.com/markbates/goth/providers/intercom"
"github.com/markbates/goth/providers/kakao"
"github.com/markbates/goth/providers/lastfm"
"github.com/markbates/goth/providers/line"
"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/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/salesforce"
"github.com/markbates/goth/providers/shopify"
"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/strava"
"github.com/markbates/goth/providers/stripe"
"github.com/markbates/goth/providers/tiktok"
"github.com/markbates/goth/providers/tumblr"
"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/yammer"
"github.com/markbates/goth/providers/yandex"
"github.com/markbates/goth/providers/zoom"
"golang.org/x/oauth2"
)
@ -74,14 +103,44 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
}
case "AzureAD":
idp = GothIdProvider{
Provider: azuread.New(clientId, clientSecret, redirectUrl, nil),
Session: &azuread.Session{},
Provider: azureadv2.New(clientId, clientSecret, redirectUrl, azureadv2.ProviderOptions{Tenant: "common"}),
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":
idp = GothIdProvider{
Provider: bitbucket.New(clientId, clientSecret, redirectUrl),
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":
idp = GothIdProvider{
Provider: digitalocean.New(clientId, clientSecret, redirectUrl),
@ -97,6 +156,16 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: dropbox.New(clientId, clientSecret, redirectUrl),
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":
idp = GothIdProvider{
Provider: facebook.New(clientId, clientSecret, redirectUrl),
@ -127,16 +196,31 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: heroku.New(clientId, clientSecret, redirectUrl),
Session: &heroku.Session{},
}
case "InfluxCloud":
idp = GothIdProvider{
Provider: influxcloud.New(clientId, clientSecret, redirectUrl),
Session: &influxcloud.Session{},
}
case "Instagram":
idp = GothIdProvider{
Provider: instagram.New(clientId, clientSecret, redirectUrl),
Session: &instagram.Session{},
}
case "Intercom":
idp = GothIdProvider{
Provider: intercom.New(clientId, clientSecret, redirectUrl),
Session: &intercom.Session{},
}
case "Kakao":
idp = GothIdProvider{
Provider: kakao.New(clientId, clientSecret, redirectUrl),
Session: &kakao.Session{},
}
case "Lastfm":
idp = GothIdProvider{
Provider: lastfm.New(clientId, clientSecret, redirectUrl),
Session: &lastfm.Session{},
}
case "Linkedin":
idp = GothIdProvider{
Provider: linkedin.New(clientId, clientSecret, redirectUrl),
@ -147,11 +231,46 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: line.New(clientId, clientSecret, redirectUrl),
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":
idp = GothIdProvider{
Provider: microsoftonline.New(clientId, clientSecret, redirectUrl),
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":
idp = GothIdProvider{
Provider: paypal.New(clientId, clientSecret, redirectUrl),
@ -172,26 +291,81 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: slack.New(clientId, clientSecret, redirectUrl),
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":
idp = GothIdProvider{
Provider: steam.New(clientSecret, redirectUrl),
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":
idp = GothIdProvider{
Provider: tumblr.New(clientId, clientSecret, redirectUrl),
Session: &tumblr.Session{},
}
case "Twitch":
idp = GothIdProvider{
Provider: twitch.New(clientId, clientSecret, redirectUrl),
Session: &twitch.Session{},
}
case "Twitter":
idp = GothIdProvider{
Provider: twitter.New(clientId, clientSecret, redirectUrl),
Session: &twitter.Session{},
Provider: twitterv2.New(clientId, clientSecret, redirectUrl),
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":
idp = GothIdProvider{
Provider: yahoo.New(clientId, clientSecret, redirectUrl),
Session: &yahoo.Session{},
}
case "Yammer":
idp = GothIdProvider{
Provider: yammer.New(clientId, clientSecret, redirectUrl),
Session: &yammer.Session{},
}
case "Yandex":
idp = GothIdProvider{
Provider: yandex.New(clientId, clientSecret, redirectUrl),
@ -202,6 +376,8 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: zoom.New(clientId, clientSecret, redirectUrl),
Session: &zoom.Session{},
}
default:
return nil
}
return &idp
@ -230,6 +406,9 @@ func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) {
// to call the function to obtain accessToken
value = url.Values{}
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)
if err != nil {

View File

@ -98,7 +98,61 @@ func GetIdProvider(typ string, subType string, clientId string, clientSecret str
return nil
}
var gothList = []string{"Apple", "AzureAd", "Slack", "Steam"}
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 {
for _, value := range gothList {

View File

@ -156,5 +156,187 @@
"autoSync": 0,
"lastSync": ""
}
],
"models": [
{
"owner": "",
"name": "",
"modelText": "",
"displayName": ""
}
],
"permissions": [
{
"actions": [
""
],
"displayName": "",
"effect": "",
"isEnabled": true,
"model": "",
"name": "",
"owner": "",
"resourceType": "",
"resources": [
""
],
"roles": [
""
],
"users": [
""
]
}
],
"payments": [
{
"currency": "",
"detail": "",
"displayName": "",
"invoiceRemark": "",
"invoiceTaxId": "",
"invoiceTitle": "",
"invoiceType": "",
"invoiceUrl": "",
"message": "",
"name": "",
"organization": "",
"owner": "",
"payUrl": "",
"personEmail": "",
"personIdCard": "",
"personName": "",
"personPhone": "",
"price": 0,
"productDisplayName": "",
"productName": "",
"provider": "",
"returnUrl": "",
"state": "",
"tag": "",
"type": "",
"user": ""
}
],
"products": [
{
"currency": "",
"detail": "",
"displayName": "",
"image": "",
"name": "",
"owner": "",
"price": 0,
"providers": [
""
],
"quantity": 0,
"returnUrl": "",
"sold": 0,
"state": "",
"tag": ""
}
],
"resources": [
{
"owner": "",
"name": "",
"user": "",
"provider": "",
"application": "",
"tag": "",
"parent": "",
"fileName": "",
"fileType": "",
"fileFormat": "",
"url": "",
"description": ""
}
],
"roles": [
{
"displayName": "",
"isEnabled": true,
"name": "",
"owner": "",
"roles": [
""
],
"users": [
""
]
}
],
"syncers": [
{
"affiliationTable": "",
"avatarBaseUrl": "",
"database": "",
"databaseType": "",
"errorText": "",
"host": "",
"isEnabled": true,
"name": "",
"organization": "",
"owner": "",
"password": "",
"port": 0,
"syncInterval": 0,
"table": "",
"tableColumns": [
{
"casdoorName": "",
"isHashed": true,
"name": "",
"type": "",
"values": [
""
]
}
],
"tablePrimaryKey": "",
"type": "",
"user": ""
}
],
"tokens": [
{
"accessToken": "",
"application": "",
"code": "",
"codeChallenge": "",
"codeExpireIn": 0,
"codeIsUsed": true,
"createdTime": "",
"expiresIn": 0,
"name": "",
"organization": "",
"owner": "",
"refreshToken": "",
"scope": "",
"tokenType": "",
"user": ""
}
],
"webhooks": [
{
"contentType": "",
"events": [
""
],
"headers": [
{
"name": "",
"value": ""
}
],
"isEnabled": true,
"isUserExtended": true,
"method": "",
"name": "",
"organization": "",
"owner": "",
"url": ""
}
]
}

View File

@ -25,8 +25,7 @@ import (
_ "github.com/denisenkom/go-mssqldb" // db = mssql
_ "github.com/go-sql-driver/mysql" // db = mysql
_ "github.com/lib/pq" // db = postgres
"xorm.io/xorm/migrate"
//_ "github.com/mattn/go-sqlite3" // db = sqlite3
_ "modernc.org/sqlite" // db = sqlite
"xorm.io/core"
"xorm.io/xorm"
)
@ -42,7 +41,7 @@ func InitConfig() {
beego.BConfig.WebConfig.Session.SessionOn = true
InitAdapter(true)
initMigrations()
MigrateDatabase()
}
func InitAdapter(createDatabase bool) {
@ -222,6 +221,11 @@ func (a *Adapter) createTable() {
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(Session))
if err != nil {
panic(err)
}
}
func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
@ -247,22 +251,3 @@ func GetSession(owner string, offset, limit int, field, value, sortField, sortOr
}
return session
}
func initMigrations() {
migrations := []*migrate.Migration{
{
ID: "20221015CasbinRule--fill ptype field with p",
Migrate: func(tx *xorm.Engine) error {
_, err := tx.Cols("ptype").Update(&xormadapter.CasbinRule{
Ptype: "p",
})
return err
},
Rollback: func(tx *xorm.Engine) error {
return tx.DropTables(&xormadapter.CasbinRule{})
},
},
}
m := migrate.New(adapter.Engine, migrate.DefaultOptions, migrations)
m.Migrate()
}

View File

@ -16,7 +16,6 @@ package object
import (
"fmt"
"net/url"
"regexp"
"strings"
@ -51,28 +50,31 @@ type Application struct {
EnableCodeSignin bool `json:"enableCodeSignin"`
EnableSamlCompress bool `json:"enableSamlCompress"`
EnableWebAuthn bool `json:"enableWebAuthn"`
EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
ExpireInHours int `json:"expireInHours"`
RefreshExpireInHours int `json:"refreshExpireInHours"`
SignupUrl string `xorm:"varchar(200)" json:"signupUrl"`
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"`
TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"`
SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
FormCss string `xorm:"text" json:"formCss"`
FormOffset int `json:"formOffset"`
FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"`
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
ExpireInHours int `json:"expireInHours"`
RefreshExpireInHours int `json:"refreshExpireInHours"`
SignupUrl string `xorm:"varchar(200)" json:"signupUrl"`
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"`
TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"`
SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
ThemeData *ThemeData `xorm:"json" json:"themeData"`
FormCss string `xorm:"text" json:"formCss"`
FormOffset int `json:"formOffset"`
FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"`
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
}
func GetApplicationCount(owner, field, value string) int {
@ -85,6 +87,16 @@ func GetApplicationCount(owner, field, value string) int {
return int(count)
}
func GetOrganizationApplicationCount(owner, Organization, field, value string) int {
session := GetSession(owner, -1, -1, field, value, "", "")
count, err := session.Count(&Application{Organization: Organization})
if err != nil {
panic(err)
}
return int(count)
}
func GetApplications(owner string) []*Application {
applications := []*Application{}
err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner})
@ -95,8 +107,18 @@ func GetApplications(owner string) []*Application {
return applications
}
func GetPaginationApplications(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Application {
func GetOrganizationApplications(owner string, organization string) []*Application {
applications := []*Application{}
err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner, Organization: organization})
if err != nil {
panic(err)
}
return applications
}
func GetPaginationApplications(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Application {
var applications []*Application
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
err := session.Find(&applications)
if err != nil {
@ -106,9 +128,10 @@ func GetPaginationApplications(owner string, offset, limit int, field, value, so
return applications
}
func GetApplicationsByOrganizationName(owner string, organization string) []*Application {
func GetPaginationOrganizationApplications(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) []*Application {
applications := []*Application{}
err := adapter.Engine.Desc("created_time").Find(&applications, &Application{Owner: owner, Organization: organization})
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
err := session.Find(&applications, &Application{Owner: owner, Organization: organization})
if err != nil {
panic(err)
}
@ -132,7 +155,7 @@ func getProviderMap(owner string) map[string]*Provider {
}
func extendApplicationWithProviders(application *Application) {
m := getProviderMap(application.Owner)
m := getProviderMap(application.Organization)
for _, providerItem := range application.Providers {
if provider, ok := m[providerItem.Name]; ok {
providerItem.Provider = provider
@ -265,7 +288,8 @@ func GetMaskedApplications(applications []*Application, userId string) []*Applic
func UpdateApplication(id string, application *Application) bool {
owner, name := util.GetOwnerAndNameFromId(id)
if getApplication(owner, name) == nil {
oldApplication := getApplication(owner, name)
if oldApplication == nil {
return false
}
@ -280,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 {
providerItem.Provider = nil
}
@ -303,6 +331,9 @@ func AddApplication(application *Application) bool {
if application.ClientSecret == "" {
application.ClientSecret = util.GenerateClientSecret()
}
if GetApplicationByClientId(application.ClientId) != nil {
return false
}
for _, providerItem := range application.Providers {
providerItem.Provider = nil
}
@ -332,56 +363,30 @@ func (application *Application) GetId() string {
return fmt.Sprintf("%s/%s", application.Owner, application.Name)
}
func CheckRedirectUriValid(application *Application, redirectUri string) bool {
validUri := false
for _, tmpUri := range application.RedirectUris {
tmpUriRegex := regexp.MustCompile(tmpUri)
if tmpUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, tmpUri) {
validUri = true
func (application *Application) IsRedirectUriValid(redirectUri string) bool {
isValid := false
for _, targetUri := range application.RedirectUris {
targetUriRegex := regexp.MustCompile(targetUri)
if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) {
isValid = true
break
}
}
return validUri
return isValid
}
func IsAllowOrigin(origin string) bool {
allowOrigin := false
originUrl, err := url.Parse(origin)
if err != nil {
return false
}
rows, err := adapter.Engine.Cols("redirect_uris").Rows(&Application{})
if err != nil {
panic(err)
}
application := Application{}
for rows.Next() {
err := rows.Scan(&application)
if err != nil {
panic(err)
}
for _, tmpRedirectUri := range application.RedirectUris {
u1, err := url.Parse(tmpRedirectUri)
if err != nil {
continue
}
if u1.Scheme == originUrl.Scheme && u1.Host == originUrl.Host {
allowOrigin = true
break
}
}
if allowOrigin {
break
func IsOriginAllowed(origin string) bool {
applications := GetApplications("")
for _, application := range applications {
if application.IsRedirectUriValid(origin) {
return true
}
}
return allowOrigin
return false
}
func getApplicationMap(organization string) map[string]*Application {
applications := GetApplicationsByOrganizationName("admin", organization)
applications := GetOrganizationApplications("admin", organization)
applicationMap := make(map[string]*Application)
for _, application := range applications {

View File

@ -15,7 +15,7 @@
package object
func (application *Application) GetProviderByCategory(category string) *Provider {
providers := GetProviders(application.Owner)
providers := GetProviders(application.Organization)
m := map[string]*Provider{}
for _, provider := range providers {
if provider.Category != category {

View File

@ -60,7 +60,7 @@ func getPermanentAvatarUrl(organization string, username string, url string, upl
}
fullFilePath := fmt.Sprintf("/avatar/%s/%s.png", organization, username)
uploadedFileUrl, _ := getUploadFileUrl(defaultStorageProvider, fullFilePath, false)
uploadedFileUrl, _ := GetUploadFileUrl(defaultStorageProvider, fullFilePath, false)
if upload {
DownloadAndUpload(url, fullFilePath)

View File

@ -148,34 +148,7 @@ func (casbinAdapter *CasbinAdapter) getTable() string {
}
}
func safeReturn(policy []string, i int) string {
if len(policy) > i {
return policy[i]
} else {
return ""
}
}
func matrixToCasbinRules(pType string, policies [][]string) []*xormadapter.CasbinRule {
res := []*xormadapter.CasbinRule{}
for _, policy := range policies {
line := xormadapter.CasbinRule{
Ptype: pType,
V0: safeReturn(policy, 0),
V1: safeReturn(policy, 1),
V2: safeReturn(policy, 2),
V3: safeReturn(policy, 3),
V4: safeReturn(policy, 4),
V5: safeReturn(policy, 5),
}
res = append(res, &line)
}
return res
}
func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule {
func initEnforcer(modelObj *Model, casbinAdapter *CasbinAdapter) (*casbin.Enforcer, error) {
// init Adapter
if casbinAdapter.Adapter == nil {
var dataSourceName string
@ -191,20 +164,60 @@ func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule {
dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.")
}
casbinAdapter.Adapter, _ = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(casbinAdapter.DatabaseType, dataSourceName, casbinAdapter.Database).Engine, casbinAdapter.getTable(), "")
var err error
casbinAdapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(casbinAdapter.DatabaseType, dataSourceName, casbinAdapter.Database).Engine, casbinAdapter.getTable(), "")
if err != nil {
return nil, err
}
}
// init Model
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
m, err := model.NewModelFromString(modelObj.ModelText)
if err != nil {
panic(err)
return nil, err
}
// init Enforcer
enforcer, err := casbin.NewEnforcer(m, casbinAdapter.Adapter)
if err != nil {
panic(err)
return nil, err
}
return enforcer, nil
}
func safeReturn(policy []string, i int) string {
if len(policy) > i {
return policy[i]
} else {
return ""
}
}
func matrixToCasbinRules(Ptype string, policies [][]string) []*xormadapter.CasbinRule {
res := []*xormadapter.CasbinRule{}
for _, policy := range policies {
line := xormadapter.CasbinRule{
Ptype: Ptype,
V0: safeReturn(policy, 0),
V1: safeReturn(policy, 1),
V2: safeReturn(policy, 2),
V3: safeReturn(policy, 3),
V4: safeReturn(policy, 4),
V5: safeReturn(policy, 5),
}
res = append(res, &line)
}
return res
}
func SyncPolicies(casbinAdapter *CasbinAdapter) ([]*xormadapter.CasbinRule, error) {
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
enforcer, err := initEnforcer(modelObj, casbinAdapter)
if err != nil {
return nil, err
}
policies := matrixToCasbinRules("p", enforcer.GetPolicy())
@ -212,5 +225,48 @@ func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule {
policies = append(policies, matrixToCasbinRules("g", enforcer.GetGroupingPolicy())...)
}
return policies
return policies, nil
}
func UpdatePolicy(oldPolicy, newPolicy []string, casbinAdapter *CasbinAdapter) (bool, error) {
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
enforcer, err := initEnforcer(modelObj, casbinAdapter)
if err != nil {
return false, err
}
affected, err := enforcer.UpdatePolicy(oldPolicy, newPolicy)
if err != nil {
return affected, err
}
return affected, nil
}
func AddPolicy(policy []string, casbinAdapter *CasbinAdapter) (bool, error) {
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
enforcer, err := initEnforcer(modelObj, casbinAdapter)
if err != nil {
return false, err
}
affected, err := enforcer.AddPolicy(policy)
if err != nil {
return affected, err
}
return affected, nil
}
func RemovePolicy(policy []string, casbinAdapter *CasbinAdapter) (bool, error) {
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
enforcer, err := initEnforcer(modelObj, casbinAdapter)
if err != nil {
return false, err
}
affected, err := enforcer.RemovePolicy(policy)
if err != nil {
return affected, err
}
return affected, nil
}

View File

@ -44,87 +44,87 @@ func init() {
func CheckUserSignup(application *Application, organization *Organization, username string, password string, displayName string, firstName string, lastName string, email string, phone string, affiliation string, lang string) string {
if organization == nil {
return i18n.Translate(lang, "OrgErr.DoNotExist")
return i18n.Translate(lang, "check:Organization does not exist")
}
if application.IsSignupItemVisible("Username") {
if len(username) <= 1 {
return i18n.Translate(lang, "UserErr.NameLessThanTwoCharacters")
return i18n.Translate(lang, "check:Username must have at least 2 characters")
}
if unicode.IsDigit(rune(username[0])) {
return i18n.Translate(lang, "UserErr.NameStartWithADigitErr")
return i18n.Translate(lang, "check:Username cannot start with a digit")
}
if util.IsEmailValid(username) {
return i18n.Translate(lang, "UserErr.NameIsEmailErr")
return i18n.Translate(lang, "check:Username cannot be an email address")
}
if reWhiteSpace.MatchString(username) {
return i18n.Translate(lang, "UserErr.NameCantainWhitSpaceErr")
return i18n.Translate(lang, "check:Username cannot contain white spaces")
}
msg := CheckUsername(username, lang)
if msg != "" {
if msg := CheckUsername(username, lang); msg != "" {
return msg
}
if HasUserByField(organization.Name, "name", username) {
return i18n.Translate(lang, "UserErr.NameExistedErr")
return i18n.Translate(lang, "check:Username already exists")
}
if HasUserByField(organization.Name, "email", email) {
return i18n.Translate(lang, "EmailErr.ExistedErr")
return i18n.Translate(lang, "check:Email already exists")
}
if HasUserByField(organization.Name, "phone", phone) {
return i18n.Translate(lang, "PhoneErr.ExistedErr")
return i18n.Translate(lang, "check:Phone already exists")
}
}
if len(password) <= 5 {
return i18n.Translate(lang, "UserErr.PasswordLessThanSixCharacters")
return i18n.Translate(lang, "check:Password must have at least 6 characters")
}
if application.IsSignupItemVisible("Email") {
if email == "" {
if application.IsSignupItemRequired("Email") {
return i18n.Translate(lang, "EmailErr.EmptyErr")
return i18n.Translate(lang, "check:Email cannot be empty")
} else {
return ""
}
}
if HasUserByField(organization.Name, "email", email) {
return i18n.Translate(lang, "EmailErr.ExistedErr")
return i18n.Translate(lang, "check:Email already exists")
} else if !util.IsEmailValid(email) {
return i18n.Translate(lang, "EmailErr.EmailInvalid")
return i18n.Translate(lang, "check:Email is invalid")
}
}
if application.IsSignupItemVisible("Phone") {
if phone == "" {
if application.IsSignupItemRequired("Phone") {
return i18n.Translate(lang, "PhoneErr.EmptyErr")
return i18n.Translate(lang, "check:Phone cannot be empty")
} else {
return ""
}
}
if HasUserByField(organization.Name, "phone", phone) {
return i18n.Translate(lang, "PhoneErr.ExistedErr")
return i18n.Translate(lang, "check:Phone already exists")
} else if organization.PhonePrefix == "86" && !util.IsPhoneCnValid(phone) {
return i18n.Translate(lang, "PhoneErr.NumberInvalid")
return i18n.Translate(lang, "check:Phone number is invalid")
}
}
if application.IsSignupItemVisible("Display name") {
if application.GetSignupItemRule("Display name") == "First, last" && (firstName != "" || lastName != "") {
if firstName == "" {
return i18n.Translate(lang, "UserErr.FirstNameBlankErr")
return i18n.Translate(lang, "check:FirstName cannot be blank")
} else if lastName == "" {
return i18n.Translate(lang, "UserErr.LastNameBlankErr")
return i18n.Translate(lang, "check:LastName cannot be blank")
}
} else {
if displayName == "" {
return i18n.Translate(lang, "UserErr.DisplayNameBlankErr")
return i18n.Translate(lang, "check:DisplayName cannot be blank")
} else if application.GetSignupItemRule("Display name") == "Real name" {
if !isValidRealName(displayName) {
return i18n.Translate(lang, "UserErr.DisplayNameInvalid")
return i18n.Translate(lang, "check:DisplayName is not valid real name")
}
}
}
@ -132,7 +132,7 @@ func CheckUserSignup(application *Application, organization *Organization, usern
if application.IsSignupItemVisible("Affiliation") {
if affiliation == "" {
return i18n.Translate(lang, "UserErr.AffiliationBlankErr")
return i18n.Translate(lang, "check:Affiliation cannot be blank")
}
}
@ -143,11 +143,11 @@ func checkSigninErrorTimes(user *User, lang string) string {
if user.SigninWrongTimes >= SigninWrongTimesLimit {
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
seconds := int(LastSignWrongTimeDuration.Seconds() - passedTime.Seconds())
minutes := int(LastSignWrongTimeDuration.Minutes() - passedTime.Minutes())
// deny the login if the error times is greater than the limit and the last login time is less than the duration
if seconds > 0 {
return fmt.Sprintf(i18n.Translate(lang, "AuthErr.WrongPasswordManyTimes"), seconds/60, seconds%60)
if minutes > 0 {
return fmt.Sprintf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), minutes)
}
// reset the error times
@ -167,7 +167,7 @@ func CheckPassword(user *User, password string, lang string) string {
organization := GetOrganizationByUser(user)
if organization == nil {
return i18n.Translate(lang, "OrgErr.DoNotExist")
return i18n.Translate(lang, "check:Organization does not exist")
}
credManager := cred.GetCredManager(organization.PasswordType)
@ -184,9 +184,9 @@ func CheckPassword(user *User, password string, lang string) string {
return ""
}
return recordSigninErrorInfo(user)
return recordSigninErrorInfo(user, lang)
} else {
return fmt.Sprintf(i18n.Translate(lang, "LoginErr.UnsupportedPasswordType"), organization.PasswordType)
return fmt.Sprintf(i18n.Translate(lang, "check:unsupported password type: %s"), organization.PasswordType)
}
}
@ -210,7 +210,7 @@ func checkLdapUserPassword(user *User, password string, lang string) (*User, str
if len(searchResult.Entries) == 0 {
continue
} else if len(searchResult.Entries) > 1 {
return nil, i18n.Translate(lang, "LdapErr.MultipleAccounts")
return nil, i18n.Translate(lang, "check:Multiple accounts with same uid, please check your ldap server")
}
dn := searchResult.Entries[0].DN
@ -221,7 +221,7 @@ func checkLdapUserPassword(user *User, password string, lang string) (*User, str
}
if !ldapLoginSuccess {
return nil, i18n.Translate(lang, "LdapErr.PasswordWrong")
return nil, i18n.Translate(lang, "check:Ldap user name or password incorrect")
}
return user, ""
}
@ -229,11 +229,11 @@ func checkLdapUserPassword(user *User, password string, lang string) (*User, str
func CheckUserPassword(organization string, username string, password string, lang string) (*User, string) {
user := GetUserByFields(organization, username)
if user == nil || user.IsDeleted == true {
return nil, i18n.Translate(lang, "UserErr.DoNotExistSignUp")
return nil, fmt.Sprintf(i18n.Translate(lang, "general:The user: %s doesn't exist"), util.GetId(organization, username))
}
if user.IsForbidden {
return nil, i18n.Translate(lang, "LoginErr.UserIsForbidden")
return nil, i18n.Translate(lang, "check:The user is forbidden to sign in, please contact the administrator")
}
if user.Ldap != "" {
@ -254,13 +254,13 @@ func filterField(field string) bool {
func CheckUserPermission(requestUserId, userId, userOwner string, strict bool, lang string) (bool, error) {
if requestUserId == "" {
return false, fmt.Errorf(i18n.Translate(lang, "LoginErr.LoginFirst"))
return false, fmt.Errorf(i18n.Translate(lang, "general:Please login first"))
}
if userId != "" {
targetUser := GetUser(userId)
if targetUser == nil {
return false, fmt.Errorf(i18n.Translate(lang, "UserErr.DoNotExist"), userId)
return false, fmt.Errorf(i18n.Translate(lang, "general:The user: %s doesn't exist"), userId)
}
userOwner = targetUser.Owner
@ -272,7 +272,7 @@ func CheckUserPermission(requestUserId, userId, userOwner string, strict bool, l
} else {
requestUser := GetUser(requestUserId)
if requestUser == nil {
return false, fmt.Errorf(i18n.Translate(lang, "LoginErr.SessionOutdated"))
return false, fmt.Errorf(i18n.Translate(lang, "check:Session outdated, please login again"))
}
if requestUser.IsGlobalAdmin {
hasPermission = true
@ -287,7 +287,7 @@ func CheckUserPermission(requestUserId, userId, userOwner string, strict bool, l
}
}
return hasPermission, fmt.Errorf(i18n.Translate(lang, "LoginErr.NoPermission"))
return hasPermission, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
}
func CheckAccessPermission(userId string, application *Application) (bool, error) {
@ -313,8 +313,9 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
return true, err
}
enforcer := getEnforcer(permission)
allowed, err = enforcer.Enforce(userId, application.Name, "read")
break
if allowed, err = enforcer.Enforce(userId, application.Name, "read"); allowed {
return allowed, err
}
}
}
return allowed, err
@ -322,9 +323,9 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
func CheckUsername(username string, lang string) string {
if username == "" {
return i18n.Translate(lang, "UserErr.NameEmptyErr")
return i18n.Translate(lang, "check:Empty username.")
} else if len(username) > 39 {
return i18n.Translate(lang, "UserErr.NameTooLang")
return i18n.Translate(lang, "check:Username is too long (maximum is 39 characters).")
}
exclude, _ := regexp.Compile("^[\u0021-\u007E]+$")
@ -335,7 +336,34 @@ func CheckUsername(username string, lang string) string {
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
re, _ := regexp.Compile("^[a-zA-Z0-9]+((?:-[a-zA-Z0-9]+)|(?:_[a-zA-Z0-9]+))*$")
if !re.MatchString(username) {
return i18n.Translate(lang, "UserErr.NameFormatErr")
return i18n.Translate(lang, "check: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.")
}
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 ""

View File

@ -18,6 +18,8 @@ import (
"fmt"
"regexp"
"time"
"github.com/casdoor/casdoor/i18n"
)
var reRealName *regexp.Regexp
@ -43,7 +45,7 @@ func resetUserSigninErrorTimes(user *User) {
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin)
}
func recordSigninErrorInfo(user *User) string {
func recordSigninErrorInfo(user *User, lang string) string {
// increase failed login count
user.SigninWrongTimes++
@ -56,9 +58,9 @@ func recordSigninErrorInfo(user *User) string {
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin)
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
if leftChances > 0 {
return fmt.Sprintf("password is incorrect, you have %d remaining chances", leftChances)
return fmt.Sprintf(i18n.Translate(lang, "check:password or code is incorrect, you have %d remaining chances"), leftChances)
}
// don't show the chance error message if the user has no chance left
return fmt.Sprintf("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

@ -25,8 +25,6 @@ import (
)
func InitDb() {
MigratePermissionRule()
existed := initBuiltInOrganization()
if !existed {
initBuiltInModel()
@ -36,6 +34,8 @@ func InitDb() {
initBuiltInApplication()
initBuiltInCert()
initBuiltInLdap()
} else {
MigrateDatabase()
}
initWebAuthn()
@ -58,6 +58,8 @@ func initBuiltInOrganization() bool {
PhonePrefix: "86",
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
Tags: []string{},
Languages: []string{"en", "zh", "es", "fr", "de", "ja", "ko", "ru"},
InitScore: 2000,
AccountItems: []*AccountItem{
{Name: "Organization", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "ID", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
@ -157,7 +159,7 @@ func initBuiltInApplication() {
},
RedirectUris: []string{},
ExpireInHours: 168,
FormOffset: 8,
FormOffset: 2,
}
AddApplication(application)
}
@ -221,7 +223,7 @@ func initBuiltInLdap() {
}
func initBuiltInProvider() {
provider := GetProvider("admin/provider_captcha_default")
provider := GetProvider(util.GetId("admin", "provider_captcha_default"))
if provider != nil {
return
}

View File

@ -23,6 +23,15 @@ type InitData struct {
Certs []*Cert `json:"certs"`
Providers []*Provider `json:"providers"`
Ldaps []*Ldap `json:"ldaps"`
Models []*Model `json:"models"`
Permissions []*Permission `json:"permissions"`
Payments []*Payment `json:"payments"`
Products []*Product `json:"products"`
Resources []*Resource `json:"resources"`
Roles []*Role `json:"roles"`
Syncers []*Syncer `json:"syncers"`
Tokens []*Token `json:"tokens"`
Webhooks []*Webhook `json:"webhooks"`
}
func InitFromFile() {
@ -46,6 +55,33 @@ func InitFromFile() {
for _, ldap := range initData.Ldaps {
initDefinedLdap(ldap)
}
for _, model := range initData.Models {
initDefinedModel(model)
}
for _, permission := range initData.Permissions {
initDefinedPermission(permission)
}
for _, payment := range initData.Payments {
initDefinedPayment(payment)
}
for _, product := range initData.Products {
initDefinedProduct(product)
}
for _, resource := range initData.Resources {
initDefinedResource(resource)
}
for _, role := range initData.Roles {
initDefinedRole(role)
}
for _, syncer := range initData.Syncers {
initDefinedSyncer(syncer)
}
for _, token := range initData.Tokens {
initDefinedToken(token)
}
for _, webhook := range initData.Webhooks {
initDefinedWebhook(webhook)
}
}
}
@ -63,6 +99,15 @@ func readInitDataFromFile(filePath string) *InitData {
Certs: []*Cert{},
Providers: []*Provider{},
Ldaps: []*Ldap{},
Models: []*Model{},
Permissions: []*Permission{},
Payments: []*Payment{},
Products: []*Product{},
Resources: []*Resource{},
Roles: []*Role{},
Syncers: []*Syncer{},
Tokens: []*Token{},
Webhooks: []*Webhook{},
}
err := util.JsonToStruct(s, data)
if err != nil {
@ -89,6 +134,41 @@ func readInitDataFromFile(filePath string) *InitData {
application.RedirectUris = []string{}
}
}
for _, permission := range data.Permissions {
if permission.Actions == nil {
permission.Actions = []string{}
}
if permission.Resources == nil {
permission.Resources = []string{}
}
if permission.Roles == nil {
permission.Roles = []string{}
}
if permission.Users == nil {
permission.Users = []string{}
}
}
for _, role := range data.Roles {
if role.Roles == nil {
role.Roles = []string{}
}
if role.Users == nil {
role.Users = []string{}
}
}
for _, syncer := range data.Syncers {
if syncer.TableColumns == nil {
syncer.TableColumns = []*TableColumn{}
}
}
for _, webhook := range data.Webhooks {
if webhook.Events == nil {
webhook.Events = []string{}
}
if webhook.Headers == nil {
webhook.Headers = []*Header{}
}
}
return data
}
@ -168,9 +248,90 @@ func initDefinedLdap(ldap *Ldap) {
}
func initDefinedProvider(provider *Provider) {
existed := GetProvider(provider.GetId())
existed := GetProvider(util.GetId("admin", provider.Name))
if existed != nil {
return
}
AddProvider(provider)
}
func initDefinedModel(model *Model) {
existed := GetModel(model.GetId())
if existed != nil {
return
}
model.CreatedTime = util.GetCurrentTime()
AddModel(model)
}
func initDefinedPermission(permission *Permission) {
existed := GetPermission(permission.GetId())
if existed != nil {
return
}
permission.CreatedTime = util.GetCurrentTime()
AddPermission(permission)
}
func initDefinedPayment(payment *Payment) {
existed := GetPayment(payment.GetId())
if existed != nil {
return
}
payment.CreatedTime = util.GetCurrentTime()
AddPayment(payment)
}
func initDefinedProduct(product *Product) {
existed := GetProduct(product.GetId())
if existed != nil {
return
}
product.CreatedTime = util.GetCurrentTime()
AddProduct(product)
}
func initDefinedResource(resource *Resource) {
existed := GetResource(resource.GetId())
if existed != nil {
return
}
resource.CreatedTime = util.GetCurrentTime()
AddResource(resource)
}
func initDefinedRole(role *Role) {
existed := GetRole(role.GetId())
if existed != nil {
return
}
role.CreatedTime = util.GetCurrentTime()
AddRole(role)
}
func initDefinedSyncer(syncer *Syncer) {
existed := GetSyncer(syncer.GetId())
if existed != nil {
return
}
syncer.CreatedTime = util.GetCurrentTime()
AddSyncer(syncer)
}
func initDefinedToken(token *Token) {
existed := GetToken(token.GetId())
if existed != nil {
return
}
token.CreatedTime = util.GetCurrentTime()
AddToken(token)
}
func initDefinedWebhook(webhook *Webhook) {
existed := GetWebhook(webhook.GetId())
if existed != nil {
return
}
webhook.CreatedTime = util.GetCurrentTime()
AddWebhook(webhook)
}

85
object/migrate.go Normal file
View File

@ -0,0 +1,85 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.package object
package object
import (
"strings"
xormadapter "github.com/casbin/xorm-adapter/v3"
"xorm.io/xorm"
"xorm.io/xorm/migrate"
)
func MigrateDatabase() {
migrations := []*migrate.Migration{
MigrateCasbinRule(),
MigratePermissionRule(),
}
m := migrate.New(adapter.Engine, migrate.DefaultOptions, migrations)
m.Migrate()
}
func MigrateCasbinRule() *migrate.Migration {
migration := migrate.Migration{
ID: "20221015CasbinRule--fill ptype field with p",
Migrate: func(engine *xorm.Engine) error {
_, err := engine.Cols("ptype").Update(&xormadapter.CasbinRule{
Ptype: "p",
})
return err
},
Rollback: func(engine *xorm.Engine) error {
return engine.DropTables(&xormadapter.CasbinRule{})
},
}
return &migration
}
func MigratePermissionRule() *migrate.Migration {
migration := migrate.Migration{
ID: "20230209MigratePermissionRule--Use V5 instead of V1 to store permissionID",
Migrate: func(engine *xorm.Engine) error {
models := []*Model{}
err := engine.Find(&models, &Model{})
if err != nil {
panic(err)
}
isHit := false
for _, model := range models {
if strings.Contains(model.ModelText, "permission") {
// update model table
model.ModelText = strings.Replace(model.ModelText, "permission,", "", -1)
UpdateModel(model.GetId(), model)
isHit = true
}
}
if isHit {
// update permission_rule table
sql := "UPDATE `permission_rule`SET V0 = V1, V1 = V2, V2 = V3, V3 = V4, V4 = V5 WHERE V0 IN (SELECT CONCAT(owner, '/', name) AS permission_id FROM `permission`)"
_, err = engine.Exec(sql)
if err != nil {
return err
}
}
return err
},
}
return &migration
}

View File

@ -68,14 +68,14 @@ func getModel(owner string, name string) *Model {
return nil
}
model := Model{Owner: owner, Name: name}
existed, err := adapter.Engine.Get(&model)
m := Model{Owner: owner, Name: name}
existed, err := adapter.Engine.Get(&m)
if err != nil {
panic(err)
}
if existed {
return &model
return &m
} else {
return nil
}
@ -86,6 +86,17 @@ func GetModel(id string) *Model {
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 {
owner, name := util.GetOwnerAndNameFromId(id)
if getModel(owner, name) == nil {
@ -98,11 +109,6 @@ func UpdateModel(id string, modelObj *Model) bool {
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)
if err != nil {
@ -152,3 +158,7 @@ func modelChangeTrigger(oldName string, newName string) error {
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"`
RequestParameterSupported bool `json:"request_parameter_supported"`
RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"`
EndSessionEndpoint string `json:"end_session_endpoint"`
}
func getOriginFromHost(host string) (string, string) {
@ -76,7 +77,7 @@ func GetOidcDiscovery(host string) OidcDiscovery {
JwksUri: fmt.Sprintf("%s/.well-known/jwks", originBackend),
IntrospectionEndpoint: fmt.Sprintf("%s/api/login/oauth/introspect", originBackend),
ResponseTypesSupported: []string{"code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token", "none"},
ResponseModesSupported: []string{"login", "code", "link"},
ResponseModesSupported: []string{"query", "fragment", "login", "code", "link"},
GrantTypesSupported: []string{"password", "authorization_code"},
SubjectTypesSupported: []string{"public"},
IdTokenSigningAlgValuesSupported: []string{"RS256"},
@ -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"},
RequestParameterSupported: true,
RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"},
EndSessionEndpoint: fmt.Sprintf("%s/api/logout", originBackend),
}
return oidcDiscovery

View File

@ -31,23 +31,34 @@ type AccountItem struct {
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 {
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"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
Favicon string `xorm:"varchar(100)" json:"favicon"`
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"`
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
Tags []string `xorm:"mediumtext" json:"tags"`
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
EnableSoftDeletion bool `json:"enableSoftDeletion"`
IsProfilePublic bool `json:"isProfilePublic"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
Favicon string `xorm:"varchar(100)" json:"favicon"`
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
PhonePrefix string `xorm:"varchar(10)" json:"phonePrefix"`
DefaultAvatar string `xorm:"varchar(100)" json:"defaultAvatar"`
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
Tags []string `xorm:"mediumtext" json:"tags"`
Languages []string `xorm:"varchar(255)" json:"languages"`
ThemeData *ThemeData `xorm:"json" json:"themeData"`
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
InitScore int `json:"initScore"`
EnableSoftDeletion bool `json:"enableSoftDeletion"`
IsProfilePublic bool `json:"isProfilePublic"`
AccountItems []*AccountItem `xorm:"varchar(3000)" json:"accountItems"`
}
@ -202,15 +213,15 @@ func GetAccountItemByName(name string, organization *Organization) *AccountItem
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User, lang string) (bool, string) {
switch accountItem.ModifyRule {
case "Admin":
if !(user.IsAdmin || user.IsGlobalAdmin) {
return false, fmt.Sprintf(i18n.Translate(lang, "OrgErr.OnlyAdmin"), accountItem.Name)
if user == nil || !user.IsAdmin && !user.IsGlobalAdmin {
return false, fmt.Sprintf(i18n.Translate(lang, "organization:Only admin can modify the %s."), accountItem.Name)
}
case "Immutable":
return false, fmt.Sprintf(i18n.Translate(lang, "OrgErr.Immutable"), accountItem.Name)
return false, fmt.Sprintf(i18n.Translate(lang, "organization:The %s is immutable."), accountItem.Name)
case "Self":
break
default:
return false, fmt.Sprintf(i18n.Translate(lang, "OrgErr.UnknownModifyRule"), accountItem.ModifyRule)
return false, fmt.Sprintf(i18n.Translate(lang, "organization:Unknown modify rule %s."), accountItem.ModifyRule)
}
return true, ""
}
@ -222,7 +233,12 @@ func GetDefaultApplication(id string) (*Application, error) {
}
if organization.DefaultApplication != "" {
return getApplication("admin", organization.DefaultApplication), fmt.Errorf("The default application: %s does not exist", organization.DefaultApplication)
defaultApplication := getApplication("admin", organization.DefaultApplication)
if defaultApplication == nil {
return nil, fmt.Errorf("The default application: %s does not exist", organization.DefaultApplication)
} else {
return defaultApplication, nil
}
}
applications := []*Application{}

View File

@ -35,7 +35,7 @@ type Payment struct {
ProductName string `xorm:"varchar(100)" json:"productName"`
ProductDisplayName string `xorm:"varchar(100)" json:"productDisplayName"`
Detail string `xorm:"varchar(100)" json:"detail"`
Detail string `xorm:"varchar(255)" json:"detail"`
Tag string `xorm:"varchar(100)" json:"tag"`
Currency string `xorm:"varchar(100)" json:"currency"`
Price float64 `json:"price"`

View File

@ -16,7 +16,6 @@ package object
import (
"fmt"
"strings"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
@ -57,6 +56,40 @@ type PermissionRule struct {
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 {
session := GetSession(owner, -1, -1, field, value, "", "")
count, err := session.Count(&Permission{})
@ -111,7 +144,33 @@ func GetPermission(id string) *Permission {
return getPermission(owner, name)
}
// checkPermissionValid verifies if the permission is valid
func checkPermissionValid(permission *Permission) {
enforcer := getEnforcer(permission)
enforcer.EnableAutoSave(false)
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 {
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
}
func UpdatePermission(id string, permission *Permission) bool {
checkPermissionValid(permission)
owner, name := util.GetOwnerAndNameFromId(id)
oldPermission := getPermission(owner, name)
if oldPermission == nil {
@ -124,6 +183,7 @@ func UpdatePermission(id string, permission *Permission) bool {
}
if affected != 0 {
removeGroupingPolicies(oldPermission)
removePolicies(oldPermission)
if oldPermission.Adapter != "" && oldPermission.Adapter != permission.Adapter {
isEmpty, _ := adapter.Engine.IsTableEmpty(oldPermission.Adapter)
@ -134,6 +194,7 @@ func UpdatePermission(id string, permission *Permission) bool {
}
}
}
addGroupingPolicies(permission)
addPolicies(permission)
}
@ -147,6 +208,7 @@ func AddPermission(permission *Permission) bool {
}
if affected != 0 {
addGroupingPolicies(permission)
addPolicies(permission)
}
@ -160,6 +222,7 @@ func DeletePermission(permission *Permission) bool {
}
if affected != 0 {
removeGroupingPolicies(permission)
removePolicies(permission)
if permission.Adapter != "" && permission.Adapter != "permission_rule" {
isEmpty, _ := adapter.Engine.IsTableEmpty(permission.Adapter)
@ -175,10 +238,6 @@ func DeletePermission(permission *Permission) bool {
return affected != 0
}
func (permission *Permission) GetId() string {
return fmt.Sprintf("%s/%s", permission.Owner, permission.Name)
}
func GetPermissionsByUser(userId string) []*Permission {
permissions := []*Permission{}
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&permissions)
@ -209,33 +268,6 @@ func GetPermissionsBySubmitter(owner string, submitter string) []*Permission {
return permissions
}
func MigratePermissionRule() {
models := []*Model{}
err := adapter.Engine.Find(&models, &Model{})
if err != nil {
panic(err)
}
isHit := false
for _, model := range models {
if strings.Contains(model.ModelText, "permission") {
// update model table
model.ModelText = strings.Replace(model.ModelText, "permission,", "", -1)
UpdateModel(model.GetId(), model)
isHit = true
}
}
if isHit {
// update permission_rule table
sql := "UPDATE `permission_rule`SET V0 = V1, V1 = V2, V2 = V3, V3 = V4, V4 = V5 WHERE V0 IN (SELECT CONCAT(owner, '/', name) AS permission_id FROM `permission`)"
_, err = adapter.Engine.Exec(sql)
if err != nil {
return
}
}
}
func ContainsAsterisk(userId string, users []string) bool {
containsAsterisk := false
group, _ := util.GetOwnerAndNameFromId(userId)
@ -249,3 +281,12 @@ func ContainsAsterisk(userId string, users []string) bool {
return containsAsterisk
}
func GetMaskedPermissions(permissions []*Permission) []*Permission {
for _, permission := range permissions {
permission.Users = nil
permission.Submitter = ""
}
return permissions
}

View File

@ -15,9 +15,11 @@
package object
import (
"fmt"
"strings"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/config"
"github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/conf"
@ -29,105 +31,125 @@ func getEnforcer(permission *Permission) *casbin.Enforcer {
tableName = permission.Adapter
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
adapter, err := xormadapter.NewAdapterWithTableName(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName()+conf.GetConfigString("dbName"), tableName, tableNamePrefix, true)
driverName := conf.GetConfigString("driverName")
dataSourceName := conf.GetConfigRealDataSourceName(driverName)
adapter, err := xormadapter.NewAdapterWithTableName(driverName, dataSourceName, tableName, tableNamePrefix, true)
if err != nil {
panic(err)
}
modelText := `
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[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)
m := model.Model{}
if permissionModel != nil {
modelText = permissionModel.ModelText
m, err = GetBuiltInModel(permissionModel.ModelText)
} else {
m, err = GetBuiltInModel("")
}
m, err := model.NewModelFromString(modelText)
if err != nil {
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)
if err != nil {
panic(err)
}
// load Policy with a specific Permission
policyFilter.V5 = []string{permission.GetId()}
err = enforcer.LoadFilteredPolicy(policyFilter)
if err != nil {
panic(err)
}
return enforcer
}
func getPolicies(permission *Permission) ([][]string, [][]string) {
func getPolicies(permission *Permission) [][]string {
var policies [][]string
var groupingPolicies [][]string
permissionId := permission.GetId()
domainExist := len(permission.Domains) > 0
for _, user := range permission.Users {
for _, resource := range permission.Resources {
for _, action := range permission.Actions {
if domainExist {
for _, domain := range permission.Domains {
policies = append(policies, []string{user, domain, resource, strings.ToLower(action)})
policies = append(policies, []string{user, domain, resource, strings.ToLower(action), "", permissionId})
}
} else {
policies = append(policies, []string{user, resource, strings.ToLower(action)})
policies = append(policies, []string{user, resource, strings.ToLower(action), "", "", permissionId})
}
}
}
}
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})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subUser, role})
}
}
for _, subRole := range roleObj.Roles {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subRole, domain, role})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subRole, role})
}
}
for _, resource := range permission.Resources {
for _, action := range permission.Actions {
if domainExist {
for _, domain := range permission.Domains {
policies = append(policies, []string{role, domain, resource, strings.ToLower(action)})
policies = append(policies, []string{role, domain, resource, strings.ToLower(action), "", permissionId})
}
} else {
policies = append(policies, []string{role, resource, strings.ToLower(action)})
policies = append(policies, []string{role, resource, strings.ToLower(action), "", "", permissionId})
}
}
}
}
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) {
enforcer := getEnforcer(permission)
policies, groupingPolicies := getPolicies(permission)
if len(groupingPolicies) > 0 {
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
policies := getPolicies(permission)
_, err := enforcer.AddPolicies(policies)
if err != nil {
@ -135,9 +157,21 @@ func addPolicies(permission *Permission) {
}
}
func removePolicies(permission *Permission) {
func addGroupingPolicies(permission *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 {
_, err := enforcer.RemoveGroupingPolicies(groupingPolicies)
@ -145,6 +179,11 @@ func removePolicies(permission *Permission) {
panic(err)
}
}
}
func removePolicies(permission *Permission) {
enforcer := getEnforcer(permission)
policies := getPolicies(permission)
_, err := enforcer.RemovePolicies(policies)
if err != nil {
@ -152,20 +191,24 @@ func removePolicies(permission *Permission) {
}
}
func Enforce(userId string, permissionRule *PermissionRule) bool {
func Enforce(permissionRule *PermissionRule) bool {
permission := GetPermission(permissionRule.Id)
enforcer := getEnforcer(permission)
allow, err := enforcer.Enforce(userId, permissionRule.V1, permissionRule.V2)
request, _ := permissionRule.GetRequest(builtInAdapter, permissionRule.Id)
allow, err := enforcer.Enforce(request...)
if err != nil {
panic(err)
}
return allow
}
func BatchEnforce(userId string, permissionRules []PermissionRule) []bool {
func BatchEnforce(permissionRules []PermissionRule) []bool {
var requests [][]interface{}
for _, permissionRule := range permissionRules {
requests = append(requests, []interface{}{userId, permissionRule.V1, permissionRule.V2})
request, _ := permissionRule.GetRequest(builtInAdapter, permissionRule.Id)
requests = append(requests, request)
}
permission := GetPermission(permissionRules[0].Id)
enforcer := getEnforcer(permission)
@ -210,3 +253,46 @@ func GetAllRoles(userId string) []string {
}
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

@ -27,15 +27,16 @@ type Product struct {
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Image string `xorm:"varchar(100)" json:"image"`
Detail string `xorm:"varchar(100)" json:"detail"`
Tag string `xorm:"varchar(100)" json:"tag"`
Currency string `xorm:"varchar(100)" json:"currency"`
Price float64 `json:"price"`
Quantity int `json:"quantity"`
Sold int `json:"sold"`
Providers []string `xorm:"varchar(100)" json:"providers"`
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
Image string `xorm:"varchar(100)" json:"image"`
Detail string `xorm:"varchar(255)" json:"detail"`
Description string `xorm:"varchar(100)" json:"description"`
Tag string `xorm:"varchar(100)" json:"tag"`
Currency string `xorm:"varchar(100)" json:"currency"`
Price float64 `json:"price"`
Quantity int `json:"quantity"`
Sold int `json:"sold"`
Providers []string `xorm:"varchar(100)" json:"providers"`
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
State string `xorm:"varchar(100)" json:"state"`
@ -213,6 +214,10 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
}
func ExtendProductWithProviders(product *Product) {
if product == nil {
return
}
product.ProviderObjs = []*Provider{}
m := getProviderMap(product.Owner)

View File

@ -25,7 +25,7 @@ import (
type Provider struct {
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 unique" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
@ -93,8 +93,8 @@ func GetMaskedProviders(providers []*Provider) []*Provider {
}
func GetProviderCount(owner, field, value string) int {
session := GetSession(owner, -1, -1, field, value, "", "")
count, err := session.Count(&Provider{})
session := GetSession("", -1, -1, field, value, "", "")
count, err := session.Where("owner = ? or owner = ? ", "admin", owner).Count(&Provider{})
if err != nil {
panic(err)
}
@ -114,7 +114,7 @@ func GetGlobalProviderCount(field, value string) int {
func GetProviders(owner string) []*Provider {
providers := []*Provider{}
err := adapter.Engine.Desc("created_time").Find(&providers, &Provider{Owner: owner})
err := adapter.Engine.Where("owner = ? or owner = ? ", "admin", owner).Desc("created_time").Find(&providers, &Provider{})
if err != nil {
panic(err)
}
@ -133,9 +133,9 @@ func GetGlobalProviders() []*Provider {
}
func GetPaginationProviders(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Provider {
var providers []*Provider
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
err := session.Find(&providers)
providers := []*Provider{}
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
err := session.Where("owner = ? or owner = ? ", "admin", owner).Find(&providers)
if err != nil {
panic(err)
}
@ -144,7 +144,7 @@ func GetPaginationProviders(owner string, offset, limit int, field, value, sortF
}
func GetPaginationGlobalProviders(offset, limit int, field, value, sortField, sortOrder string) []*Provider {
var providers []*Provider
providers := []*Provider{}
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
err := session.Find(&providers)
if err != nil {
@ -159,7 +159,7 @@ func getProvider(owner string, name string) *Provider {
return nil
}
provider := Provider{Owner: owner, Name: name}
provider := Provider{Name: name}
existed, err := adapter.Engine.Get(&provider)
if err != nil {
panic(err)
@ -277,7 +277,7 @@ func GetCaptchaProviderByOwnerName(applicationId, lang string) (*Provider, error
}
if !existed {
return nil, fmt.Errorf(i18n.Translate(lang, "ProviderErr.DoNotExist"), applicationId)
return nil, fmt.Errorf(i18n.Translate(lang, "provider:the provider: %s does not exist"), applicationId)
}
return &provider, nil
@ -289,7 +289,7 @@ func GetCaptchaProviderByApplication(applicationId, isCurrentProvider, lang stri
}
application := GetApplication(applicationId)
if application == nil || len(application.Providers) == 0 {
return nil, fmt.Errorf(i18n.Translate(lang, "ApplicationErr.InvalidID"))
return nil, fmt.Errorf(i18n.Translate(lang, "provider:Invalid application id"))
}
for _, provider := range application.Providers {
if provider.Provider == nil {

View File

@ -15,7 +15,9 @@
package object
type ProviderItem struct {
Name string `json:"name"`
Owner string `json:"owner"`
Name string `json:"name"`
CanSignUp bool `json:"canSignUp"`
CanSignIn bool `json:"canSignIn"`
CanUnlink bool `json:"canUnlink"`

View File

@ -23,7 +23,7 @@ import (
type Resource struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(250) notnull pk" json:"name"`
Name string `xorm:"varchar(180) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
User string `xorm:"varchar(100)" json:"user"`
@ -31,12 +31,12 @@ type Resource struct {
Application string `xorm:"varchar(100)" json:"application"`
Tag string `xorm:"varchar(100)" json:"tag"`
Parent string `xorm:"varchar(100)" json:"parent"`
FileName string `xorm:"varchar(1000)" json:"fileName"`
FileName string `xorm:"varchar(255)" json:"fileName"`
FileType string `xorm:"varchar(100)" json:"fileType"`
FileFormat string `xorm:"varchar(100)" json:"fileFormat"`
FileSize int `json:"fileSize"`
Url string `xorm:"varchar(1000)" json:"url"`
Description string `xorm:"varchar(1000)" json:"description"`
Url string `xorm:"varchar(255)" json:"url"`
Description string `xorm:"varchar(255)" json:"description"`
}
func GetResourceCount(owner, user, field, value string) int {

View File

@ -95,6 +95,12 @@ func UpdateRole(id string, role *Role) bool {
return false
}
permissions := GetPermissionsByRole(id)
for _, permission := range permissions {
removeGroupingPolicies(permission)
removePolicies(permission)
}
if name != role.Name {
err := roleChangeTrigger(name, role.Name)
if err != nil {
@ -107,6 +113,13 @@ func UpdateRole(id string, role *Role) bool {
panic(err)
}
newRoleID := role.GetId()
permissions = GetPermissionsByRole(newRoleID)
for _, permission := range permissions {
addGroupingPolicies(permission)
addPolicies(permission)
}
return affected != 0
}
@ -120,6 +133,13 @@ func AddRole(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{})
if err != nil {
panic(err)
@ -192,3 +212,11 @@ func roleChangeTrigger(oldName string, newName string) error {
return session.Commit()
}
func GetMaskedRoles(roles []*Role) []*Role {
for _, role := range roles {
role.Users = nil
}
return roles
}

View File

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

View File

@ -45,7 +45,7 @@ func ParseSamlResponse(samlResponse string, providerType string) (string, error)
func GenerateSamlLoginUrl(id, relayState, lang string) (string, string, error) {
provider := GetProvider(id)
if provider.Category != "SAML" {
return "", "", fmt.Errorf(i18n.Translate(lang, "ProviderErr.CategoryNotSAML"), provider.Name)
return "", "", fmt.Errorf(i18n.Translate(lang, "saml_sp:provider %s's category is not SAML"), provider.Name)
}
sp, err := buildSp(provider, "")
if err != nil {

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

@ -54,7 +54,7 @@ func escapePath(path string) string {
return res
}
func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
escapedPath := util.UrlJoin(provider.PathPrefix, escapePath(fullFilePath))
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
@ -74,10 +74,15 @@ func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
}
fileUrl := util.UrlJoin(host, escapePath(objectKey))
if hasTimestamp {
fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime())
}
if provider.Type == "Tencent Cloud COS" {
objectKey = escapePath(objectKey)
}
return fileUrl, objectKey
}
@ -93,7 +98,7 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
UpdateProvider(provider.GetId(), provider)
}
fileUrl, objectKey := getUploadFileUrl(provider, fullFilePath, true)
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
_, err := storageProvider.Put(objectKey, fileBuffer)
if err != nil {
@ -130,13 +135,13 @@ func UploadFileSafe(provider *Provider, fullFilePath string, fileBuffer *bytes.B
func DeleteFile(provider *Provider, objectKey string, lang string) error {
// check fullFilePath is there security issue
if strings.Contains(objectKey, "..") {
return fmt.Errorf(i18n.Translate(lang, "StorageErr.ObjectKeyNotAllowed"), objectKey)
return fmt.Errorf(i18n.Translate(lang, "storage:The objectKey: %s is not allowed"), objectKey)
}
endpoint := getProviderEndpoint(provider)
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
if storageProvider == nil {
return fmt.Errorf(i18n.Translate(lang, "ProviderErr.ProviderNotSupported"), provider.Type)
return fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type)
}
if provider.Domain == "" {

View File

@ -18,7 +18,6 @@ import (
"crypto/sha256"
"encoding/base64"
"fmt"
"strings"
"time"
"github.com/casdoor/casdoor/i18n"
@ -28,7 +27,7 @@ import (
)
const (
hourSeconds = 3600
hourMinutes = 60
InvalidRequest = "invalid_request"
InvalidClient = "invalid_client"
InvalidGrant = "invalid_grant"
@ -169,6 +168,10 @@ func GetToken(id string) *Token {
return getToken(owner, name)
}
func (token *Token) GetId() string {
return fmt.Sprintf("%s/%s", token.Owner, token.Name)
}
func UpdateToken(id string, token *Token) bool {
owner, name := util.GetOwnerAndNameFromId(id)
if getToken(owner, name) == nil {
@ -201,7 +204,7 @@ func DeleteToken(token *Token) bool {
return affected != 0
}
func DeleteTokenByAccessToken(accessToken string) (bool, *Application) {
func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token) {
token := Token{AccessToken: accessToken}
existed, err := adapter.Engine.Get(&token)
if err != nil {
@ -209,15 +212,17 @@ func DeleteTokenByAccessToken(accessToken string) (bool, *Application) {
}
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 {
panic(err)
}
return affected != 0, application
application := getApplication(token.Owner, token.Application)
return affected != 0, application, &token
}
func GetTokenByAccessToken(accessToken string) *Token {
@ -241,23 +246,16 @@ func GetTokenByTokenAndApplication(token string, application string) *Token {
func CheckOAuthLogin(clientId string, responseType string, redirectUri string, scope string, state string, lang string) (string, *Application) {
if responseType != "code" && responseType != "token" && responseType != "id_token" {
return fmt.Sprintf(i18n.Translate(lang, "ApplicationErr.GrantTypeNotSupport"), responseType), nil
return fmt.Sprintf(i18n.Translate(lang, "token:Grant_type: %s is not supported in this application"), responseType), nil
}
application := GetApplicationByClientId(clientId)
if application == nil {
return i18n.Translate(lang, "TokenErr.InvalidClientId"), nil
return i18n.Translate(lang, "token:Invalid client_id"), nil
}
validUri := false
for _, tmpUri := range application.RedirectUris {
if strings.Contains(redirectUri, tmpUri) {
validUri = true
break
}
}
if !validUri {
return fmt.Sprintf(i18n.Translate(lang, "TokenErr.RedirectURIDoNotExist"), redirectUri), application
if !application.IsRedirectUriValid(redirectUri) {
return fmt.Sprintf(i18n.Translate(lang, "token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri), application
}
// Mask application for /api/get-app-login
@ -269,7 +267,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
user := GetUser(userId)
if user == nil {
return &Code{
Message: fmt.Sprintf("The user: %s doesn't exist", userId),
Message: fmt.Sprintf("general:The user: %s doesn't exist", userId),
Code: "",
}
}
@ -308,7 +306,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
Code: util.GenerateClientId(),
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds,
ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope,
TokenType: "Bearer",
CodeChallenge: challenge,
@ -442,7 +440,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
Code: util.GenerateClientId(),
AccessToken: newAccessToken,
RefreshToken: newRefreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds,
ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope,
TokenType: "Bearer",
}
@ -592,7 +590,7 @@ func GetPasswordToken(application *Application, username string, password string
Code: util.GenerateClientId(),
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds,
ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope,
TokenType: "Bearer",
CodeIsUsed: true,
@ -632,7 +630,7 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
User: nullUser.Name,
Code: util.GenerateClientId(),
AccessToken: accessToken,
ExpiresIn: application.ExpireInHours * hourSeconds,
ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope,
TokenType: "Bearer",
CodeIsUsed: true,
@ -659,7 +657,7 @@ func GetTokenByUser(application *Application, user *User, scope string, host str
Code: util.GenerateClientId(),
AccessToken: accessToken,
RefreshToken: refreshToken,
ExpiresIn: application.ExpireInHours * hourSeconds,
ExpiresIn: application.ExpireInHours * hourMinutes,
Scope: scope,
TokenType: "Bearer",
CodeIsUsed: true,
@ -678,7 +676,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
ErrorDescription: "the application does not support wechat mini program",
}
}
provider := GetProvider(util.GetId(mpProvider.Name))
provider := GetProvider(util.GetId("admin", mpProvider.Name))
mpIdp := idp.NewWeChatMiniProgramIdProvider(provider.ClientId, provider.ClientSecret)
session, err := mpIdp.GetSessionByCode(code)
if err != nil {

View File

@ -36,6 +36,60 @@ type UserShort struct {
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 {
*UserShort
TokenType string `json:"tokenType,omitempty"`
@ -44,6 +98,15 @@ type ClaimsShort struct {
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 {
res := &UserShort{
Owner: user.Owner,
@ -52,6 +115,69 @@ func getShortUser(user *User) *UserShort {
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 {
res := ClaimsShort{
UserShort: getShortUser(claims.User),
@ -63,12 +189,44 @@ func getShortClaims(claims Claims) ClaimsShort {
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) {
nowTime := time.Now()
expireTime := nowTime.Add(time.Duration(application.ExpireInHours) * time.Hour)
refreshExpireTime := nowTime.Add(time.Duration(application.RefreshExpireInHours) * time.Hour)
user.Password = ""
user = refineUser(user)
_, originBackend := getOriginFromHost(host)
name := util.GenerateId()
@ -104,10 +262,12 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
claimsShort.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
} else {
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claims.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
}
cert := getCertByApplication(application)

View File

@ -78,31 +78,80 @@ type User struct {
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
LastSigninIp string `xorm:"varchar(100)" json:"lastSigninIp"`
GitHub string `xorm:"github varchar(100)" json:"github"`
Google string `xorm:"varchar(100)" json:"google"`
QQ string `xorm:"qq varchar(100)" json:"qq"`
WeChat string `xorm:"wechat varchar(100)" json:"wechat"`
Facebook string `xorm:"facebook varchar(100)" json:"facebook"`
DingTalk string `xorm:"dingtalk varchar(100)" json:"dingtalk"`
Weibo string `xorm:"weibo varchar(100)" json:"weibo"`
Gitee string `xorm:"gitee varchar(100)" json:"gitee"`
LinkedIn string `xorm:"linkedin varchar(100)" json:"linkedin"`
Wecom string `xorm:"wecom varchar(100)" json:"wecom"`
Lark string `xorm:"lark varchar(100)" json:"lark"`
Gitlab string `xorm:"gitlab varchar(100)" json:"gitlab"`
Adfs string `xorm:"adfs varchar(100)" json:"adfs"`
Baidu string `xorm:"baidu varchar(100)" json:"baidu"`
Alipay string `xorm:"alipay varchar(100)" json:"alipay"`
Casdoor string `xorm:"casdoor varchar(100)" json:"casdoor"`
Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"`
Apple string `xorm:"apple varchar(100)" json:"apple"`
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
Slack string `xorm:"slack varchar(100)" json:"slack"`
Steam string `xorm:"steam varchar(100)" json:"steam"`
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
Okta string `xorm:"okta varchar(100)" json:"okta"`
Douyin string `xorm:"douyin varchar(100)" json:"douyin"`
Custom string `xorm:"custom varchar(100)" json:"custom"`
GitHub string `xorm:"github varchar(100)" json:"github"`
Google string `xorm:"varchar(100)" json:"google"`
QQ string `xorm:"qq varchar(100)" json:"qq"`
WeChat string `xorm:"wechat varchar(100)" json:"wechat"`
Facebook string `xorm:"facebook varchar(100)" json:"facebook"`
DingTalk string `xorm:"dingtalk varchar(100)" json:"dingtalk"`
Weibo string `xorm:"weibo varchar(100)" json:"weibo"`
Gitee string `xorm:"gitee varchar(100)" json:"gitee"`
LinkedIn string `xorm:"linkedin varchar(100)" json:"linkedin"`
Wecom string `xorm:"wecom varchar(100)" json:"wecom"`
Lark string `xorm:"lark varchar(100)" json:"lark"`
Gitlab string `xorm:"gitlab varchar(100)" json:"gitlab"`
Adfs string `xorm:"adfs varchar(100)" json:"adfs"`
Baidu string `xorm:"baidu varchar(100)" json:"baidu"`
Alipay string `xorm:"alipay varchar(100)" json:"alipay"`
Casdoor string `xorm:"casdoor varchar(100)" json:"casdoor"`
Infoflow string `xorm:"infoflow varchar(100)" json:"infoflow"`
Apple string `xorm:"apple varchar(100)" json:"apple"`
AzureAD string `xorm:"azuread varchar(100)" json:"azuread"`
Slack string `xorm:"slack varchar(100)" json:"slack"`
Steam string `xorm:"steam varchar(100)" json:"steam"`
Bilibili string `xorm:"bilibili varchar(100)" json:"bilibili"`
Okta string `xorm:"okta varchar(100)" json:"okta"`
Douyin string `xorm:"douyin varchar(100)" json:"douyin"`
Line string `xorm:"line varchar(100)" json:"line"`
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"`
@ -528,6 +577,9 @@ func AddUsersInBatch(users []*User) bool {
}
func DeleteUser(user *User) bool {
// Forced offline the user first
DeleteSession(user.GetId())
affected, err := adapter.Engine.ID(core.PK{user.Owner, user.Name}).Delete(&User{})
if err != nil {
panic(err)

View File

@ -26,6 +26,10 @@ import (
"xorm.io/core"
)
const (
wrongCode = "wrongCode"
)
type VerificationRecord struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
@ -153,7 +157,7 @@ func CheckVerificationCode(dest, code, lang string) string {
record := getVerificationRecord(dest)
if record == nil {
return i18n.Translate(lang, "PhoneErr.CodeNotSent")
return i18n.Translate(lang, "verification:Code has not been sent yet!")
}
timeout, err := conf.GetConfigInt64("verificationCodeTimeout")
@ -163,11 +167,11 @@ func CheckVerificationCode(dest, code, lang string) string {
now := time.Now().Unix()
if now-record.Time > timeout*60 {
return fmt.Sprintf(i18n.Translate(lang, "PhoneErr.CodeTimeOut"), timeout)
return fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeout)
}
if record.Code != code {
return "Wrong code!"
return wrongCode
}
return ""
@ -186,6 +190,24 @@ func DisableVerificationCode(dest string) {
}
}
func CheckSigninCode(user *User, dest, code, lang string) string {
// check the login error times
if msg := checkSigninErrorTimes(user, lang); msg != "" {
return msg
}
result := CheckVerificationCode(dest, code, lang)
switch result {
case "":
resetUserSigninErrorTimes(user)
return ""
case wrongCode:
return recordSigninErrorInfo(user, lang)
default:
return result
}
}
// From Casnode/object/validateCode.go line 116
var stdNums = []byte("0123456789")

View File

@ -37,7 +37,7 @@ type Webhook struct {
Method string `xorm:"varchar(100)" json:"method"`
ContentType string `xorm:"varchar(100)" json:"contentType"`
Headers []*Header `xorm:"mediumtext" json:"headers"`
Events []string `xorm:"varchar(100)" json:"events"`
Events []string `xorm:"varchar(1000)" json:"events"`
IsUserExtended bool `json:"isUserExtended"`
IsEnabled bool `json:"isEnabled"`
}

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