Compare commits

...

177 Commits

Author SHA1 Message Date
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
939b416717 fix: limit only under PC can login by following Wechat Official Account (#1293) 2022-11-14 09:03:55 +08:00
f115843fbb feat: fix verification code send time's limit logic (#1292) 2022-11-13 22:00:48 +08:00
aa6a4dc74f feat: support login by following wechat official account (#1284)
* show QRcode when click WeChat Icon

* update how to show qrcode

* handle wechat scan qrcode

* fix api problems

* fix url problems

* fix problems

* modify get frequency

* remove useless print

* fix:fix PR problems

* fix: fix PR problems

* fix:fix PR problem

* fix IMG load delay problems

* fix:fix provider problems

* fix test problems

* use gofumpt to fmt code

* fix:delete useless variables

* feat:add button for follow official account

* fix:fix review problems

* use gofumpt to fmt code

* fix:fix scantype problems

* fix Response problem

* use gofumpt to format code
2022-11-13 15:05:15 +08:00
462a82a3d5 fix: Add distinctions between access_token and refresh_token (#1280) 2022-11-13 13:00:25 +08:00
262aeba7e2 fix(RoleEditPage): Fix a crash when the sample role domains is null (#1268) 2022-11-13 12:28:24 +08:00
61c2fd5412 feat: fix the issue of jumping back to the login page after resetting password (#1288)
* fix: redirect to login page

* fix: front end router

* fix: front end router

* fix: signup page router

* fix: redirect to login page
2022-11-13 12:16:49 +08:00
d542208eb8 feat: fix select language box overlay (#1289)
* fix: select language box overlay

* fix: select language box position

* fix: select language box position

* fix: select language box position
2022-11-13 10:52:22 +08:00
f818200c95 feat: fix empty organization in adapter edit page (#1274) 2022-11-08 21:03:15 +08:00
5bc2e91344 fix: fix typo (#1264) 2022-11-06 21:14:26 +08:00
295f732b18 Show tag in i18n 2022-11-06 20:19:31 +08:00
770ae47471 feat: fix memory leak problem (#1257) 2022-11-06 01:43:27 +08:00
2ce4f96355 fix: forget page mobile view (#1263) 2022-11-05 22:54:22 +08:00
07ed834b27 fix: system info mobile view (#1261) 2022-11-05 22:46:52 +08:00
8d686411ee feat: support add providers inside the Organization scope (#1250)
* feat: support add providers inside the Organization scope

Signed-off-by: magicwind <2814461814@qq.com>

* Update ProviderListPage.js

* fix: gloabal admin can see all providers

* fix: table fixed column warning

* fix: edit application page can get all providers

Signed-off-by: magicwind <2814461814@qq.com>
Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-11-04 21:31:08 +08:00
ce722897f1 feat: support prefix path for storage files (#1258) 2022-11-04 21:08:39 +08:00
a8381e875b feat: change all occurrences when a object name is changed (#1252) 2022-11-02 00:17:38 +08:00
4c81fd7d16 feat: fix generating wrong x.509 private key file header (#1253)
According to the [official x509 documentation](https://pkg.go.dev/crypto/x509#MarshalPKCS1PrivateKey), the private key generated using `x509.MarshalPKCS1PrivateKey` starts with `-----BEGIN RSA PRIVATE KEY-----` instead of `-----BEGIN PRIVATE KEY-----`. Otherwise, it will not be parsed by most tools (like OpenSSL, [jwt.io](https://jwt.io/), etc.) because it does not conform to the specification.
2022-11-01 22:19:38 +08:00
25ee4226d3 feat: clear the session of a signin but non-existent user (#1246) 2022-10-29 20:18:02 +08:00
9d5b019243 fix: nil error if init data is empty (#1247) 2022-10-29 20:04:43 +08:00
6bb7b545b4 feat: restrict DingTalk user log in who is under the DingTalk Org(which ClientId belong) (#1241)
* feat: fix bug in GetAcceptLanguage()

* feat: add appName when logging in with DingTalk

* fix review problems

* format code

* delete useless printf

* modify display name

Co-authored-by: Gucheng Wang <nomeguy@qq.com>
2022-10-28 22:14:05 +08:00
25d56ee8d5 feat: allow captcha to be enabled when logging in (#1211)
* Fix bug in GetAcceptLanguage()

* feat: allow captcha to be enabled when logging in

* feat: when the login password is wrong, enable captcha

* feat: Restrict captcha from frontend

* fix: modify CaptchaModal component

* fix: modify the words of i18n

* Update data.json

Co-authored-by: Gucheng Wang <nomeguy@qq.com>
Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-10-28 13:38:14 +08:00
7e5952c804 fix: login / signin frontend router (#1244)
* fix: go to link

* fix: remove gotologin

* fix: redirect to login page

* fix: redirect to login page

* remove comments

* fix: formats

* fix: formats

* Update Setting.js

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-10-28 02:23:57 +08:00
80bf29d79a feat: fix showing wrong error message: "Please sign in first" (#1245) 2022-10-27 23:50:45 +08:00
971e53dfd8 fix: fix duplicated user bug in user list page (#1243)
* fix: user list repititon errer

* Update UserListPage.js

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-10-27 22:51:05 +08:00
654b903d7a feat: fix multi-platform docker image (#1242) 2022-10-26 23:31:00 +08:00
2f72e6971b fix: make the app list in homepage have the same height (#1239)
* fix: make the app list in homepage have the same height

* fix: make the app list in homepage have the same height

* Update SingleCard.js

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-10-25 19:27:24 +08:00
d4b587b93e feat: fix bug in GetAcceptLanguage() (#1237)
Co-authored-by: Gucheng Wang <nomeguy@qq.com>
2022-10-25 10:50:10 +08:00
ac7a510949 Fix go.mod 2022-10-23 16:14:49 +08:00
d86f3c88c7 feat: support i18n in backend err messages (#1232)
* feat: support i18n in backend err messages

* use gofumpt to fmt code

* fix review problems

* support auto generate err message

* delete beego/i18n moudle

* fix Github action test problems

* fix review problems

* use gofumpt to format code

* use gofumpt to fmt code
2022-10-23 15:16:24 +08:00
7c77519069 Fix formPosition typo 2022-10-23 02:26:50 +08:00
2bdf467e3a Update formCss default value 2022-10-23 01:27:01 +08:00
52b692c8ad Refactor to renderLink() 2022-10-22 23:48:59 +08:00
304643736b fix: forget password and sign up router (#1227)
* fix: forget password and sign up router

* fix: link

* fix: jump logic

* fix: signup link

* fix: signup link

* fix: login and signup router

* remove comments

* fix: normal router

* fix: link abstraction

* rename jump component

* fix: session storage

* fix: store signin url

* fix: jumplink props

* fix: simplify link

* fix: path join

* fix: remove unused functions
2022-10-22 23:17:50 +08:00
b0f572c51a feat: add left-side image and improve login page (#1226) 2022-10-22 21:43:41 +08:00
19d351d157 feat: allow non-ASCII characters in username (#1235) 2022-10-22 20:46:50 +08:00
d0751bf2fa feat: add arm docker (#1236) 2022-10-22 11:08:29 +08:00
290cc60f00 feat: non root user for casdoor image (#1234)
Signed-off-by: abingcbc <abingcbc626@gmail.com>

Signed-off-by: abingcbc <abingcbc626@gmail.com>
2022-10-21 17:19:58 +08:00
6a1ec51978 feat: fix SSRF when download avatar (#1193) 2022-10-20 14:47:08 +08:00
dffa68cbce feat: fix SAML login error bug (#1228)
* Update LoginPage.js

* fix saml login error
2022-10-20 01:14:38 +08:00
fad209a7a3 Don't check username in UpdateUser() API 2022-10-19 22:50:19 +08:00
8b222ce2e3 Use Steam ID as username 2022-10-18 22:07:20 +08:00
c5293f428d fix: delete this accidentally added files (#1229)
* fix: delete this accidentally added files

* fix: ignore build result

* fix: remove unnecessary asterisk
2022-10-18 21:55:34 +08:00
146aec9ee8 feat: skip username restriction for new users coming from OAuth providers. (#1225) 2022-10-17 18:01:01 +08:00
50a52de856 feat: support database version control (#1221)
* feat: support Database version control

* Update adapter.go

* fix review problems

* Update adapter.go

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-10-15 17:20:20 +08:00
8f7a8d7d4f fix: translation without reloading (#1215)
* fix: translation without reloading

* fix: language switch
2022-10-12 19:52:02 +08:00
23f3fe1e3c feat: update code format (#1214)
* feat: doc

* feat: doc

* Update model.go

Co-authored-by: Gucheng <85475922+nomeguy@users.noreply.github.com>
2022-10-12 11:42:14 +08:00
59ff5e02ab fix: Add support for including underscores for username (#1210)
* fix: Add support for including underscores for username

* Update check.go

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-10-11 19:39:19 +08:00
8d41508d6b fix: center loading in account page (#1209)
* fix: center loading in account page

* Update UserEditPage.js

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-10-11 00:52:08 +08:00
04f70cf012 Improve renderRightDropdown() 2022-10-10 22:53:47 +08:00
83724c73f9 feat: fix pad and mobile views (#1202)
* fix figure width

* fix: pad resolution menu

* feat: drawer style mobile menu

* fix: menu button i18n
2022-10-10 22:37:25 +08:00
33e419e133 Show more items to org admin 2022-10-10 21:58:17 +08:00
b832c304ae Can get owner in getObject() 2022-10-10 20:56:55 +08:00
4c7f6fda37 fix: Add restriction to username when signing up (#1203) 2022-10-10 19:58:02 +08:00
e4a54fe375 fix: disable roles inputbox when model doesn't support RBAC (#1201)
* feat:Support simple ldap server

* fix:fix review problems

* fix:fix review problems

* fix: fix ldapserver crash bug

* Update ldapserver.go

* fix: fix dulpicate go routines

* fix gofumpt problems

* fix: fix UserList error

* feat:disable 'sub role' when model is incorrect

* feat:disable 'sub role' when model is incorrect

* feat:disable 'sub role' when model is incorrect

* delete useless output

* update func name

* Update PermissionEditPage.js

* Update PermissionEditPage.js

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-10-10 00:53:55 +08:00
87da3dad76 Remove useless file 2022-10-09 22:18:38 +08:00
44ad88353f Add error to GetDefaultApplication() 2022-10-09 10:39:33 +08:00
a955fb57d6 feat: fix UserList error (#1194)
* feat:Support simple ldap server

* fix:fix review problems

* fix:fix review problems

* fix: fix ldapserver crash bug

* Update ldapserver.go

* fix: fix dulpicate go routines

* fix gofumpt problems

* fix: fix UserList error
2022-10-08 20:00:45 +08:00
d2960ad66b Fix README typo 2022-10-08 16:00:08 +08:00
5243aabf43 docs: Create SECURITY.md (#1192) 2022-10-07 19:02:35 +08:00
d3a2c2a66e Improve org admin permissions 2022-10-07 16:27:21 +08:00
0a9058a585 Improve user list page 2022-10-07 15:43:50 +08:00
225719810b Update link typo in README 2022-10-06 19:37:00 +08:00
c634d4a891 feat: add some css style for the custom Provider button (#1185)
* fix: add some css style for the custom button

* fix: refactor previous code

* fix: add i18 adaptation

* fix: modifiy the saml codition
2022-10-06 19:28:02 +08:00
3dc01ec85d fix: language widget poisition without border css (#1188) 2022-10-06 17:26:12 +08:00
a7324f1da1 Improve className 2022-10-03 22:45:36 +08:00
6da452d7e0 feat: show language widget in signup and signin pages (#1180) 2022-10-03 22:40:19 +08:00
5abcf913e6 Fix language menu 2022-10-03 22:39:10 +08:00
58455e688e Improve WebAuthnCredentialTable and border radius 2022-10-03 18:46:40 +08:00
4d6f68eddc Improve footer and color 2022-10-03 17:43:19 +08:00
67f3c5a489 Add verificationCode to login method 2022-10-03 15:41:20 +08:00
9c48582e0c feat: fix bugs in webauthn (#1173) 2022-10-01 11:10:55 +08:00
645c631db9 fix: fix the delete file vulnerability issue (#1174) 2022-10-01 00:33:27 +08:00
3128e68df4 Add sendSilentSigninData() 2022-09-30 01:51:58 +08:00
2247c6a883 Add isSelf() in user edit page 2022-09-29 22:24:05 +08:00
04709f731b Update beego to v1.12.11 2022-09-29 19:45:17 +08:00
ebe1887e8b feat: add saml provider error (#1168)
* fix: add saml provider error

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: search

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-09-28 21:09:39 +08:00
a7a8805713 feat: fix dulpicate go routines (#1167)
* feat:Support simple ldap server

* fix:fix review problems

* fix:fix review problems

* fix: fix ldapserver crash bug

* Update ldapserver.go

* fix: fix dulpicate go routines

* fix gofumpt problems
2022-09-28 20:28:00 +08:00
ceabbe27b4 feat: remove p_type in table permission_rule (#1165) 2022-09-27 22:50:27 +08:00
7393b90155 Add enableAutoSignin to application 2022-09-27 20:06:46 +08:00
0098c05fb3 feat: add support for smsbao sms (#1164) 2022-09-27 08:37:55 +08:00
34324d9f72 fix: fix ldapserver crash bug (#1161)
* feat:Support simple ldap server

* fix:fix review problems

* fix:fix review problems

* fix: fix ldapserver crash bug

* Update ldapserver.go
2022-09-26 18:27:17 +08:00
28b381e01e fix: fix webauthn redirection (#1148) 2022-09-25 21:41:52 +08:00
40039e0412 feat: add twilio SMS (#1159) 2022-09-25 17:58:12 +08:00
116420adb2 feat: revert "feat: fix openid address format" (#1158)
This reverts commit a447d64bf2.
2022-09-25 09:58:45 +08:00
07c1e3b836 feat: support simple LDAP server (#1155)
* feat:Support simple ldap server

* fix:fix review problems

* fix:fix review problems
2022-09-24 21:48:29 +08:00
a447d64bf2 fix: openid address format (#1157) 2022-09-24 15:34:11 +08:00
4116b1d305 feat: fix google login flash bug (#1147) 2022-09-23 16:03:09 +08:00
1490044295 fix: add returnUrl for user edit page (#1152)
* feat: add redirect param

Signed-off-by: magicwind <2814461814@qq.com>

* Update UserEditPage.js

Signed-off-by: magicwind <2814461814@qq.com>
Co-authored-by: Gucheng <85475922+nomeguy@users.noreply.github.com>
2022-09-23 12:01:21 +08:00
79f2af405a fix: check whether to use go proxy in build (#1149) 2022-09-22 22:14:25 +08:00
575a248c41 Add TestGetEmailsForUsers() 2022-09-22 20:51:50 +08:00
7083904634 Improve isValidPersonName() 2022-09-21 21:35:39 +08:00
3d50255060 feat: login background image display (#1145) 2022-09-20 23:06:24 +08:00
e295da774f Improve record list page 2022-09-18 23:11:40 +08:00
a3cee496b4 Add add-record API 2022-09-18 17:35:34 +08:00
084a5c3e6b Show logs to org admin 2022-09-18 16:16:45 +08:00
6670450439 Update CI node-version to 16 2022-09-18 15:52:12 +08:00
e1331f314d Add RequireSignedInUser() 2022-09-18 15:43:49 +08:00
604033aa02 feat: use Casdoor to manage Casbin DB table (#1100)
* feat: use Casdoor to manage Casbin DB table

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: remove trivial codes

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* chore: go fmt

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* feat: support role definition

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: i18n

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: i18n

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-09-14 22:14:13 +08:00
729c20393c fix: missing providers and org in GetDefaultApplication (#1123)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-09-13 22:54:05 +08:00
a90b27b74a Fix admin UI issues 2022-09-13 21:32:18 +08:00
5707e38912 feat: add batchSize to conf (#1120) 2022-09-13 20:31:22 +08:00
ed959bd8c7 feat: improve login page style (#1119)
Signed-off-by: magicwind <2814461814@qq.com>

Signed-off-by: magicwind <2814461814@qq.com>
2022-09-12 00:01:18 +08:00
b6cdc46023 feat: add defaultApplication for Orgnization (#1111)
* feat: add defaultApplication for Orgnization

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: remove redundant codes

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: don't use app-built-in

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* fix: add query param

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* Update organization.go

* Update organization.go

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-09-10 20:41:45 +08:00
c661a57cb2 Support regex in CheckRedirectUriValid() 2022-09-10 13:12:36 +08:00
8456b7f7c4 fix: with error pq: column "DingTalk" of relation "user" does not exist (#1116)
* feat: add dingtalk union_id

* fix: with pg, column Dingtalk of relation user table does not exist.

* Update user_util.go

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-09-10 13:08:37 +08:00
e8d2906e3c Fix bug in form CSS 2022-09-10 01:33:44 +08:00
1edb91b3a3 feat: custom login form and background (#1107)
* feat: custom login form and background

Signed-off-by: magicwind <2814461814@qq.com>

* feat: costom login form border

* chore: update i18

* Update ApplicationEditPage.js

* Update LoginPage.js

* Update SignupPage.js

* Update LoginPage.js

* Update ApplicationEditPage.js

Signed-off-by: magicwind <2814461814@qq.com>
Co-authored-by: Gucheng <85475922+nomeguy@users.noreply.github.com>
2022-09-10 00:56:37 +08:00
94b6eb803d Fix WeChat MP login "state too long" bug 2022-09-09 11:43:54 +08:00
cfce5289ed Rename getStateFromQueryParams() and getQueryParamsFromState() 2022-09-09 02:02:32 +08:00
10f1c37730 Fix 403 bug for /api/login/* APIs 2022-09-09 01:54:05 +08:00
6035b98653 feat: add dingtalk union_id (#1110) 2022-09-08 14:44:06 +08:00
e158b58ffa fix: add hidden signal to support chrome extension to auto-signin (#1109)
* feat: add hiden applicationName(support chrome extension to auto recognize applicationName)

* feat: add hiden applicationName for all page

* fix typo

* delete unuseful code

* remove hidden applicationName from login page

* prevent crash if signupApplication is null

* Update App.js

Co-authored-by: Gucheng <85475922+nomeguy@users.noreply.github.com>
2022-09-07 17:02:28 +08:00
a399184cfc fix: correct edit URL in model list (#1108)
Co-authored-by: Mario Fischer <mario.fischer@inmanet.de>
2022-09-07 00:54:27 +08:00
221 changed files with 9794 additions and 2437 deletions

0
$env
View File

View File

@ -34,7 +34,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14.17.0'
node-version: 16
# cache
- uses: c-hive/gha-yarn-cache@v2
with:
@ -89,7 +89,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 12
node-version: 16
- name: Fetch Previous version
id: get-previous-tag
@ -125,26 +125,36 @@ jobs:
fi
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up buildx
id: buildx
uses: docker/setup-buildx-action@v2
with:
version: latest
- name: Log in to Docker Hub
uses: docker/login-action@v1
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' &&steps.should_push.outputs.push=='true'
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Push to Docker Hub
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
with:
target: STANDARD
platforms: linux/amd64,linux/arm64
push: true
tags: casbin/casdoor:${{steps.get-current-tag.outputs.tag }},casbin/casdoor:latest
- name: Push All In One Version to Docker Hub
uses: docker/build-push-action@v2
uses: docker/build-push-action@v3
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
with:
target: ALLINONE
platforms: linux/amd64,linux/arm64
push: true
tags: casbin/casdoor-all-in-one:${{steps.get-current-tag.outputs.tag }},casbin/casdoor-all-in-one:latest

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 }}

3
.gitignore vendored
View File

@ -27,3 +27,6 @@ logs/
files/
lastupdate.tmp
commentsRouter*.go
# ignore build result
casdoor

View File

@ -2,7 +2,7 @@ FROM node:16.13.0 AS FRONT
WORKDIR /web
COPY ./web .
RUN yarn config set registry https://registry.npmmirror.com
RUN yarn install && yarn run build
RUN yarn install --frozen-lockfile --network-timeout 1000000 && yarn run build
FROM golang:1.17.5 AS BACK
@ -13,16 +13,29 @@ RUN ./build.sh
FROM alpine:latest AS STANDARD
LABEL MAINTAINER="https://casdoor.org/"
ARG USER=casdoor
ARG TARGETOS
ARG TARGETARCH
ENV BUILDX_ARCH="${TARGETOS:-linux}_${TARGETARCH:-amd64}"
RUN sed -i 's/https/http/' /etc/apk/repositories
RUN apk add --update sudo
RUN apk add curl
RUN apk add ca-certificates && update-ca-certificates
RUN adduser -D $USER -u 1000 \
&& echo "$USER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/$USER \
&& chmod 0440 /etc/sudoers.d/$USER \
&& mkdir logs \
&& chown -R $USER:$USER logs
USER 1000
WORKDIR /
COPY --from=BACK /go/src/casdoor/server ./server
COPY --from=BACK /go/src/casdoor/swagger ./swagger
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf
COPY --from=FRONT /web/build ./web/build
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/server_${BUILDX_ARCH} ./server
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/swagger ./swagger
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/conf/app.conf ./conf/app.conf
COPY --from=FRONT --chown=$USER:$USER /web/build ./web/build
ENTRYPOINT ["/server"]
@ -36,12 +49,15 @@ RUN apt update \
FROM db AS ALLINONE
LABEL MAINTAINER="https://casdoor.org/"
ARG TARGETOS
ARG TARGETARCH
ENV BUILDX_ARCH="${TARGETOS:-linux}_${TARGETARCH:-amd64}"
RUN apt update
RUN apt install -y ca-certificates && update-ca-certificates
WORKDIR /
COPY --from=BACK /go/src/casdoor/server ./server
COPY --from=BACK /go/src/casdoor/server_${BUILDX_ARCH} ./server
COPY --from=BACK /go/src/casdoor/swagger ./swagger
COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf

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://docs.casdoor.cn
https://casdoor.org
## Install
@ -69,7 +67,7 @@ https://casdoor.org/docs/how-to-connect/overview
## Integrations
https://casdoor.org/docs/integration/apisix
https://casdoor.org/docs/category/integrations
## How to contact?

9
SECURITY.md Normal file
View File

@ -0,0 +1,9 @@
# Security Policy
## Reporting a Vulnerability
We are grateful for security researchers and users reporting a vulnerability to us first. To ensure that your request is handled in a timely manner and we can keep users safe, please follow the below guidelines.
- **Please do not report security vulnerabilities directly on GitHub.**
- To report a vulnerability, please email [admin@casdoor.org](admin@casdoor.org).

View File

@ -15,10 +15,14 @@
package authz
import (
"fmt"
"strings"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v2"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object"
stringadapter "github.com/qiangmzsx/string-adapter/v2"
)
@ -28,7 +32,9 @@ func InitAuthz() {
var err error
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
a, err := xormadapter.NewAdapterWithTableName(conf.GetConfigString("driverName"), conf.GetBeegoConfDataSourceName()+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)
}
@ -81,12 +87,15 @@ p, *, *, POST, /api/logout, *, *
p, *, *, GET, /api/logout, *, *
p, *, *, GET, /api/get-account, *, *
p, *, *, GET, /api/userinfo, *, *
p, *, *, POST, /api/webhook, *, *
p, *, *, GET, /api/get-webhook-event, *, *
p, *, *, *, /api/login/oauth, *, *
p, *, *, GET, /api/get-application, *, *
p, *, *, GET, /api/get-organization-applications, *, *
p, *, *, GET, /api/get-user, *, *
p, *, *, GET, /api/get-user-application, *, *
p, *, *, GET, /api/get-resources, *, *
p, *, *, GET, /api/get-records, *, *
p, *, *, GET, /api/get-product, *, *
p, *, *, POST, /api/buy-product, *, *
p, *, *, GET, /api/get-payment, *, *
@ -108,6 +117,7 @@ p, *, *, GET, /api/saml/metadata, *, *
p, *, *, *, /cas, *, *
p, *, *, *, /api/webauthn, *, *
p, *, *, GET, /api/get-release, *, *
p, *, *, GET, /api/get-default-application, *, *
`
sa := stringadapter.NewAdapter(ruleText)
@ -134,6 +144,12 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
}
}
userId := fmt.Sprintf("%s/%s", subOwner, subName)
user := object.GetUser(userId)
if user != nil && user.IsAdmin && (subOwner == objOwner || (objOwner == "admin" && subOwner == objName)) {
return true
}
res, err := Enforcer.Enforce(subOwner, subName, method, urlPath, objOwner, objName)
if err != nil {
panic(err)
@ -144,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 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" {
return true
} else if urlPath == "/api/update-user" {
// Allow ordinary users to update their own information

View File

@ -1,6 +1,6 @@
#!/bin/bash
#try to connect to google to determine whether user need to use proxy
curl www.google.com -o /dev/null --connect-timeout 5 2 > /dev/null
curl www.google.com -o /dev/null --connect-timeout 5 2> /dev/null
if [ $? == 0 ]
then
echo "Successfully connected to Google, no need to use Go proxy"
@ -8,4 +8,5 @@ else
echo "Google is blocked, Go proxy is enabled: GOPROXY=https://goproxy.cn,direct"
export GOPROXY="https://goproxy.cn,direct"
fi
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o server .
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o server_linux_amd64 .
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-w -s" -o server_linux_arm64 .

View File

@ -14,6 +14,8 @@
package captcha
import "fmt"
type CaptchaProvider interface {
VerifyCaptcha(token, clientSecret string) (bool, error)
}
@ -29,6 +31,17 @@ func GetCaptchaProvider(captchaType string) CaptchaProvider {
return NewAliyunCaptchaProvider()
} else if captchaType == "GEETEST" {
return NewGEETESTCaptchaProvider()
} else if captchaType == "Cloudflare Turnstile" {
return NewCloudflareTurnstileProvider()
}
return nil
}
func VerifyCaptchaByCaptchaType(captchaType, token, clientSecret string) (bool, error) {
provider := GetCaptchaProvider(captchaType)
if provider == nil {
return false, fmt.Errorf("invalid captcha provider: %s", captchaType)
}
return provider.VerifyCaptcha(token, clientSecret)
}

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

@ -18,3 +18,7 @@ logPostOnly = true
origin =
staticBaseUrl = "https://cdn.casbin.org"
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,15 +15,49 @@
package conf
import (
"encoding/json"
"fmt"
"os"
"runtime"
"strconv"
"strings"
"github.com/astaxie/beego"
"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"}
for _, key := range presetConfigItems {
if value, ok := os.LookupEnv(key); ok {
err := beego.AppConfig.Set(key, value)
if err != nil {
panic(err)
}
}
}
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 {
if value, ok := os.LookupEnv(key); ok {
return value
@ -55,17 +89,7 @@ func GetConfigInt64(key string) (int64, error) {
return num, err
}
func init() {
// this array contains the beego configuration items that may be modified via env
presetConfigItems := []string{"httpport", "appname"}
for _, key := range presetConfigItems {
if value, ok := os.LookupEnv(key); ok {
beego.AppConfig.Set(key, value)
}
}
}
func GetBeegoConfDataSourceName() string {
func GetConfigDataSourceName() string {
dataSourceName := GetConfigString("dataSourceName")
runningInDocker := os.Getenv("RUNNING_IN_DOCKER")
@ -84,3 +108,25 @@ func GetBeegoConfDataSourceName() string {
func IsDemoMode() bool {
return strings.ToLower(GetConfigString("isDemoMode")) == "true"
}
func GetConfigBatchSize() int {
res, err := strconv.Atoi(GetConfigString("batchSize"))
if err != nil {
res = 100
}
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

@ -18,7 +18,7 @@ import (
"os"
"testing"
"github.com/astaxie/beego"
"github.com/beego/beego"
"github.com/stretchr/testify/assert"
)
@ -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

@ -64,6 +64,10 @@ type RequestForm struct {
RelayState string `json:"relayState"`
SamlRequest string `json:"samlRequest"`
SamlResponse string `json:"samlResponse"`
CaptchaType string `json:"captchaType"`
CaptchaToken string `json:"captchaToken"`
ClientSecret string `json:"clientSecret"`
}
type Response struct {
@ -98,7 +102,7 @@ type Captcha struct {
// @router /signup [post]
func (c *ApiController) Signup() {
if c.GetSessionUsername() != "" {
c.ResponseError("Please sign out first before signing up", c.GetSessionUsername())
c.ResponseError(c.T("account:Please sign out first before signing up"), c.GetSessionUsername())
return
}
@ -111,21 +115,21 @@ func (c *ApiController) Signup() {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if !application.EnableSignUp {
c.ResponseError("The application does not allow to sign up new account")
c.ResponseError(c.T("account:The application does not allow to sign up new account"))
return
}
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", form.Organization))
msg := object.CheckUserSignup(application, organization, form.Username, form.Password, form.Name, form.FirstName, form.LastName, form.Email, form.Phone, form.Affiliation)
msg := object.CheckUserSignup(application, organization, form.Username, form.Password, form.Name, form.FirstName, form.LastName, form.Email, form.Phone, form.Affiliation, c.GetAcceptLanguage())
if msg != "" {
c.ResponseError(msg)
return
}
if application.IsSignupItemVisible("Email") && application.GetSignupItemRule("Email") != "No verification" && form.Email != "" {
checkResult := object.CheckVerificationCode(form.Email, form.EmailCode)
checkResult := object.CheckVerificationCode(form.Email, form.EmailCode, c.GetAcceptLanguage())
if len(checkResult) != 0 {
c.ResponseError(fmt.Sprintf("Email: %s", checkResult))
c.ResponseError(c.T("account:Email: %s"), checkResult)
return
}
}
@ -133,9 +137,9 @@ func (c *ApiController) Signup() {
var checkPhone string
if application.IsSignupItemVisible("Phone") && form.Phone != "" {
checkPhone = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Phone)
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode)
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode, c.GetAcceptLanguage())
if len(checkResult) != 0 {
c.ResponseError(fmt.Sprintf("Phone: %s", checkResult))
c.ResponseError(c.T("account:Phone: %s"), checkResult)
return
}
}
@ -157,9 +161,9 @@ func (c *ApiController) Signup() {
username = id
}
initScore, err := getInitScore()
initScore, err := getInitScore(organization)
if err != nil {
c.ResponseError(fmt.Errorf("get init score failed, error: %w", err).Error())
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
return
}
@ -205,7 +209,7 @@ func (c *ApiController) Signup() {
affected := object.AddUser(user)
if !affected {
c.ResponseError(fmt.Sprintf("Failed to create user, user information is invalid: %s", util.StructToJson(user)))
c.ResponseError(c.T("account:Invalid information"), util.StructToJson(user))
return
}
@ -241,8 +245,7 @@ func (c *ApiController) Logout() {
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
application := c.GetSessionApplication()
c.SetSessionUsername("")
c.SetSessionData(nil)
c.ClearUserSession()
if application == nil || application.Name == "app-built-in" || application.HomepageUrl == "" {
c.ResponseOk(user)
@ -258,17 +261,11 @@ func (c *ApiController) Logout() {
// @Success 200 {object} controllers.Response The Response object
// @router /get-account [get]
func (c *ApiController) GetAccount() {
userId, ok := c.RequireSignedIn()
user, ok := c.RequireSignedInUser()
if !ok {
return
}
user := object.GetUser(userId)
if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s doesn't exist", userId))
return
}
managedAccounts := c.Input().Get("managedAccounts")
if managedAccounts == "1" {
user = object.ExtendManagedAccountsWithUser(user)
@ -294,18 +291,16 @@ func (c *ApiController) GetAccount() {
// @Success 200 {object} object.Userinfo The Response object
// @router /userinfo [get]
func (c *ApiController) GetUserinfo() {
userId, ok := c.RequireSignedIn()
user, ok := c.RequireSignedInUser()
if !ok {
return
}
scope, aud := c.GetSessionOidc()
host := c.Ctx.Request.Host
resp, err := object.GetUserInfo(userId, scope, aud, host)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = resp
userInfo := object.GetUserInfo(user, scope, aud, host)
c.Data["json"] = userInfo
c.ServeJSON()
}
@ -317,7 +312,7 @@ func (c *ApiController) GetCaptcha() {
applicationId := c.Input().Get("applicationId")
isCurrentProvider := c.Input().Get("isCurrentProvider")
captchaProvider, err := object.GetCaptchaProviderByApplication(applicationId, isCurrentProvider)
captchaProvider, err := object.GetCaptchaProviderByApplication(applicationId, isCurrentProvider, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return

View File

@ -18,7 +18,7 @@ import (
"encoding/json"
"fmt"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -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)
@ -86,7 +86,7 @@ func (c *ApiController) GetUserApplication() {
id := c.Input().Get("id")
user := object.GetUser(id)
if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s doesn't exist", id))
c.ResponseError(fmt.Sprintf(c.T("application:The user: %s doesn't exist"), id))
return
}
@ -103,17 +103,31 @@ 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("Parameter organization is missing")
c.ResponseError(c.T("application:Parameter organization is missing"))
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
@ -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

@ -17,12 +17,16 @@ package controllers
import (
"encoding/base64"
"encoding/json"
"encoding/xml"
"fmt"
"io/ioutil"
"net/url"
"strconv"
"strings"
"sync"
"time"
"github.com/casdoor/casdoor/captcha"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/idp"
"github.com/casdoor/casdoor/object"
@ -31,6 +35,11 @@ import (
"github.com/google/uuid"
)
var (
wechatScanType string
lock sync.RWMutex
)
func codeToResponse(code *object.Code) *Response {
if code.Code == "" {
return &Response{Status: "error", Msg: code.Message, Data: code.Code}
@ -56,7 +65,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
return
}
if !allowed {
c.ResponseError("Unauthorized operation")
c.ResponseError(c.T("auth:Unauthorized operation"))
return
}
@ -75,10 +84,10 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
codeChallenge := c.Input().Get("code_challenge")
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
c.ResponseError("Challenge method should be S256")
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)
code := object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce, codeChallenge, c.Ctx.Request.Host, c.GetAcceptLanguage())
resp = codeToResponse(code)
if application.EnableSigninSession || application.HasPromptPage() {
@ -151,7 +160,7 @@ func (c *ApiController) GetApplicationLogin() {
scope := c.Input().Get("scope")
state := c.Input().Get("state")
msg, application := object.CheckOAuthLogin(clientId, responseType, redirectUri, scope, state)
msg, application := object.CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, c.GetAcceptLanguage())
application = object.GetMaskedApplication(application, "")
if msg != "" {
c.ResponseError(msg, application)
@ -161,7 +170,7 @@ func (c *ApiController) GetApplicationLogin() {
}
func setHttpClient(idProvider idp.IdProvider, providerType string) {
if providerType == "GitHub" || providerType == "Google" || providerType == "Facebook" || providerType == "LinkedIn" || providerType == "Steam" {
if providerType == "GitHub" || providerType == "Google" || providerType == "Facebook" || providerType == "LinkedIn" || providerType == "Steam" || providerType == "Line" {
idProvider.SetHttpClient(proxy.ProxyHttpClient)
} else {
idProvider.SetHttpClient(proxy.DefaultHttpClient)
@ -196,7 +205,7 @@ func (c *ApiController) Login() {
if form.Username != "" {
if form.Type == ResponseTypeLogin {
if c.GetSessionUsername() != "" {
c.ResponseError("Please sign out first before signing in", c.GetSessionUsername())
c.ResponseError(c.T("auth:Please sign out first before signing in"), c.GetSessionUsername())
return
}
}
@ -218,11 +227,11 @@ func (c *ApiController) Login() {
if user != nil && util.GetMaskedEmail(user.Email) == form.Username {
form.Username = user.Email
}
checkResult = object.CheckVerificationCode(form.Username, form.Code)
checkResult = object.CheckVerificationCode(form.Username, form.Code, c.GetAcceptLanguage())
} else {
verificationCodeType = "phone"
if len(form.PhonePrefix) == 0 {
responseText := fmt.Sprintf("%s%s", verificationCodeType, "No phone prefix")
responseText := fmt.Sprintf(c.T("auth:%s No phone prefix"), verificationCodeType)
c.ResponseError(responseText)
return
}
@ -230,7 +239,7 @@ func (c *ApiController) Login() {
form.Username = user.Phone
}
checkPhone := fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)
checkResult = object.CheckVerificationCode(checkPhone, form.Code)
checkResult = object.CheckVerificationCode(checkPhone, form.Code, c.GetAcceptLanguage())
}
if len(checkResult) != 0 {
responseText := fmt.Sprintf("%s%s", verificationCodeType, checkResult)
@ -247,12 +256,31 @@ func (c *ApiController) Login() {
user = object.GetUserByFields(form.Organization, form.Username)
if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s/%s doesn't exist", form.Organization, form.Username))
c.ResponseError(fmt.Sprintf(c.T("auth:The user: %s/%s doesn't exist"), form.Organization, form.Username))
return
}
} else {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
return
}
if object.CheckToEnableCaptcha(application) {
isHuman, err := captcha.VerifyCaptchaByCaptchaType(form.CaptchaType, form.CaptchaToken, form.ClientSecret)
if err != nil {
c.ResponseError(err.Error())
return
}
if !isHuman {
c.ResponseError(c.T("auth:Turing test failed."))
return
}
}
password := form.Password
user, msg = object.CheckUserPassword(form.Organization, form.Username, password)
user, msg = object.CheckUserPassword(form.Organization, form.Username, password, c.GetAcceptLanguage())
}
if msg != "" {
@ -260,7 +288,7 @@ func (c *ApiController) Login() {
} 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
}
@ -274,15 +302,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("The application: %s does not exist", 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("The provider: %s is not enabled for the application", provider.Name))
c.ResponseError(fmt.Sprintf(c.T("auth:The provider: %s is not enabled for the application"), provider.Name))
return
}
@ -306,14 +334,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("The provider type: %s is not supported", 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("state expected: \"%s\", but got: \"%s\"", conf.GetConfigString("authState"), form.State))
c.ResponseError(fmt.Sprintf(c.T("auth:State expected: %s, but got: %s"), conf.GetConfigString("authState"), form.State))
return
}
@ -325,13 +353,13 @@ func (c *ApiController) Login() {
}
if !token.Valid() {
c.ResponseError("Invalid token")
c.ResponseError(c.T("auth:Invalid token"))
return
}
userInfo, err = idProvider.GetUserInfo(token)
if err != nil {
c.ResponseError(fmt.Sprintf("Failed to login in: %s", err.Error()))
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to login in: %s"), err.Error()))
return
}
}
@ -348,7 +376,7 @@ func (c *ApiController) Login() {
// Sign in via OAuth (want to sign up but already have account)
if user.IsForbidden {
c.ResponseError("the user is forbidden to sign in, please contact the administrator")
c.ResponseError(c.T("auth:The user is forbidden to sign in, please contact the administrator"))
}
resp = c.HandleLoggedIn(application, user, &form)
@ -360,12 +388,12 @@ func (c *ApiController) Login() {
} else if provider.Category == "OAuth" {
// Sign up via OAuth
if !application.EnableSignUp {
c.ResponseError(fmt.Sprintf("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))
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("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))
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
}
@ -384,9 +412,9 @@ func (c *ApiController) Login() {
properties := map[string]string{}
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
initScore, err := getInitScore()
initScore, err := getInitScore(organization)
if err != nil {
c.ResponseError(fmt.Errorf("get init score failed, error: %w", err).Error())
c.ResponseError(fmt.Errorf(c.T("auth:Get init score failed, error: %w"), err).Error())
return
}
@ -413,7 +441,7 @@ func (c *ApiController) Login() {
affected := object.AddUser(user)
if !affected {
c.ResponseError(fmt.Sprintf("Failed to create user, user information is invalid: %s", util.StructToJson(user)))
c.ResponseError(fmt.Sprintf(c.T("auth:Failed to create user, user information is invalid: %s"), util.StructToJson(user)))
return
}
@ -438,13 +466,13 @@ func (c *ApiController) Login() {
} else { // form.Method != "signup"
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError("The account does not exist", userInfo)
c.ResponseError(c.T("auth:The account does not exist"), userInfo)
return
}
oldUser := object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
if oldUser != nil {
c.ResponseError(fmt.Sprintf("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))
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
}
@ -465,7 +493,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("The application: %s does not exist", form.Application))
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), form.Application))
return
}
@ -477,7 +505,7 @@ func (c *ApiController) Login() {
record.User = user.Name
util.SafeGoroutine(func() { object.AddRecord(record) })
} else {
c.ResponseError(fmt.Sprintf("unknown authentication type (not password or provider), form = %s", util.StructToJson(form)))
c.ResponseError(fmt.Sprintf(c.T("auth:Unknown authentication type (not password or provider), form = %s"), util.StructToJson(form)))
return
}
}
@ -489,7 +517,7 @@ func (c *ApiController) Login() {
func (c *ApiController) GetSamlLogin() {
providerId := c.Input().Get("id")
relayState := c.Input().Get("relayState")
authURL, method, err := object.GenerateSamlLoginUrl(providerId, relayState)
authURL, method, err := object.GenerateSamlLoginUrl(providerId, relayState, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
}
@ -510,3 +538,46 @@ func (c *ApiController) HandleSamlLogin() {
slice[4], relayState, samlResponse)
c.Redirect(targetUrl, 303)
}
// HandleOfficialAccountEvent ...
// @Tag HandleOfficialAccountEvent API
// @Title HandleOfficialAccountEvent
// @router /api/webhook [POST]
func (c *ApiController) HandleOfficialAccountEvent() {
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
if err != nil {
c.ResponseError(err.Error())
}
var data struct {
MsgType string `xml:"MsgType"`
Event string `xml:"Event"`
EventKey string `xml:"EventKey"`
}
err = xml.Unmarshal(respBytes, &data)
if err != nil {
c.ResponseError(err.Error())
}
lock.Lock()
defer lock.Unlock()
if data.EventKey != "" {
wechatScanType = data.Event
c.Ctx.WriteString("")
}
}
// GetWebhookEventType ...
// @Tag GetWebhookEventType API
// @Title GetWebhookEventType
// @router /api/get-webhook-event [GET]
func (c *ApiController) GetWebhookEventType() {
lock.Lock()
defer lock.Unlock()
resp := &Response{
Status: "ok",
Msg: "",
Data: wechatScanType,
}
c.Data["json"] = resp
wechatScanType = ""
c.ServeJSON()
}

View File

@ -18,8 +18,8 @@ import (
"strings"
"time"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/beego/beego"
"github.com/beego/beego/logs"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -63,8 +63,7 @@ func (c *ApiController) GetSessionUsername() string {
if sessionData != nil &&
sessionData.ExpireTime != 0 &&
sessionData.ExpireTime < time.Now().Unix() {
c.SetSessionUsername("")
c.SetSessionData(nil)
c.ClearUserSession()
return ""
}
@ -85,13 +84,17 @@ func (c *ApiController) GetSessionApplication() *object.Application {
return application
}
func (c *ApiController) ClearUserSession() {
c.SetSessionUsername("")
c.SetSessionData(nil)
}
func (c *ApiController) GetSessionOidc() (string, string) {
sessionData := c.GetSessionData()
if sessionData != nil &&
sessionData.ExpireTime != 0 &&
sessionData.ExpireTime < time.Now().Unix() {
c.SetSessionUsername("")
c.SetSessionData(nil)
c.ClearUserSession()
return "", ""
}
scopeValue := c.GetSession("scope")

View File

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

View File

@ -0,0 +1,157 @@
// 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"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
func (c *ApiController) GetCasbinAdapters() {
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 limit == "" || page == "" {
adapters := object.GetCasbinAdapters(owner)
c.ResponseOk(adapters)
} else {
limit := util.ParseInt(limit)
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetCasbinAdapterCount(owner, field, value)))
adapters := object.GetPaginationCasbinAdapters(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
c.ResponseOk(adapters, paginator.Nums())
}
}
func (c *ApiController) GetCasbinAdapter() {
id := c.Input().Get("id")
adapter := object.GetCasbinAdapter(id)
c.ResponseOk(adapter)
}
func (c *ApiController) UpdateCasbinAdapter() {
id := c.Input().Get("id")
var casbinAdapter object.CasbinAdapter
err := json.Unmarshal(c.Ctx.Input.RequestBody, &casbinAdapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateCasbinAdapter(id, &casbinAdapter))
c.ServeJSON()
}
func (c *ApiController) AddCasbinAdapter() {
var casbinAdapter object.CasbinAdapter
err := json.Unmarshal(c.Ctx.Input.RequestBody, &casbinAdapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddCasbinAdapter(&casbinAdapter))
c.ServeJSON()
}
func (c *ApiController) DeleteCasbinAdapter() {
var casbinAdapter object.CasbinAdapter
err := json.Unmarshal(c.Ctx.Input.RequestBody, &casbinAdapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteCasbinAdapter(&casbinAdapter))
c.ServeJSON()
}
func (c *ApiController) SyncPolicies() {
id := c.Input().Get("id")
adapter := object.GetCasbinAdapter(id)
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

@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)

View File

@ -21,12 +21,6 @@ import (
)
func (c *ApiController) Enforce() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError("Please sign in first")
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("Please sign in first")
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("Please sign in first")
c.ResponseError(c.T("enforcer:Please sign in first"))
return
}
@ -70,7 +58,7 @@ func (c *ApiController) GetAllObjects() {
func (c *ApiController) GetAllActions() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError("Please sign in first")
c.ResponseError(c.T("enforcer:Please sign in first"))
return
}
@ -81,7 +69,7 @@ func (c *ApiController) GetAllActions() {
func (c *ApiController) GetAllRoles() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError("Please sign in first")
c.ResponseError(c.T("enforcer:Please sign in 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("Missing parameter")
c.ResponseError(c.T("ldap:Missing parameter"))
return
}
@ -120,7 +120,7 @@ func (c *ApiController) GetLdap() {
id := c.Input().Get("id")
if util.IsStrsEmpty(id) {
c.ResponseError("Missing parameter")
c.ResponseError(c.T("ldap: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("Missing parameter")
c.ResponseError(c.T("ldap:Missing parameter"))
return
}
if util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
c.ResponseError("Missing parameter")
c.ResponseError(c.T("ldap:Missing parameter"))
return
}
if object.CheckLdapExist(&ldap) {
c.ResponseError("Ldap server exist")
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("Missing parameter")
c.ResponseError(c.T("ldap:Missing parameter"))
return
}

118
controllers/ldapserver.go Normal file
View File

@ -0,0 +1,118 @@
// 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 (
"fmt"
"log"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object"
"github.com/forestmgy/ldapserver"
"github.com/lor00x/goldap/message"
)
func StartLdapServer() {
server := ldapserver.NewServer()
routes := ldapserver.NewRouteMux()
routes.Bind(handleBind)
routes.Search(handleSearch).Label(" SEARCH****")
server.Handle(routes)
server.ListenAndServe("0.0.0.0:" + conf.GetConfigString("ldapServerPort"))
}
func handleBind(w ldapserver.ResponseWriter, m *ldapserver.Message) {
r := m.GetBindRequest()
res := ldapserver.NewBindResponse(ldapserver.LDAPResultSuccess)
if r.AuthenticationChoice() == "simple" {
bindusername, bindorg, err := object.GetNameAndOrgFromDN(string(r.Name()))
if err != "" {
log.Printf("Bind failed ,ErrMsg=%s", err)
res.SetResultCode(ldapserver.LDAPResultInvalidDNSyntax)
res.SetDiagnosticMessage("bind failed ErrMsg: " + err)
w.Write(res)
return
}
bindpassword := string(r.AuthenticationSimple())
binduser, err := object.CheckUserPassword(bindorg, bindusername, bindpassword, "en")
if err != "" {
log.Printf("Bind failed User=%s, Pass=%#v, ErrMsg=%s", string(r.Name()), r.Authentication(), err)
res.SetResultCode(ldapserver.LDAPResultInvalidCredentials)
res.SetDiagnosticMessage("invalid credentials ErrMsg: " + err)
w.Write(res)
return
}
if bindorg == "built-in" {
m.Client.IsGlobalAdmin, m.Client.IsOrgAdmin = true, true
} else if binduser.IsAdmin {
m.Client.IsOrgAdmin = true
}
m.Client.IsAuthenticated = true
m.Client.UserName = bindusername
m.Client.OrgName = bindorg
} else {
res.SetResultCode(ldapserver.LDAPResultAuthMethodNotSupported)
res.SetDiagnosticMessage("Authentication method not supported,Please use Simple Authentication")
}
w.Write(res)
}
func handleSearch(w ldapserver.ResponseWriter, m *ldapserver.Message) {
res := ldapserver.NewSearchResultDoneResponse(ldapserver.LDAPResultSuccess)
if !m.Client.IsAuthenticated {
res.SetResultCode(ldapserver.LDAPResultUnwillingToPerform)
w.Write(res)
return
}
r := m.GetSearchRequest()
if r.FilterString() == "(objectClass=*)" {
w.Write(res)
return
}
name, org, errCode := object.GetUserNameAndOrgFromBaseDnAndFilter(string(r.BaseObject()), r.FilterString())
if errCode != ldapserver.LDAPResultSuccess {
res.SetResultCode(errCode)
w.Write(res)
return
}
// Handle Stop Signal (server stop / client disconnected / Abandoned request....)
select {
case <-m.Done:
log.Print("Leaving handleSearch...")
return
default:
}
users, errCode := object.GetFilteredUsers(m, name, org)
if errCode != ldapserver.LDAPResultSuccess {
res.SetResultCode(errCode)
w.Write(res)
return
}
for i := 0; i < len(users); i++ {
user := users[i]
dn := fmt.Sprintf("cn=%s,%s", user.DisplayName, 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("postalAddress", message.AttributeValue(user.Address[0]))
w.Write(e)
}
w.Write(res)
}

View File

@ -29,7 +29,7 @@ type LinkForm struct {
// @router /unlink [post]
// @Tag Login API
func (c *ApiController) Unlink() {
userId, ok := c.RequireSignedIn()
user, ok := c.RequireSignedInUser()
if !ok {
return
}
@ -44,11 +44,10 @@ func (c *ApiController) Unlink() {
// the user will be unlinked from the provider
unlinkedUser := form.User
user := object.GetUser(userId)
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("You are not the global admin, you can't unlink other users")
c.ResponseError(c.T("link:You are not the global admin, you can't unlink other users"))
return
}
@ -56,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("You can't unlink yourself, you are not a member of any application")
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("This application has no providers")
c.ResponseError(c.T("link:This application has no providers"))
return
}
provider := application.GetProviderItemByType(providerType)
if provider == nil {
c.ResponseError("This application has no providers of type " + providerType)
c.ResponseError(c.T("link:This application has no providers of type") + providerType)
return
}
if !provider.CanUnlink {
c.ResponseError("This provider can't be unlinked")
c.ResponseError(c.T("link:This provider can't be unlinked"))
return
}
@ -85,7 +84,7 @@ func (c *ApiController) Unlink() {
value := object.GetUserField(&unlinkedUser, providerType)
if value == "" {
c.ResponseError("Please link first", value)
c.ResponseError(c.T("link:Please link first"), value)
return
}

View File

@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)

View File

@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -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()
}
@ -121,3 +127,24 @@ func (c *ApiController) DeleteOrganization() {
c.Data["json"] = wrapActionResponse(object.DeleteOrganization(&organization))
c.ServeJSON()
}
// GetDefaultApplication ...
// @Title GetDefaultApplication
// @Tag Organization API
// @Description get default application
// @Param id query string true "organization id"
// @Success 200 {object} Response The Response object
// @router /get-default-application [get]
func (c *ApiController) GetDefaultApplication() {
userId := c.GetSessionUsername()
id := c.Input().Get("id")
application, err := object.GetDefaultApplication(id)
if err != nil {
c.ResponseError(err.Error())
return
}
maskedApplication := object.GetMaskedApplication(application, userId)
c.ResponseOk(maskedApplication)
}

View File

@ -18,7 +18,7 @@ import (
"encoding/json"
"fmt"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)

View File

@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -55,13 +55,26 @@ func (c *ApiController) GetPermissions() {
// @Success 200 {array} object.Permission The Response object
// @router /get-permissions-by-submitter [get]
func (c *ApiController) GetPermissionsBySubmitter() {
userId, ok := c.RequireSignedIn()
user, ok := c.RequireSignedInUser()
if !ok {
return
}
owner, username := util.GetOwnerAndNameFromId(userId)
permissions := object.GetPermissionsBySubmitter(owner, username)
permissions := object.GetPermissionsBySubmitter(user.Owner, user.Name)
c.ResponseOk(permissions, len(permissions))
return
}
// GetPermissionsByRole
// @Title GetPermissionsByRole
// @Tag Permission API
// @Description get permissions by role
// @Param id query string true "The id 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
}

View File

@ -18,7 +18,7 @@ import (
"encoding/json"
"fmt"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -141,13 +141,13 @@ func (c *ApiController) BuyProduct() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError("Please login first")
c.ResponseError(c.T("product:Please login first"))
return
}
user := object.GetUser(userId)
if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s doesn't exist", userId))
c.ResponseError(fmt.Sprintf(c.T("product:The user: %s doesn't exist"), userId))
return
}

View File

@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -48,6 +48,30 @@ func (c *ApiController) GetProviders() {
}
}
// GetGlobalProviders
// @Title GetGlobalProviders
// @Tag Provider API
// @Description get Global providers
// @Success 200 {array} object.Provider The Response object
// @router /get-global-providers [get]
func (c *ApiController) GetGlobalProviders() {
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 limit == "" || page == "" {
c.Data["json"] = object.GetMaskedProviders(object.GetGlobalProviders())
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetGlobalProviderCount(field, value)))
providers := object.GetMaskedProviders(object.GetPaginationGlobalProviders(paginator.Offset(), limit, field, value, sortField, sortOrder))
c.ResponseOk(providers, paginator.Nums())
}
}
// GetProvider
// @Title GetProvider
// @Tag Provider API
@ -57,7 +81,6 @@ func (c *ApiController) GetProviders() {
// @router /get-provider [get]
func (c *ApiController) GetProvider() {
id := c.Input().Get("id")
c.Data["json"] = object.GetMaskedProvider(object.GetProvider(id))
c.ServeJSON()
}
@ -99,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

@ -15,7 +15,9 @@
package controllers
import (
"github.com/astaxie/beego/utils/pagination"
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -29,6 +31,11 @@ import (
// @Success 200 {object} object.Record The Response object
// @router /get-records [get]
func (c *ApiController) GetRecords() {
organization, ok := c.RequireAdmin()
if !ok {
return
}
limit := c.Input().Get("pageSize")
page := c.Input().Get("p")
field := c.Input().Get("field")
@ -40,8 +47,9 @@ func (c *ApiController) GetRecords() {
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetRecordCount(field, value)))
records := object.GetPaginationRecords(paginator.Offset(), limit, field, value, sortField, sortOrder)
filterRecord := &object.Record{Organization: organization}
paginator := pagination.SetPaginator(c.Ctx, limit, int64(object.GetRecordCount(field, value, filterRecord)))
records := object.GetPaginationRecords(paginator.Offset(), limit, field, value, sortField, sortOrder, filterRecord)
c.ResponseOk(records, paginator.Nums())
}
}
@ -66,3 +74,22 @@ func (c *ApiController) GetRecordsByFilter() {
c.Data["json"] = object.GetRecordsByField(record)
c.ServeJSON()
}
// AddRecord
// @Title AddRecord
// @Tag Record API
// @Description add a record
// @Param body body object.Record true "The details of the record"
// @Success 200 {object} controllers.Response The Response object
// @router /add-record [post]
func (c *ApiController) AddRecord() {
var record object.Record
err := json.Unmarshal(c.Ctx.Input.RequestBody, &record)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddRecord(&record))
c.ServeJSON()
}

View File

@ -22,7 +22,7 @@ import (
"mime"
"path/filepath"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -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()
@ -113,7 +122,7 @@ func (c *ApiController) DeleteResource() {
return
}
err = object.DeleteFile(provider, resource.Name)
err = object.DeleteFile(provider, resource.Name, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
@ -145,7 +154,7 @@ func (c *ApiController) UploadResource() {
defer file.Close()
if username == "" || fullFilePath == "" {
c.ResponseError(fmt.Sprintf("username or fullFilePath is empty: username = %s, fullFilePath = %s", 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("user is nil for tag: \"avatar\"")
return
}
c.ResponseError(c.T("resource:User is nil for tag: avatar"))
return
}
user.Avatar = fileUrl

View File

@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)

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("err: application %s not found", 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("Empty parameters for emailForm: %v", 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("Invalid Email receivers: %s", 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("Invalid phone receivers: %s", invalidReceivers))
c.ResponseError(fmt.Sprintf(c.T("service:Invalid phone receivers: %s"), invalidReceivers))
return
}

View File

@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)

View File

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

View File

@ -18,7 +18,7 @@ import (
"encoding/json"
"net/http"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -150,12 +150,12 @@ func (c *ApiController) GetOAuthCode() {
codeChallenge := c.Input().Get("code_challenge")
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
c.ResponseError("Challenge method should be S256")
c.ResponseError(c.T("token:Challenge method should be S256"))
return
}
host := c.Ctx.Request.Host
c.Data["json"] = object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce, codeChallenge, host)
c.Data["json"] = object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce, codeChallenge, host, c.GetAcceptLanguage())
c.ServeJSON()
}
@ -204,7 +204,7 @@ func (c *ApiController) GetOAuthToken() {
}
host := c.Ctx.Request.Host
c.Data["json"] = object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, tag, avatar)
c.Data["json"] = object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, tag, avatar, c.GetAcceptLanguage())
c.SetTokenErrorHttpStatus()
c.ServeJSON()
}
@ -261,7 +261,7 @@ func (c *ApiController) TokenLogout() {
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) {
if application != nil && application.IsRedirectUriValid(redirectUri) {
c.Ctx.Redirect(http.StatusFound, redirectUri+"?state="+state)
return
}
@ -290,7 +290,7 @@ func (c *ApiController) IntrospectToken() {
clientId = c.Input().Get("client_id")
clientSecret = c.Input().Get("client_secret")
if clientId == "" || clientSecret == "" {
c.ResponseError("empty clientId or clientSecret")
c.ResponseError(c.T("token:Empty clientId or clientSecret"))
c.Data["json"] = &object.TokenError{
Error: object.InvalidRequest,
}
@ -301,7 +301,7 @@ func (c *ApiController) IntrospectToken() {
}
application := object.GetApplicationByClientId(clientId)
if application == nil || application.ClientSecret != clientSecret {
c.ResponseError("invalid application or wrong clientSecret")
c.ResponseError(c.T("token:Invalid application or wrong clientSecret"))
c.Data["json"] = &object.TokenError{
Error: object.InvalidClient,
}

View File

@ -19,7 +19,7 @@ import (
"fmt"
"strings"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -100,7 +100,7 @@ func (c *ApiController) GetUser() {
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", owner))
if !organization.IsProfilePublic {
requestUserId := c.GetSessionUsername()
hasPermission, err := object.CheckUserPermission(requestUserId, id, owner, false)
hasPermission, err := object.CheckUserPermission(requestUserId, id, owner, false, c.GetAcceptLanguage())
if !hasPermission {
c.ResponseError(err.Error())
return
@ -149,7 +149,7 @@ func (c *ApiController) UpdateUser() {
}
if user.DisplayName == "" {
c.ResponseError("Display name cannot be empty")
c.ResponseError(c.T("user:Display name cannot be empty"))
return
}
@ -183,6 +183,18 @@ 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)
return
}
c.Data["json"] = wrapActionResponse(object.AddUser(&user))
c.ServeJSON()
}
@ -224,7 +236,7 @@ func (c *ApiController) GetEmailAndPhone() {
user := object.GetUserByFields(form.Organization, form.Username)
if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s/%s doesn't exist", form.Organization, form.Username))
c.ResponseError(fmt.Sprintf(c.T("user:The user: %s/%s doesn't exist"), form.Organization, form.Username))
return
}
@ -265,7 +277,7 @@ func (c *ApiController) SetPassword() {
requestUserId := c.GetSessionUsername()
userId := fmt.Sprintf("%s/%s", userOwner, userName)
hasPermission, err := object.CheckUserPermission(requestUserId, userId, userOwner, true)
hasPermission, err := object.CheckUserPermission(requestUserId, userId, userOwner, true, c.GetAcceptLanguage())
if !hasPermission {
c.ResponseError(err.Error())
return
@ -274,7 +286,7 @@ func (c *ApiController) SetPassword() {
targetUser := object.GetUser(userId)
if oldPassword != "" {
msg := object.CheckPassword(targetUser, oldPassword)
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
if msg != "" {
c.ResponseError(msg)
return
@ -282,12 +294,12 @@ func (c *ApiController) SetPassword() {
}
if strings.Contains(newPassword, " ") {
c.ResponseError("New password cannot contain blank space.")
c.ResponseError(c.T("user:New password cannot contain blank space."))
return
}
if len(newPassword) <= 5 {
c.ResponseError("New password must have at least 6 characters")
c.ResponseError(c.T("user:New password must have at least 6 characters"))
return
}
@ -309,7 +321,7 @@ func (c *ApiController) CheckUserPassword() {
return
}
_, msg := object.CheckUserPassword(user.Owner, user.Name, user.Password)
_, msg := object.CheckUserPassword(user.Owner, user.Name, user.Password, c.GetAcceptLanguage())
if msg == "" {
c.ResponseOk()
} else {

View File

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

View File

@ -17,8 +17,10 @@ package controllers
import (
"fmt"
"strconv"
"strings"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
@ -48,6 +50,19 @@ func (c *ApiController) ResponseError(error string, data ...interface{}) {
c.ResponseJsonData(resp, data...)
}
func (c *ApiController) T(error string) string {
return i18n.Translate(c.GetAcceptLanguage(), error)
}
// GetAcceptLanguage ...
func (c *ApiController) GetAcceptLanguage() string {
lang := c.Ctx.Request.Header.Get("Accept-Language")
if lang == "" || !strings.Contains(conf.GetConfigString("languages"), lang[0:2]) {
lang = "en"
}
return lang[0:2]
}
// SetTokenErrorHttpStatus ...
func (c *ApiController) SetTokenErrorHttpStatus() {
_, ok := c.Data["json"].(*object.TokenError)
@ -69,22 +84,55 @@ func (c *ApiController) SetTokenErrorHttpStatus() {
func (c *ApiController) RequireSignedIn() (string, bool) {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError("Please sign in first")
c.ResponseError(c.T("util:Please login first"), "Please login first")
return "", false
}
return userId, true
}
func getInitScore() (int, error) {
return strconv.Atoi(conf.GetConfigString("initScore"))
// RequireSignedInUser ...
func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
userId, ok := c.RequireSignedIn()
if !ok {
return nil, false
}
user := object.GetUser(userId)
if user == nil {
c.ClearUserSession()
c.ResponseError(fmt.Sprintf(c.T("util:The user: %s doesn't exist"), userId))
return nil, false
}
return user, true
}
// RequireAdmin ...
func (c *ApiController) RequireAdmin() (string, bool) {
user, ok := c.RequireSignedInUser()
if !ok {
return "", false
}
if user.Owner == "built-in" {
return "", true
}
return user.Owner, true
}
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(fmt.Sprintf("The provider: %s is not found", providerName))
c.ResponseError(c.T("util:The provider: %s is not found"), providerName)
return nil, nil, false
}
return provider, nil, true
@ -97,15 +145,59 @@ func (c *ApiController) GetProviderFromContext(category string) (*object.Provide
application, user := object.GetApplicationByUserId(userId)
if application == nil {
c.ResponseError(fmt.Sprintf("No application is found for userId: \"%s\"", 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("No provider for category: \"%s\" is found for application: %s", 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("Missing parameter: type.")
c.ResponseError(c.T("verification:Missing parameter") + ": type.")
return
}
if dest == "" {
c.ResponseError("Missing parameter: dest.")
c.ResponseError(c.T("verification:Missing parameter") + ": dest.")
return
}
if applicationId == "" {
c.ResponseError("Missing parameter: applicationId.")
c.ResponseError(c.T("verification:Missing parameter") + ": applicationId.")
return
}
if !strings.Contains(applicationId, "/") {
c.ResponseError("Wrong parameter: applicationId.")
c.ResponseError(c.T("verification:Wrong parameter") + ": applicationId.")
return
}
if checkType == "" {
c.ResponseError("Missing parameter: checkType.")
c.ResponseError(c.T("verification:Missing parameter") + ": checkType.")
return
}
@ -74,7 +75,7 @@ func (c *ApiController) SendVerificationCode() {
if captchaProvider != nil {
if checkKey == "" {
c.ResponseError("Missing parameter: checkKey.")
c.ResponseError(c.T("verification:Missing parameter") + ": checkKey.")
return
}
isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId)
@ -84,7 +85,7 @@ func (c *ApiController) SendVerificationCode() {
}
if !isHuman {
c.ResponseError("Turing test failed.")
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("Please login first")
c.ResponseError(c.T("verification:Please login first"))
return
}
@ -110,7 +115,13 @@ func (c *ApiController) SendVerificationCode() {
dest = user.Email
}
if !util.IsEmailValid(dest) {
c.ResponseError("Invalid Email address")
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("Invalid phone number")
c.ResponseError(c.T("verification:Phone number is invalid"))
return
}
if organization == nil {
c.ResponseError("The organization doesn't exist.")
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
}
@ -148,22 +161,16 @@ func (c *ApiController) SendVerificationCode() {
// @Title ResetEmailOrPhone
// @router /api/reset-email-or-phone [post]
func (c *ApiController) ResetEmailOrPhone() {
userId, ok := c.RequireSignedIn()
user, ok := c.RequireSignedInUser()
if !ok {
return
}
user := object.GetUser(userId)
if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s doesn't exist", userId))
return
}
destType := c.Ctx.Request.Form.Get("type")
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("Missing parameter.")
c.ResponseError(c.T("verification:Missing parameter"))
return
}
@ -172,11 +179,11 @@ func (c *ApiController) ResetEmailOrPhone() {
if destType == "phone" {
phoneItem := object.GetAccountItemByName("Phone", org)
if phoneItem == nil {
c.ResponseError("Unable to get the phone modify rule.")
c.ResponseError(c.T("verification:Unable to get the phone modify rule."))
return
}
if pass, errMsg := object.CheckAccountItemModifyRule(phoneItem, user); !pass {
if pass, errMsg := object.CheckAccountItemModifyRule(phoneItem, user, c.GetAcceptLanguage()); !pass {
c.ResponseError(errMsg)
return
}
@ -189,16 +196,16 @@ func (c *ApiController) ResetEmailOrPhone() {
} else if destType == "email" {
emailItem := object.GetAccountItemByName("Email", org)
if emailItem == nil {
c.ResponseError("Unable to get the email modify rule.")
c.ResponseError(c.T("verification:Unable to get the email modify rule."))
return
}
if pass, errMsg := object.CheckAccountItemModifyRule(emailItem, user); !pass {
if pass, errMsg := object.CheckAccountItemModifyRule(emailItem, user, c.GetAcceptLanguage()); !pass {
c.ResponseError(errMsg)
return
}
}
if ret := object.CheckVerificationCode(checkDest, code); len(ret) != 0 {
if ret := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage()); len(ret) != 0 {
c.ResponseError(ret)
return
}
@ -211,7 +218,7 @@ func (c *ApiController) ResetEmailOrPhone() {
user.Phone = dest
object.SetUserField(user, "phone", user.Phone)
default:
c.ResponseError("Unknown type.")
c.ResponseError(c.T("verification:Unknown type"))
return
}
@ -230,17 +237,17 @@ func (c *ApiController) VerifyCaptcha() {
captchaToken := c.Ctx.Request.Form.Get("captchaToken")
clientSecret := c.Ctx.Request.Form.Get("clientSecret")
if captchaToken == "" {
c.ResponseError("Missing parameter: captchaToken.")
c.ResponseError(c.T("verification:Missing parameter") + ": captchaToken.")
return
}
if clientSecret == "" {
c.ResponseError("Missing parameter: clientSecret.")
c.ResponseError(c.T("verification:Missing parameter") + ": clientSecret.")
return
}
provider := captcha.GetCaptchaProvider(captchaType)
if provider == nil {
c.ResponseError("Invalid captcha provider.")
c.ResponseError(c.T("verification:Invalid captcha provider."))
return
}

View File

@ -16,6 +16,7 @@ package controllers
import (
"bytes"
"fmt"
"io"
"github.com/casdoor/casdoor/object"
@ -34,7 +35,7 @@ func (c *ApiController) WebAuthnSignupBegin() {
webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host)
user := c.getCurrentUser()
if user == nil {
c.ResponseError("Please login first.")
c.ResponseError(c.T("webauthn:Please login first"))
return
}
@ -65,13 +66,13 @@ func (c *ApiController) WebAuthnSignupFinish() {
webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host)
user := c.getCurrentUser()
if user == nil {
c.ResponseError("Please login first.")
c.ResponseError(c.T("webauthn:Please login first"))
return
}
sessionObj := c.GetSession("registration")
sessionData, ok := sessionObj.(webauthn.SessionData)
if !ok {
c.ResponseError("Please call WebAuthnSignupBegin first")
c.ResponseError(c.T("webauthn:Please call WebAuthnSigninBegin first"))
return
}
c.Ctx.Request.Body = io.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody))
@ -100,9 +101,14 @@ func (c *ApiController) WebAuthnSigninBegin() {
userName := c.Input().Get("name")
user := object.GetUserByFields(userOwner, userName)
if user == nil {
c.ResponseError("Please Giveout Owner and Username.")
c.ResponseError(fmt.Sprintf(c.T("webauthn:The user: %s/%s doesn't exist"), 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())
@ -121,11 +127,12 @@ func (c *ApiController) WebAuthnSigninBegin() {
// @Success 200 {object} Response "The Response object"
// @router /webauthn/signin/finish [post]
func (c *ApiController) WebAuthnSigninFinish() {
responseType := c.Input().Get("responseType")
webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host)
sessionObj := c.GetSession("authentication")
sessionData, ok := sessionObj.(webauthn.SessionData)
if !ok {
c.ResponseError("Please call WebAuthnSigninBegin first")
c.ResponseError(c.T("webauthn:Please call WebAuthnSigninBegin first"))
return
}
c.Ctx.Request.Body = io.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody))
@ -138,5 +145,11 @@ func (c *ApiController) WebAuthnSigninFinish() {
}
c.SetSessionUsername(userId)
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
c.ResponseOk(userId)
application := object.GetApplicationByUser(user)
var form RequestForm
form.Type = responseType
resp := c.HandleLoggedIn(application, user, &form)
c.Data["json"] = resp
c.ServeJSON()
}

View File

@ -17,7 +17,7 @@ package controllers
import (
"encoding/json"
"github.com/astaxie/beego/utils/pagination"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)

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

22
go.mod
View File

@ -5,27 +5,30 @@ go 1.16
require (
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
github.com/astaxie/beego v1.12.3
github.com/aws/aws-sdk-go v1.44.4
github.com/beego/beego v1.12.11
github.com/beevik/etree v1.1.0
github.com/casbin/casbin/v2 v2.30.1
github.com/casbin/xorm-adapter/v2 v2.5.1
github.com/casdoor/go-sms-sender v0.3.0
github.com/casdoor/goth v1.69.0-FIX2
github.com/casbin/xorm-adapter/v3 v3.0.1
github.com/casdoor/go-sms-sender v0.5.1
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/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
github.com/go-ldap/ldap/v3 v3.3.0
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
@ -33,21 +36,22 @@ 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/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.62.0 // indirect
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
xorm.io/builder v0.3.12 // indirect
xorm.io/core v0.7.2
xorm.io/xorm v1.0.4
xorm.io/xorm v1.0.5
)

92
go.sum
View File

@ -74,13 +74,13 @@ github.com/aliyun/alibaba-cloud-sdk-go v1.61.1075/go.mod h1:pUKYbK5JQ+1Dfxk80P0q
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/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/astaxie/beego v1.12.3 h1:SAQkdD2ePye+v8Gn1r4X6IKZM1wd28EyUOVQ3PDSOOQ=
github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
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=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
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/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
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=
@ -96,12 +96,10 @@ github.com/casbin/casbin/v2 v2.1.0/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n
github.com/casbin/casbin/v2 v2.28.3/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casbin/casbin/v2 v2.30.1 h1:P5HWadDL7olwUXNdcuKUBk+x75Y2eitFxYTcLNKeKF0=
github.com/casbin/casbin/v2 v2.30.1/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casbin/xorm-adapter/v2 v2.5.1 h1:BkpIxRHKa0s3bSMx173PpuU7oTs+Zw7XmD0BIta0HGM=
github.com/casbin/xorm-adapter/v2 v2.5.1/go.mod h1:AeH4dBKHC9/zYxzdPVHhPDzF8LYLqjDdb767CWJoV54=
github.com/casdoor/go-sms-sender v0.3.0 h1:c4bWVcKZhO2L3Xu1oy7aeVkCK6HRJkW/b5K1xU9mV60=
github.com/casdoor/go-sms-sender v0.3.0/go.mod h1:fsZsNnALvFIo+HFcE1U/oCQv4ZT42FdglXKMsEm3WSk=
github.com/casdoor/goth v1.69.0-FIX2 h1:RgfIMkL9kekylgxHHK2ZY8ASAwOGns2HVlaBwLu7Bcs=
github.com/casdoor/goth v1.69.0-FIX2/go.mod h1:Om55nRo8CkeDkPSNBbzXW4G5uI28ZUkSk5S69dPek3s=
github.com/casbin/xorm-adapter/v3 v3.0.1 h1:0l0zkYxo6cNuIdrBZgFxlje1TRvmheYa/zIp+sGPK58=
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/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/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -115,9 +113,9 @@ github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 h1:Puu1hUwfps3+1C
github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
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/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo=
github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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=
@ -125,6 +123,9 @@ 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/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 h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/duo-labs/webauthn v0.0.0-20211221191814-a22482edaa3b h1:L63RATZFZuFMXy6ixnKmv3eNAXwYQF6HW1vd4IYsQqQ=
@ -137,6 +138,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
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/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
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/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@ -171,8 +174,12 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
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/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
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/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=
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
@ -188,8 +195,9 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -206,8 +214,9 @@ 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=
@ -241,7 +250,6 @@ 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/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=
@ -275,7 +283,6 @@ github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr
github.com/json-iterator/go v1.1.10/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/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/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
@ -294,14 +301,28 @@ 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/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
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=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 h1:wIONC+HMNRqmWBjuMxhatuSzHaljStc4gjDeKycxy0A=
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=
@ -334,7 +355,6 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
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/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
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=
@ -386,11 +406,10 @@ github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKz
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/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
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/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/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=
@ -400,11 +419,11 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
@ -417,16 +436,19 @@ github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03O
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
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/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-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
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=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
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=
@ -447,11 +469,13 @@ golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
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=
@ -482,6 +506,7 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -514,8 +539,8 @@ 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-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=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
@ -537,6 +562,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/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=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -552,6 +578,7 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -573,7 +600,9 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
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-20201119102817-f84b799fce68/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=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -581,6 +610,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
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/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -639,6 +669,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.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -739,8 +770,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.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/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=
@ -767,10 +798,11 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/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.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.0.5 h1:LRr5PfOUb4ODPR63YwbowkNDwcolT2LnkwP/TUaMaB0=
xorm.io/xorm v1.0.5/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=

View File

@ -83,7 +83,7 @@ func parseToData() *I18nData {
data := I18nData{}
for _, word := range allWords {
tokens := strings.Split(word, ":")
tokens := strings.SplitN(word, ":", 2)
namespace := tokens[0]
key := tokens[1]

115
i18n/generate_backend.go Normal file
View File

@ -0,0 +1,115 @@
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package i18n
import (
"os"
"path/filepath"
"regexp"
"strings"
"github.com/casdoor/casdoor/util"
)
var (
reI18nBackendObject *regexp.Regexp
re18nBackendController *regexp.Regexp
)
func init() {
reI18nBackendObject, _ = regexp.Compile("i18n.Translate\\((.*?)\"\\)")
re18nBackendController, _ = regexp.Compile("c.T\\((.*?)\"\\)")
}
func GetAllI18nStrings(fileContent string, path string) []string {
res := []string{}
if strings.Contains(path, "object") {
matches := reI18nBackendObject.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}
for _, match := range matches {
match := strings.SplitN(match[1], ",", 2)
res = append(res, match[1][2:])
}
} else {
matches := re18nBackendController.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}
for _, match := range matches {
res = append(res, match[1][1:])
}
}
return res
}
func getAllGoFilePaths() []string {
path := "../"
res := []string{}
err := filepath.Walk(path,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !strings.HasSuffix(info.Name(), ".go") {
return nil
}
res = append(res, path)
// fmt.Println(path, info.Name())
return nil
})
if err != nil {
panic(err)
}
return res
}
func getErrName(paths []string) map[string]string {
ErrName := make(map[string]string)
for i := 0; i < len(paths); i++ {
content := util.ReadStringFromPath(paths[i])
words := GetAllI18nStrings(content, paths[i])
for j := 0; j < len(words); j++ {
ErrName[words[j]] = paths[i]
}
}
return ErrName
}
func getI18nJSONData(errName map[string]string) *I18nData {
data := I18nData{}
for k, v := range errName {
var index int
if strings.Contains(v, "/") {
index = strings.LastIndex(v, "/")
} else {
index = strings.LastIndex(v, "\\")
}
namespace := v[index+1 : len(v)-3]
key := k[len(namespace)+1:]
// fmt.Printf("k=%s,v=%s,namespace=%s,key=%s\n", k, v, namespace, key)
if _, ok := data[namespace]; !ok {
data[namespace] = map[string]string{}
}
data[namespace][key] = key
}
return &data
}

View File

@ -14,7 +14,10 @@
package i18n
import "testing"
import (
"fmt"
"testing"
)
func applyToOtherLanguage(dataEn *I18nData, lang string) {
dataOther := readI18nFile(lang)
@ -24,7 +27,7 @@ func applyToOtherLanguage(dataEn *I18nData, lang string) {
writeI18nFile(lang, dataEn)
}
func TestGenerateI18nStrings(t *testing.T) {
func TestGenerateI18nStringsForFrontend(t *testing.T) {
dataEn := parseToData()
writeI18nFile("en", dataEn)
@ -35,3 +38,27 @@ func TestGenerateI18nStrings(t *testing.T) {
applyToOtherLanguage(dataEn, "ru")
applyToOtherLanguage(dataEn, "zh")
}
func TestGenerateI18nStringsForBackend(t *testing.T) {
paths := getAllGoFilePaths()
errName := getErrName(paths)
dataEn := getI18nJSONData(errName)
writeI18nFile("backend_en", dataEn)
applyToOtherLanguage(dataEn, "backend_de")
applyToOtherLanguage(dataEn, "backend_es")
applyToOtherLanguage(dataEn, "backend_fr")
applyToOtherLanguage(dataEn, "backend_ja")
applyToOtherLanguage(dataEn, "backend_ko")
applyToOtherLanguage(dataEn, "backend_ru")
applyToOtherLanguage(dataEn, "backend_zh")
fmt.Println("Total Err Words:", len(errName))
for i := range errName {
fmt.Println(i)
}
}

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

@ -0,0 +1,172 @@
{
"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 up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
},
"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",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist": "The user: %s doesn't exist",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"unsupported password type: %s": "unsupported password type: %s"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
},
"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."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"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"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
},
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"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",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
}
}

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

@ -0,0 +1,172 @@
{
"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 up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
},
"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",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist": "The user: %s doesn't exist",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"unsupported password type: %s": "unsupported password type: %s"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
},
"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."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"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"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
},
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"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",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
}
}

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

@ -0,0 +1,172 @@
{
"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 up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
},
"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",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist": "The user: %s doesn't exist",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"unsupported password type: %s": "unsupported password type: %s"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
},
"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."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"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"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
},
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"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",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
}
}

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

@ -0,0 +1,172 @@
{
"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 up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
},
"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",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist": "The user: %s doesn't exist",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"unsupported password type: %s": "unsupported password type: %s"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
},
"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."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"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"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
},
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"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",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
}
}

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

@ -0,0 +1,172 @@
{
"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 up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
},
"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",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist": "The user: %s doesn't exist",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"unsupported password type: %s": "unsupported password type: %s"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
},
"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."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"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"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
},
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"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",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
}
}

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

@ -0,0 +1,172 @@
{
"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 up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
},
"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",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist": "The user: %s doesn't exist",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"unsupported password type: %s": "unsupported password type: %s"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
},
"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."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"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"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
},
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"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",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
}
}

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

@ -0,0 +1,172 @@
{
"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 up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
},
"application": {
"Parameter organization is missing": "Parameter organization is missing",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"auth": {
"%s No phone prefix": "%s No phone prefix",
"Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "Failed to login in: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid token": "Invalid token",
"Please sign out first before signing in": "Please sign out first before signing in",
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account does not exist": "The account does not exist",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The provider type: %s is not supported": "The provider type: %s is not supported",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist",
"Turing test failed.": "Turing test failed.",
"Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
},
"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",
"Please login first": "Please login first",
"Session outdated, please login again": "Session outdated, please login again",
"The user doesn't exist": "The user doesn't exist",
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist": "The user: %s doesn't exist",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address",
"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 don't have the permission to do this": "You don't have the permission to do this",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again",
"unsupported password type: %s": "unsupported password type: %s"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "You have entered the wrong password too many times, please wait for %d minutes and try again",
"password is incorrect, you have %d remaining chances": "password is incorrect, you have %d remaining chances"
},
"enforcer": {
"Please sign in first": "Please sign in first"
},
"ldap": {
"Ldap server exist": "Ldap server exist",
"Missing parameter": "Missing parameter"
},
"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."
},
"product": {
"Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"provider": {
"Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist"
},
"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"
},
"system_info": {
"You are not authorized to access this resource": "You are not authorized to access this resource"
},
"token": {
"Challenge method should be S256": "Challenge method should be S256",
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"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"
},
"user": {
"Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.",
"New password must have at least 6 characters": "New password must have at least 6 characters",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
},
"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",
"Please login first": "Please login first",
"The provider: %s is not found": "The provider: %s is not found",
"The user: %s doesn't exist": "The user: %s doesn't exist"
},
"verification": {
"Code has not been sent yet!": "Code has not been sent yet!",
"Email is invalid": "Email is invalid",
"Invalid captcha provider.": "Invalid captcha provider.",
"Missing parameter": "Missing parameter",
"Organization does not exist": "Organization does not exist",
"Phone number is invalid": "Phone number is invalid",
"Please login first": "Please login first",
"Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"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",
"Please login first": "Please login first",
"The user: %s/%s doesn't exist": "The user: %s/%s doesn't exist"
}
}

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

@ -0,0 +1,172 @@
{
"account": {
"Email: %s": "邮件: %s",
"Get init score failed, error: %w": "初始化分数失败: %w",
"Invalid information": "无效信息",
"Phone: %s": "电话: %s",
"Please sign out first before signing up": "请在登陆前登出",
"The application does not allow to sign up new account": "该应用不允许注册新账户"
},
"application": {
"Parameter organization is missing": "Organization参数丢失",
"The user: %s doesn't exist": "用户不存在: %s"
},
"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",
"Get init score failed, error: %w": "初始化分数失败: %w",
"Invalid token": "无效token",
"Please sign out first before signing in": "请在登陆前登出",
"State expected: %s, but got: %s": "期望状态位: %s, 实际状态为: %s",
"The account does not exist": "账户不存在",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许通过 %s 注册新账户, 请使用其他方式注册",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, 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 provider type: %s is not supported": "不支持该类型的提供商: %s",
"The provider: %s is not enabled for the application": "提供商: %s 未被启用",
"The user is forbidden to sign in, please contact the administrator": "该用户被禁止登陆,请联系管理员",
"The user: %s/%s doesn't exist": "用户不存在: %s/%s",
"Turing test failed.": "真人验证失败",
"Unauthorized operation": "未授权的操作",
"Unknown authentication type (not password or provider), form = %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": "无效电话",
"Please login first": "请先登录",
"Session outdated, please login again": "Session已过期请重新登陆",
"The user doesn't exist": "用户不存在",
"The user is forbidden to sign in, please contact the administrator": "该用户被禁止登陆,请联系管理员",
"The user: %s doesn't exist": "用户不存在: %s",
"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 don't have the permission to do this": "用户名至少要有2个字符",
"You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again": "输入密码错误次数已达上限,请在 %d 分 %d 秒后重试",
"unsupported password type: %s": "不支持的密码类型: %s"
},
"check_util": {
"You have entered the wrong password too many times, please wait for %d minutes and try again": "输入密码错误次数已达上限,请在 %d 分后重试",
"password is incorrect, you have %d remaining chances": "密码错误,您还有 %d 次尝试的机会"
},
"enforcer": {
"Please sign in first": "请先登录"
},
"ldap": {
"Ldap server exist": "Ldap服务器已存在",
"Missing parameter": "参数丢失"
},
"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.": "您无法取消链接,您不是任何应用程序的成员",
"The %s is immutable.": "%s是不可变的",
"Unknown modify rule %s.": "未知的修改规则"
},
"product": {
"Please login first": "请先登录",
"The user: %s doesn't exist": "用户不存在: %s"
},
"provider": {
"Invalid application id": "无效的Application ID",
"the provider: %s does not exist": "提供商: %s 不存在"
},
"resource": {
"User is nil for tag: avatar": "用户头像标签为空",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "username或FilePath为空: username = %s, fullFilePath = %s"
},
"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": "object key :%s 不被允许",
"The provider type: %s is not supported": "提供商类型: %s 尚未支持"
},
"system_info": {
"You are not authorized to access this resource": "您无权获取此资源"
},
"token": {
"Challenge method should be S256": "Challenge 方法应该为 S256",
"Empty clientId or clientSecret": "clientId或clientSecret为空",
"Grant_type: %s is not supported in this application": "此应用中不支持此授权类型: %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 在可列表中未找到"
},
"user": {
"Display name cannot be empty": "展示名称不可为空",
"New password cannot contain blank space.": "新密码不可以包含空格",
"New password must have at least 6 characters": "新密码至少需要6位字符",
"The user: %s/%s doesn't exist": "用户不存在: %s/%s"
},
"user_upload": {
"Failed to import users": "导入用户失败"
},
"util": {
"No application is found for userId: %s": "找不到该用户的应用程序 %s",
"No provider for category: %s is found for application: %s": "找不到该用户的应用程序 %s",
"Please login first": "请先登录",
"The provider: %s is not found": "该提供商未找到: %s",
"The user: %s doesn't exist": "用户不存在: %s"
},
"verification": {
"Code has not been sent yet!": "验证码还未发送",
"Email is invalid": "非法的邮箱",
"Invalid captcha provider.": "非法的验证码提供商",
"Missing parameter": "参数丢失",
"Organization does not exist": "组织不存在",
"Phone number is invalid": "非法的电话号码",
"Please login first": "请先登录",
"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": "请先调用 WebAuthnSigninBegi",
"Please login first": "请先登录",
"The user: %s/%s doesn't exist": "用户: %s/%s 不存在"
}
}

View File

@ -15,14 +15,26 @@
package i18n
import (
"embed"
"fmt"
"strings"
"github.com/casdoor/casdoor/util"
)
//go:embed locales/*/data.json
var f embed.FS
var langMap = make(map[string]map[string]map[string]string) // for example : langMap[en][account][Invalid information] = Invalid information
func getI18nFilePath(language string) string {
return fmt.Sprintf("../web/src/locales/%s/data.json", language)
if strings.Contains(language, "backend") {
// change language from 'backend_en' to 'en'
language = language[8:]
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 {
@ -62,3 +74,22 @@ func applyData(data1 *I18nData, data2 *I18nData) {
}
}
}
func Translate(lang string, error string) string {
parts := strings.SplitN(error, ":", 2)
if !strings.Contains(error, ":") || len(parts) != 2 {
return "Translate Error: " + error
}
if langMap[lang] != nil {
return langMap[lang][parts[0]][parts[1]]
} else {
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

@ -15,9 +15,12 @@
package idp
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
@ -121,6 +124,7 @@ func (idp *DingTalkIdProvider) GetToken(code string) (*oauth2.Token, error) {
type DingTalkUserResponse struct {
Nick string `json:"nick"`
OpenId string `json:"openId"`
UnionId string `json:"unionId"`
AvatarUrl string `json:"avatarUrl"`
Email string `json:"email"`
Errmsg string `json:"message"`
@ -162,10 +166,14 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
Id: dtUserInfo.OpenId,
Username: dtUserInfo.Nick,
DisplayName: dtUserInfo.Nick,
UnionId: dtUserInfo.UnionId,
Email: dtUserInfo.Email,
AvatarUrl: dtUserInfo.AvatarUrl,
}
isUserInOrg, err := idp.isUserInOrg(userInfo.UnionId)
if !isUserInOrg {
return nil, err
}
return &userInfo, nil
}
@ -192,3 +200,64 @@ func (idp *DingTalkIdProvider) postWithBody(body interface{}, url string) ([]byt
return data, nil
}
func (idp *DingTalkIdProvider) getInnerAppAccessToken() string {
appKey := idp.Config.ClientID
appSecret := idp.Config.ClientSecret
body := make(map[string]string)
body["appKey"] = appKey
body["appSecret"] = appSecret
bodyData, err := json.Marshal(body)
if err != nil {
log.Println(err.Error())
}
reader := bytes.NewReader(bodyData)
request, err := http.NewRequest("POST", "https://api.dingtalk.com/v1.0/oauth2/accessToken", reader)
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
resp, err := idp.Client.Do(request)
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err.Error())
}
var data struct {
ExpireIn int `json:"expireIn"`
AccessToken string `json:"accessToken"`
}
err = json.Unmarshal(respBytes, &data)
if err != nil {
log.Println(err.Error())
}
return data.AccessToken
}
func (idp *DingTalkIdProvider) isUserInOrg(unionId string) (bool, error) {
body := make(map[string]string)
body["unionid"] = unionId
bodyData, err := json.Marshal(body)
if err != nil {
log.Println(err.Error())
}
reader := bytes.NewReader(bodyData)
accessToken := idp.getInnerAppAccessToken()
request, _ := http.NewRequest("POST", "https://oapi.dingtalk.com/topapi/user/getbyunionid?access_token="+accessToken, reader)
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
resp, err := idp.Client.Do(request)
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err.Error())
}
var data struct {
ErrCode int `json:"errcode"`
ErrMessage string `json:"errmsg"`
}
err = json.Unmarshal(respBytes, &data)
if err != nil {
log.Println(err.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,35 @@ 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/azureadv2"
"github.com/markbates/goth/providers/bitbucket"
"github.com/markbates/goth/providers/digitalocean"
"github.com/markbates/goth/providers/discord"
"github.com/markbates/goth/providers/dropbox"
"github.com/markbates/goth/providers/facebook"
"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/instagram"
"github.com/markbates/goth/providers/kakao"
"github.com/markbates/goth/providers/line"
"github.com/markbates/goth/providers/linkedin"
"github.com/markbates/goth/providers/microsoftonline"
"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/steam"
"github.com/markbates/goth/providers/tumblr"
"github.com/markbates/goth/providers/twitter"
"github.com/markbates/goth/providers/yahoo"
"github.com/markbates/goth/providers/yandex"
"github.com/markbates/goth/providers/zoom"
"golang.org/x/oauth2"
)
@ -74,8 +74,8 @@ 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 "Bitbucket":
idp = GothIdProvider{
@ -202,6 +202,8 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: zoom.New(clientId, clientSecret, redirectUrl),
Session: &zoom.Session{},
}
default:
return nil
}
return &idp
@ -282,7 +284,7 @@ func getUser(gothUser goth.User, provider string) *UserInfo {
}
}
if provider == "steam" {
user.Username = user.DisplayName
user.Username = user.Id
user.Email = ""
}
return &user

View File

@ -25,6 +25,7 @@ type UserInfo struct {
Id string
Username string
DisplayName string
UnionId string
Email string
AvatarUrl string
}
@ -97,7 +98,7 @@ 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"}
func isGothSupport(provider string) bool {
for _, value := range gothList {

View File

@ -16,14 +16,17 @@ package idp
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
"github.com/skip2/go-qrcode"
"golang.org/x/oauth2"
)
@ -191,3 +194,54 @@ func (idp *WeChatIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
}
return &userInfo, nil
}
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) {
accessTokenUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", clientId, clientSecret)
request, err := http.NewRequest("GET", accessTokenUrl, nil)
client := new(http.Client)
resp, err := client.Do(request)
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var data struct {
ExpireIn int `json:"expires_in"`
AccessToken string `json:"access_token"`
}
err = json.Unmarshal(respBytes, &data)
if err != nil {
return "", err
}
return data.AccessToken, nil
}
func GetWechatOfficialAccountQRCode(clientId string, clientSecret string) (string, error) {
accessToken, err := GetWechatOfficialAccountAccessToken(clientId, clientSecret)
client := new(http.Client)
params := "{\"action_name\": \"QR_LIMIT_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": \"test\"}}}"
bodyData := bytes.NewReader([]byte(params))
qrCodeUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=%s", accessToken)
requeset, err := http.NewRequest("POST", qrCodeUrl, bodyData)
resp, err := client.Do(requeset)
if err != nil {
return "", err
}
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var data struct {
Ticket string `json:"ticket"`
ExpireSeconds int `json:"expire_seconds"`
URL string `json:"url"`
}
err = json.Unmarshal(respBytes, &data)
if err != nil {
return "", err
}
var png []byte
png, err = qrcode.Encode(data.URL, qrcode.Medium, 256)
base64Image := base64.StdEncoding.EncodeToString(png)
return base64Image, nil
}

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": ""
}
]
}

10
main.go
View File

@ -18,11 +18,12 @@ import (
"flag"
"fmt"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
_ "github.com/astaxie/beego/session/redis"
"github.com/beego/beego"
"github.com/beego/beego/logs"
_ "github.com/beego/beego/session/redis"
"github.com/casdoor/casdoor/authz"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/controllers"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/proxy"
"github.com/casdoor/casdoor/routers"
@ -76,5 +77,8 @@ func main() {
port := beego.AppConfig.DefaultInt("httpport", 8000)
// logs.SetLevel(logs.LevelInformational)
logs.SetLogFuncCall(false)
go controllers.StartLdapServer()
beego.Run(fmt.Sprintf(":%v", port))
}

View File

@ -18,12 +18,14 @@ import (
"fmt"
"runtime"
"github.com/astaxie/beego"
"github.com/beego/beego"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
_ "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
"xorm.io/core"
"xorm.io/xorm"
@ -40,10 +42,11 @@ func InitConfig() {
beego.BConfig.WebConfig.Session.SessionOn = true
InitAdapter(true)
initMigrations()
}
func InitAdapter(createDatabase bool) {
adapter = NewAdapter(conf.GetConfigString("driverName"), conf.GetBeegoConfDataSourceName(), conf.GetConfigString("dbName"))
adapter = NewAdapter(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName"))
if createDatabase {
adapter.CreateDatabase()
}
@ -145,6 +148,11 @@ func (a *Adapter) createTable() {
panic(err)
}
err = a.Engine.Sync2(new(CasbinAdapter))
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(Provider))
if err != nil {
panic(err)
@ -209,6 +217,11 @@ func (a *Adapter) createTable() {
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(xormadapter.CasbinRule))
if err != nil {
panic(err)
}
}
func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
@ -234,3 +247,22 @@ 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,9 +16,10 @@ package object
import (
"fmt"
"net/url"
"regexp"
"strings"
"github.com/casdoor/casdoor/idp"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
)
@ -45,9 +46,11 @@ type Application struct {
EnablePassword bool `json:"enablePassword"`
EnableSignUp bool `json:"enableSignUp"`
EnableSigninSession bool `json:"enableSigninSession"`
EnableAutoSignin bool `json:"enableAutoSignin"`
EnableCodeSignin bool `json:"enableCodeSignin"`
EnableSamlCompress bool `json:"enableSamlCompress"`
EnableWebAuthn bool `json:"enableWebAuthn"`
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"`
@ -66,6 +69,10 @@ type Application struct {
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"`
}
func GetApplicationCount(owner, field, value string) int {
@ -78,6 +85,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})
@ -88,8 +105,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 {
@ -99,9 +126,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)
}
@ -113,9 +141,11 @@ func getProviderMap(owner string) map[string]*Provider {
providers := GetProviders(owner)
m := map[string]*Provider{}
for _, provider := range providers {
//if provider.Category != "OAuth" {
// continue
//}
// Get QRCode only once
if provider.Type == "WeChat" && provider.DisableSsl == true && provider.Content == "" {
provider.Content, _ = idp.GetWechatOfficialAccountQRCode(provider.ClientId2, provider.ClientSecret2)
UpdateProvider(provider.Owner+"/"+provider.Name, provider)
}
m[provider.Name] = GetMaskedProvider(provider)
}
@ -123,7 +153,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
@ -264,6 +294,13 @@ func UpdateApplication(id string, application *Application) bool {
application.Name = name
}
if name != application.Name {
err := applicationChangeTrigger(name, application.Name)
if err != nil {
return false
}
}
for _, providerItem := range application.Providers {
providerItem.Provider = nil
}
@ -316,55 +353,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 {
if 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 {
@ -393,3 +405,55 @@ func ExtendManagedAccountsWithUser(user *User) *User {
return user
}
func applicationChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
organization := new(Organization)
organization.DefaultApplication = newName
_, err = session.Where("default_application=?", oldName).Update(organization)
if err != nil {
return err
}
user := new(User)
user.SignupApplication = newName
_, err = session.Where("signup_application=?", oldName).Update(user)
if err != nil {
return err
}
resource := new(Resource)
resource.Application = newName
_, err = session.Where("application=?", oldName).Update(resource)
if err != nil {
return err
}
var permissions []*Permission
err = adapter.Engine.Find(&permissions)
if err != nil {
return err
}
for i := 0; i < len(permissions); i++ {
permissionResoureces := permissions[i].Resources
for j := 0; j < len(permissionResoureces); j++ {
if permissionResoureces[j] == oldName {
permissionResoureces[j] = newName
}
}
permissions[i].Resources = permissionResoureces
_, err = session.Where("name=?", permissions[i].Name).Update(permissions[i])
if err != nil {
return err
}
}
return session.Commit()
}

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

@ -50,7 +50,7 @@ func downloadFile(url string) (*bytes.Buffer, error) {
return fileBuffer, nil
}
func getPermanentAvatarUrl(organization string, username string, url string) string {
func getPermanentAvatarUrl(organization string, username string, url string, upload bool) string {
if url == "" {
return ""
}
@ -60,8 +60,16 @@ func getPermanentAvatarUrl(organization string, username string, url string) str
}
fullFilePath := fmt.Sprintf("/avatar/%s/%s.png", organization, username)
uploadedFileUrl, _ := getUploadFileUrl(defaultStorageProvider, fullFilePath, false)
uploadedFileUrl, _ := GetUploadFileUrl(defaultStorageProvider, fullFilePath, false)
if upload {
DownloadAndUpload(url, fullFilePath)
}
return uploadedFileUrl
}
func DownloadAndUpload(url string, fullFilePath string) {
fileBuffer, err := downloadFile(url)
if err != nil {
panic(err)
@ -71,6 +79,4 @@ func getPermanentAvatarUrl(organization string, username string, url string) str
if err != nil {
panic(err)
}
return uploadedFileUrl
}

View File

@ -32,7 +32,7 @@ func TestSyncPermanentAvatars(t *testing.T) {
continue
}
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar)
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, true)
updateUserColumn("permanent_avatar", user)
fmt.Printf("[%d/%d]: Update user: [%s]'s permanent avatar: %s\n", i, len(users), user.GetId(), user.PermanentAvatar)
}

272
object/casbin_adapter.go Normal file
View File

@ -0,0 +1,272 @@
// 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 (
"fmt"
"strings"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
)
type CasbinAdapter 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"`
Organization string `xorm:"varchar(100)" json:"organization"`
Type string `xorm:"varchar(100)" json:"type"`
Model string `xorm:"varchar(100)" json:"model"`
Host string `xorm:"varchar(100)" json:"host"`
Port int `json:"port"`
User string `xorm:"varchar(100)" json:"user"`
Password string `xorm:"varchar(100)" json:"password"`
DatabaseType string `xorm:"varchar(100)" json:"databaseType"`
Database string `xorm:"varchar(100)" json:"database"`
Table string `xorm:"varchar(100)" json:"table"`
IsEnabled bool `json:"isEnabled"`
Adapter *xormadapter.Adapter `xorm:"-" json:"-"`
}
func GetCasbinAdapterCount(owner, field, value string) int {
session := GetSession(owner, -1, -1, field, value, "", "")
count, err := session.Count(&CasbinAdapter{})
if err != nil {
panic(err)
}
return int(count)
}
func GetCasbinAdapters(owner string) []*CasbinAdapter {
adapters := []*CasbinAdapter{}
err := adapter.Engine.Where("owner = ?", owner).Find(&adapters)
if err != nil {
panic(err)
}
return adapters
}
func GetPaginationCasbinAdapters(owner string, page, limit int, field, value, sort, order string) []*CasbinAdapter {
session := GetSession(owner, page, limit, field, value, sort, order)
adapters := []*CasbinAdapter{}
err := session.Find(&adapters)
if err != nil {
panic(err)
}
return adapters
}
func getCasbinAdapter(owner, name string) *CasbinAdapter {
if owner == "" || name == "" {
return nil
}
casbinAdapter := CasbinAdapter{Owner: owner, Name: name}
existed, err := adapter.Engine.Get(&casbinAdapter)
if err != nil {
panic(err)
}
if existed {
return &casbinAdapter
} else {
return nil
}
}
func GetCasbinAdapter(id string) *CasbinAdapter {
owner, name := util.GetOwnerAndNameFromId(id)
return getCasbinAdapter(owner, name)
}
func UpdateCasbinAdapter(id string, casbinAdapter *CasbinAdapter) bool {
owner, name := util.GetOwnerAndNameFromId(id)
if getCasbinAdapter(owner, name) == nil {
return false
}
session := adapter.Engine.ID(core.PK{owner, name}).AllCols()
if casbinAdapter.Password == "***" {
session.Omit("password")
}
affected, err := session.Update(casbinAdapter)
if err != nil {
panic(err)
}
return affected != 0
}
func AddCasbinAdapter(casbinAdapter *CasbinAdapter) bool {
affected, err := adapter.Engine.Insert(casbinAdapter)
if err != nil {
panic(err)
}
return affected != 0
}
func DeleteCasbinAdapter(casbinAdapter *CasbinAdapter) bool {
affected, err := adapter.Engine.ID(core.PK{casbinAdapter.Owner, casbinAdapter.Name}).Delete(&CasbinAdapter{})
if err != nil {
panic(err)
}
return affected != 0
}
func (casbinAdapter *CasbinAdapter) GetId() string {
return fmt.Sprintf("%s/%s", casbinAdapter.Owner, casbinAdapter.Name)
}
func (casbinAdapter *CasbinAdapter) getTable() string {
if casbinAdapter.DatabaseType == "mssql" {
return fmt.Sprintf("[%s]", casbinAdapter.Table)
} else {
return casbinAdapter.Table
}
}
func initEnforcer(modelObj *Model, casbinAdapter *CasbinAdapter) (*casbin.Enforcer, error) {
// init Adapter
if casbinAdapter.Adapter == nil {
var dataSourceName string
if casbinAdapter.DatabaseType == "mssql" {
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s", casbinAdapter.User, casbinAdapter.Password, casbinAdapter.Host, casbinAdapter.Port, casbinAdapter.Database)
} else if casbinAdapter.DatabaseType == "postgres" {
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=disable dbname=%s", casbinAdapter.User, casbinAdapter.Password, casbinAdapter.Host, casbinAdapter.Port, casbinAdapter.Database)
} else {
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/", casbinAdapter.User, casbinAdapter.Password, casbinAdapter.Host, casbinAdapter.Port)
}
if !isCloudIntranet {
dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.")
}
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
m, err := model.NewModelFromString(modelObj.ModelText)
if err != nil {
return nil, err
}
// init Enforcer
enforcer, err := casbin.NewEnforcer(m, casbinAdapter.Adapter)
if err != nil {
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())
if strings.Contains(modelObj.ModelText, "[role_definition]") {
policies = append(policies, matrixToCasbinRules("g", enforcer.GetGroupingPolicy())...)
}
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

@ -114,6 +114,12 @@ func UpdateCert(id string, cert *Cert) bool {
return false
}
if name != cert.Name {
err := certChangeTrigger(name, cert.Name)
if err != nil {
return false
}
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(cert)
if err != nil {
panic(err)
@ -161,3 +167,22 @@ func getCertByApplication(application *Application) *Cert {
func GetDefaultCert() *Cert {
return getCert("admin", "cert-built-in")
}
func certChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
application := new(Application)
application.Cert = newName
_, err = session.Where("cert=?", oldName).Update(application)
if err != nil {
return err
}
return session.Commit()
}

View File

@ -22,6 +22,7 @@ import (
"unicode"
"github.com/casdoor/casdoor/cred"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util"
goldap "github.com/go-ldap/ldap/v3"
)
@ -41,84 +42,89 @@ func init() {
reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
}
func CheckUserSignup(application *Application, organization *Organization, username string, password string, displayName string, firstName string, lastName string, email string, phone string, affiliation string) string {
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 "organization does not exist"
return i18n.Translate(lang, "check:Organization does not exist")
}
if application.IsSignupItemVisible("Username") {
if len(username) <= 1 {
return "username must have at least 2 characters"
return i18n.Translate(lang, "check:Username must have at least 2 characters")
}
if unicode.IsDigit(rune(username[0])) {
return "username cannot start with a digit"
return i18n.Translate(lang, "check:Username cannot start with a digit")
}
if util.IsEmailValid(username) {
return "username cannot be an email address"
return i18n.Translate(lang, "check:Username cannot be an email address")
}
if reWhiteSpace.MatchString(username) {
return "username cannot contain white spaces"
return i18n.Translate(lang, "check:Username cannot contain white spaces")
}
msg := CheckUsername(username, lang)
if msg != "" {
return msg
}
if HasUserByField(organization.Name, "name", username) {
return "username already exists"
return i18n.Translate(lang, "check:Username already exists")
}
if HasUserByField(organization.Name, "email", email) {
return "email already exists"
return i18n.Translate(lang, "check:Email already exists")
}
if HasUserByField(organization.Name, "phone", phone) {
return "phone already exists"
return i18n.Translate(lang, "check:Phone already exists")
}
}
if len(password) <= 5 {
return "password must have at least 6 characters"
return i18n.Translate(lang, "check:Password must have at least 6 characters")
}
if application.IsSignupItemVisible("Email") {
if email == "" {
if application.IsSignupItemRequired("Email") {
return "email cannot be empty"
return i18n.Translate(lang, "check:Email cannot be empty")
} else {
return ""
}
}
if HasUserByField(organization.Name, "email", email) {
return "email already exists"
return i18n.Translate(lang, "check:Email already exists")
} else if !util.IsEmailValid(email) {
return "email is invalid"
return i18n.Translate(lang, "check:Email is invalid")
}
}
if application.IsSignupItemVisible("Phone") {
if phone == "" {
if application.IsSignupItemRequired("Phone") {
return "phone cannot be empty"
return i18n.Translate(lang, "check:Phone cannot be empty")
} else {
return ""
}
}
if HasUserByField(organization.Name, "phone", phone) {
return "phone already exists"
return i18n.Translate(lang, "check:Phone already exists")
} else if organization.PhonePrefix == "86" && !util.IsPhoneCnValid(phone) {
return "phone number is invalid"
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 "firstName cannot be blank"
return i18n.Translate(lang, "check:FirstName cannot be blank")
} else if lastName == "" {
return "lastName cannot be blank"
return i18n.Translate(lang, "check:LastName cannot be blank")
}
} else {
if displayName == "" {
return "displayName cannot be blank"
return i18n.Translate(lang, "check:DisplayName cannot be blank")
} else if application.GetSignupItemRule("Display name") == "Real name" {
if !isValidRealName(displayName) {
return "displayName is not valid real name"
return i18n.Translate(lang, "check:DisplayName is not valid real name")
}
}
}
@ -126,14 +132,14 @@ func CheckUserSignup(application *Application, organization *Organization, usern
if application.IsSignupItemVisible("Affiliation") {
if affiliation == "" {
return "affiliation cannot be blank"
return i18n.Translate(lang, "check:Affiliation cannot be blank")
}
}
return ""
}
func checkSigninErrorTimes(user *User) string {
func checkSigninErrorTimes(user *User, lang string) string {
if user.SigninWrongTimes >= SigninWrongTimesLimit {
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
@ -141,7 +147,7 @@ func checkSigninErrorTimes(user *User) string {
// 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("You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again", seconds/60, seconds%60)
return fmt.Sprintf(i18n.Translate(lang, "check:You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again"), seconds/60, seconds%60)
}
// reset the error times
@ -153,15 +159,15 @@ func checkSigninErrorTimes(user *User) string {
return ""
}
func CheckPassword(user *User, password string) string {
func CheckPassword(user *User, password string, lang string) string {
// check the login error times
if msg := checkSigninErrorTimes(user); msg != "" {
if msg := checkSigninErrorTimes(user, lang); msg != "" {
return msg
}
organization := GetOrganizationByUser(user)
if organization == nil {
return "organization does not exist"
return i18n.Translate(lang, "check:Organization does not exist")
}
credManager := cred.GetCredManager(organization.PasswordType)
@ -178,13 +184,13 @@ func CheckPassword(user *User, password string) string {
return ""
}
return recordSigninErrorInfo(user)
return recordSigninErrorInfo(user, lang)
} else {
return fmt.Sprintf("unsupported password type: %s", organization.PasswordType)
return fmt.Sprintf(i18n.Translate(lang, "check:unsupported password type: %s"), organization.PasswordType)
}
}
func checkLdapUserPassword(user *User, password string) (*User, string) {
func checkLdapUserPassword(user *User, password string, lang string) (*User, string) {
ldaps := GetLdaps(user.Owner)
ldapLoginSuccess := false
for _, ldapServer := range ldaps {
@ -204,7 +210,7 @@ func checkLdapUserPassword(user *User, password string) (*User, string) {
if len(searchResult.Entries) == 0 {
continue
} else if len(searchResult.Entries) > 1 {
return nil, "Error: multiple accounts with same uid, please check your ldap server"
return nil, i18n.Translate(lang, "check:Multiple accounts with same uid, please check your ldap server")
}
dn := searchResult.Entries[0].DN
@ -215,26 +221,26 @@ func checkLdapUserPassword(user *User, password string) (*User, string) {
}
if !ldapLoginSuccess {
return nil, "ldap user name or password incorrect"
return nil, i18n.Translate(lang, "check:Ldap user name or password incorrect")
}
return user, ""
}
func CheckUserPassword(organization string, username string, password string) (*User, string) {
func CheckUserPassword(organization string, username string, password string, lang string) (*User, string) {
user := GetUserByFields(organization, username)
if user == nil || user.IsDeleted == true {
return nil, "the user does not exist, please sign up first"
return nil, i18n.Translate(lang, "check:The user doesn't exist")
}
if user.IsForbidden {
return nil, "the user is forbidden to sign in, please contact the administrator"
return nil, i18n.Translate(lang, "check:The user is forbidden to sign in, please contact the administrator")
}
if user.Ldap != "" {
// ONLY for ldap users
return checkLdapUserPassword(user, password)
return checkLdapUserPassword(user, password, lang)
} else {
msg := CheckPassword(user, password)
msg := CheckPassword(user, password, lang)
if msg != "" {
return nil, msg
}
@ -246,15 +252,15 @@ func filterField(field string) bool {
return reFieldWhiteList.MatchString(field)
}
func CheckUserPermission(requestUserId, userId, userOwner string, strict bool) (bool, error) {
func CheckUserPermission(requestUserId, userId, userOwner string, strict bool, lang string) (bool, error) {
if requestUserId == "" {
return false, fmt.Errorf("please login first")
return false, fmt.Errorf(i18n.Translate(lang, "check:Please login first"))
}
if userId != "" {
targetUser := GetUser(userId)
if targetUser == nil {
return false, fmt.Errorf("the user: %s doesn't exist", userId)
return false, fmt.Errorf(i18n.Translate(lang, "check:The user: %s doesn't exist"), userId)
}
userOwner = targetUser.Owner
@ -266,7 +272,7 @@ func CheckUserPermission(requestUserId, userId, userOwner string, strict bool) (
} else {
requestUser := GetUser(requestUserId)
if requestUser == nil {
return false, fmt.Errorf("session outdated, please login again")
return false, fmt.Errorf(i18n.Translate(lang, "check:Session outdated, please login again"))
}
if requestUser.IsGlobalAdmin {
hasPermission = true
@ -281,7 +287,7 @@ func CheckUserPermission(requestUserId, userId, userOwner string, strict bool) (
}
}
return hasPermission, fmt.Errorf("you don't have the permission to do this")
return hasPermission, fmt.Errorf(i18n.Translate(lang, "check:You don't have the permission to do this"))
}
func CheckAccessPermission(userId string, application *Application) (bool, error) {
@ -307,9 +313,48 @@ 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
}
func CheckUsername(username string, lang string) string {
if username == "" {
return i18n.Translate(lang, "check:Empty username.")
} else if len(username) > 39 {
return i18n.Translate(lang, "check:Username is too long (maximum is 39 characters).")
}
exclude, _ := regexp.Compile("^[\u0021-\u007E]+$")
if !exclude.MatchString(username) {
return ""
}
// 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, "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 CheckToEnableCaptcha(application *Application) bool {
if len(application.Providers) == 0 {
return false
}
for _, providerItem := range application.Providers {
if providerItem.Provider == nil {
continue
}
if providerItem.Provider.Category == "Captcha" && providerItem.Provider.Type == "Default" {
return providerItem.Rule == "Always"
}
}
return false
}

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_util:password 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_util:You have entered the wrong password too many times, please wait for %d minutes and try again"), int(LastSignWrongTimeDuration.Minutes()))
}

View File

@ -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"},
@ -143,7 +145,7 @@ func initBuiltInApplication() {
EnablePassword: true,
EnableSignUp: true,
Providers: []*ProviderItem{
{Name: "provider_captcha_default", CanSignUp: false, CanSignIn: false, CanUnlink: false, Prompted: false, AlertType: "None", Provider: nil},
{Name: "provider_captcha_default", CanSignUp: false, CanSignIn: false, CanUnlink: false, Prompted: false, AlertType: "None", Rule: "None", Provider: nil},
},
SignupItems: []*SignupItem{
{Name: "ID", Visible: false, Required: true, Prompted: false, Rule: "Random"},
@ -157,6 +159,7 @@ func initBuiltInApplication() {
},
RedirectUris: []string{},
ExpireInHours: 168,
FormOffset: 2,
}
AddApplication(application)
}
@ -220,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)
}
}
}
@ -56,12 +92,84 @@ func readInitDataFromFile(filePath string) *InitData {
s := util.ReadStringFromPath(filePath)
data := &InitData{}
data := &InitData{
Organizations: []*Organization{},
Applications: []*Application{},
Users: []*User{},
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 {
panic(err)
}
// transform nil slice to empty slice
for _, organization := range data.Organizations {
if organization.Tags == nil {
organization.Tags = []string{}
}
}
for _, application := range data.Applications {
if application.Providers == nil {
application.Providers = []*ProviderItem{}
}
if application.SignupItems == nil {
application.SignupItems = []*SignupItem{}
}
if application.GrantTypes == nil {
application.GrantTypes = []string{}
}
if application.RedirectUris == nil {
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
}
@ -140,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)
}

View File

@ -19,7 +19,7 @@ import (
"fmt"
"strings"
"github.com/astaxie/beego"
"github.com/beego/beego"
"github.com/casdoor/casdoor/util"
goldap "github.com/go-ldap/ldap/v3"
"github.com/thanhpk/randstr"
@ -409,6 +409,7 @@ func SyncLdapUsers(owner string, users []LdapRespUser, ldapId string) (*[]LdapRe
}
}
}
if !found && !AddUser(&User{
Owner: owner,
Name: buildLdapUserName(user.Uid, user.UidNumber),

View File

@ -5,7 +5,7 @@ import (
"sync"
"time"
"github.com/astaxie/beego/logs"
"github.com/beego/beego/logs"
"github.com/casdoor/casdoor/util"
)

74
object/ldapserver.go Normal file
View File

@ -0,0 +1,74 @@
// 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 (
"fmt"
"log"
"strings"
"github.com/forestmgy/ldapserver"
)
func GetNameAndOrgFromDN(DN string) (string, string, string) {
DNValue := strings.Split(DN, ",")
if len(DNValue) == 1 || strings.ToLower(DNValue[0])[0] != 'c' || strings.ToLower(DNValue[1])[0] != 'o' {
return "", "", "please use correct Admin Name format like cn=xxx,ou=xxx,dc=example,dc=com"
}
return DNValue[0][3:], DNValue[1][3:], ""
}
func GetUserNameAndOrgFromBaseDnAndFilter(baseDN, filter string) (string, string, int) {
if !strings.Contains(baseDN, "ou=") || !strings.Contains(filter, "cn=") {
return "", "", ldapserver.LDAPResultInvalidDNSyntax
}
name := getUserNameFromFilter(filter)
_, org, _ := GetNameAndOrgFromDN(fmt.Sprintf("cn=%s,", name) + baseDN)
errCode := ldapserver.LDAPResultSuccess
return name, org, errCode
}
func getUserNameFromFilter(filter string) string {
nameIndex := strings.Index(filter, "cn=")
var name string
for i := nameIndex + 3; filter[i] != ')'; i++ {
name = name + string(filter[i])
}
return name
}
func GetFilteredUsers(m *ldapserver.Message, name, org string) ([]*User, int) {
var filteredUsers []*User
if name == "*" && m.Client.IsOrgAdmin { // get all users from organization 'org'
if m.Client.OrgName == "built-in" && org == "*" {
filteredUsers = GetGlobalUsers()
return filteredUsers, ldapserver.LDAPResultSuccess
} else if m.Client.OrgName == "built-in" || org == m.Client.OrgName {
filteredUsers = GetUsers(org)
return filteredUsers, ldapserver.LDAPResultSuccess
} else {
return nil, ldapserver.LDAPResultInsufficientAccessRights
}
} else {
hasPermission, err := CheckUserPermission(fmt.Sprintf("%s/%s", m.Client.OrgName, m.Client.UserName), fmt.Sprintf("%s/%s", org, name), org, true, "en")
if !hasPermission {
log.Printf("ErrMsg = %v", err.Error())
return nil, ldapserver.LDAPResultInsufficientAccessRights
}
user := getUser(org, name)
filteredUsers = append(filteredUsers, user)
return filteredUsers, ldapserver.LDAPResultSuccess
}
}

View File

@ -92,6 +92,12 @@ func UpdateModel(id string, modelObj *Model) bool {
return false
}
if name != modelObj.Name {
err := modelChangeTrigger(name, modelObj.Name)
if err != nil {
return false
}
}
// check model grammar
_, err := model.NewModelFromString(modelObj.ModelText)
if err != nil {
@ -127,3 +133,22 @@ func DeleteModel(model *Model) bool {
func (model *Model) GetId() string {
return fmt.Sprintf("%s/%s", model.Owner, model.Name)
}
func modelChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
permission := new(Permission)
permission.Model = newName
_, err = session.Where("model=?", oldName).Update(permission)
if err != nil {
return err
}
return session.Commit()
}

View File

@ -16,8 +16,10 @@ package object
import (
"fmt"
"strings"
"github.com/casdoor/casdoor/cred"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
)
@ -41,8 +43,11 @@ type Organization struct {
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"`
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
InitScore int `json:"initScore"`
EnableSoftDeletion bool `json:"enableSoftDeletion"`
IsProfilePublic bool `json:"isProfilePublic"`
@ -132,15 +137,10 @@ func UpdateOrganization(id string, organization *Organization) bool {
}
if name != organization.Name {
go func() {
application := new(Application)
application.Organization = organization.Name
_, _ = adapter.Engine.Where("organization=?", name).Update(application)
user := new(User)
user.Owner = organization.Name
_, _ = adapter.Engine.Where("owner=?", name).Update(user)
}()
err := organizationChangeTrigger(name, organization.Name)
if err != nil {
return false
}
}
if organization.MasterPassword != "" && organization.MasterPassword != "***" {
@ -201,18 +201,202 @@ func GetAccountItemByName(name string, organization *Organization) *AccountItem
return nil
}
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User) (bool, string) {
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User, lang string) (bool, string) {
switch accountItem.ModifyRule {
case "Admin":
if !(user.IsAdmin || user.IsGlobalAdmin) {
return false, fmt.Sprintf("Only admin can modify the %s.", accountItem.Name)
return false, fmt.Sprintf(i18n.Translate(lang, "organization:Only admin can modify the %s."), accountItem.Name)
}
case "Immutable":
return false, fmt.Sprintf("The %s is 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("Unknown modify rule %s.", accountItem.ModifyRule)
return false, fmt.Sprintf(i18n.Translate(lang, "organization:Unknown modify rule %s."), accountItem.ModifyRule)
}
return true, ""
}
func GetDefaultApplication(id string) (*Application, error) {
organization := GetOrganization(id)
if organization == nil {
return nil, fmt.Errorf("The organization: %s does not exist", id)
}
if 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{}
err := adapter.Engine.Asc("created_time").Find(&applications, &Application{Organization: organization.Name})
if err != nil {
panic(err)
}
if len(applications) == 0 {
return nil, fmt.Errorf("The application does not exist")
}
defaultApplication := applications[0]
for _, application := range applications {
if application.EnableSignUp {
defaultApplication = application
break
}
}
extendApplicationWithProviders(defaultApplication)
extendApplicationWithOrg(defaultApplication)
return defaultApplication, nil
}
func organizationChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
application := new(Application)
application.Organization = newName
_, err = session.Where("organization=?", oldName).Update(application)
if err != nil {
return err
}
user := new(User)
user.Owner = newName
_, err = session.Where("owner=?", oldName).Update(user)
if err != nil {
return err
}
role := new(Role)
_, err = adapter.Engine.Where("owner=?", oldName).Get(role)
if err != nil {
return err
}
for i, u := range role.Users {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
role.Users[i] = split[0] + "/" + split[1]
}
}
for i, u := range role.Roles {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
role.Roles[i] = split[0] + "/" + split[1]
}
}
role.Owner = newName
_, err = session.Where("owner=?", oldName).Update(role)
if err != nil {
return err
}
permission := new(Permission)
_, err = adapter.Engine.Where("owner=?", oldName).Get(permission)
if err != nil {
return err
}
for i, u := range permission.Users {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
permission.Users[i] = split[0] + "/" + split[1]
}
}
for i, u := range permission.Roles {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
permission.Roles[i] = split[0] + "/" + split[1]
}
}
permission.Owner = newName
_, err = session.Where("owner=?", oldName).Update(permission)
if err != nil {
return err
}
casbinAdapter := new(CasbinAdapter)
casbinAdapter.Owner = newName
casbinAdapter.Organization = newName
_, err = session.Where("owner=?", oldName).Update(casbinAdapter)
if err != nil {
return err
}
ldap := new(Ldap)
ldap.Owner = newName
_, err = session.Where("owner=?", oldName).Update(ldap)
if err != nil {
return err
}
model := new(Model)
model.Owner = newName
_, err = session.Where("owner=?", oldName).Update(model)
if err != nil {
return err
}
payment := new(Payment)
payment.Organization = newName
_, err = session.Where("organization=?", oldName).Update(payment)
if err != nil {
return err
}
record := new(Record)
record.Owner = newName
record.Organization = newName
_, err = session.Where("organization=?", oldName).Update(record)
if err != nil {
return err
}
resource := new(Resource)
resource.Owner = newName
_, err = session.Where("owner=?", oldName).Update(resource)
if err != nil {
return err
}
syncer := new(Syncer)
syncer.Organization = newName
_, err = session.Where("organization=?", oldName).Update(syncer)
if err != nil {
return err
}
token := new(Token)
token.Organization = newName
_, err = session.Where("organization=?", oldName).Update(token)
if err != nil {
return err
}
webhook := new(Webhook)
webhook.Organization = newName
_, err = session.Where("organization=?", oldName).Update(webhook)
if err != nil {
return err
}
return session.Commit()
}

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

@ -111,7 +111,27 @@ 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, groupingPolicies := getPolicies(permission)
if len(groupingPolicies) > 0 {
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
_, err := enforcer.AddPolicies(policies)
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 {

View File

@ -19,7 +19,7 @@ import (
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v2"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/conf"
)
@ -29,7 +29,9 @@ func getEnforcer(permission *Permission) *casbin.Enforcer {
tableName = permission.Adapter
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
adapter, err := xormadapter.NewAdapterWithTableName(conf.GetConfigString("driverName"), conf.GetBeegoConfDataSourceName()+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)
}
@ -39,7 +41,7 @@ func getEnforcer(permission *Permission) *casbin.Enforcer {
r = sub, obj, act
[policy_definition]
p = sub, obj, act
p = sub, obj, act, "", "", permissionId
[role_definition]
g = _, _
@ -63,22 +65,27 @@ m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`
panic(err)
}
// load Policy with a specific Permission
enforcer.LoadFilteredPolicy(xormadapter.Filter{
V5: []string{permission.Owner + "/" + permission.Name},
})
return enforcer
}
func getPolicies(permission *Permission) ([][]string, [][]string) {
var policies [][]string
var groupingPolicies [][]string
permissionId := permission.Owner + "/" + permission.Name
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})
}
}
}
@ -88,29 +95,29 @@ func getPolicies(permission *Permission) ([][]string, [][]string) {
for _, subUser := range roleObj.Users {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subUser, domain, role})
groupingPolicies = append(groupingPolicies, []string{subUser, domain, role, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subUser, role})
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})
groupingPolicies = append(groupingPolicies, []string{subRole, domain, role, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subRole, role})
groupingPolicies = append(groupingPolicies, []string{subRole, role, "", "", "", permissionId})
}
}
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})
}
}
}
@ -152,20 +159,29 @@ 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 := []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2}
if permissionRule.V3 != "" {
request = append(request, permissionRule.V3)
}
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})
if permissionRule.V3 != "" {
requests = append(requests, []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2, permissionRule.V3})
} else {
requests = append(requests, []interface{}{permissionRule.V0, permissionRule.V1, permissionRule.V2})
}
}
permission := GetPermission(permissionRules[0].Id)
enforcer := getEnforcer(permission)

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

@ -17,6 +17,7 @@ package object
import (
"fmt"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/pp"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
@ -24,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"`
@ -45,9 +46,9 @@ type Provider struct {
Host string `xorm:"varchar(100)" json:"host"`
Port int `json:"port"`
DisableSsl bool `json:"disableSsl"`
DisableSsl bool `json:"disableSsl"` // If the provider type is WeChat, DisableSsl means EnableQRCode
Title string `xorm:"varchar(100)" json:"title"`
Content string `xorm:"varchar(1000)" json:"content"`
Content string `xorm:"varchar(1000)" json:"content"` // If provider type is WeChat, Content means QRCode string by Base64 encoding
Receiver string `xorm:"varchar(100)" json:"receiver"`
RegionId string `xorm:"varchar(100)" json:"regionId"`
@ -59,6 +60,7 @@ type Provider struct {
IntranetEndpoint string `xorm:"varchar(100)" json:"intranetEndpoint"`
Domain string `xorm:"varchar(100)" json:"domain"`
Bucket string `xorm:"varchar(100)" json:"bucket"`
PathPrefix string `xorm:"varchar(100)" json:"pathPrefix"`
Metadata string `xorm:"mediumtext" json:"metadata"`
IdP string `xorm:"mediumtext" json:"idP"`
@ -91,7 +93,17 @@ func GetMaskedProviders(providers []*Provider) []*Provider {
}
func GetProviderCount(owner, field, value string) int {
session := GetSession(owner, -1, -1, field, value, "", "")
session := GetSession("", -1, -1, field, value, "", "")
count, err := session.Where("owner = ? or owner = ? ", "admin", owner).Count(&Provider{})
if err != nil {
panic(err)
}
return int(count)
}
func GetGlobalProviderCount(field, value string) int {
session := GetSession("", -1, -1, field, value, "", "")
count, err := session.Count(&Provider{})
if err != nil {
panic(err)
@ -102,7 +114,17 @@ func GetProviderCount(owner, 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)
}
return providers
}
func GetGlobalProviders() []*Provider {
providers := []*Provider{}
err := adapter.Engine.Desc("created_time").Find(&providers)
if err != nil {
panic(err)
}
@ -112,7 +134,18 @@ func GetProviders(owner string) []*Provider {
func GetPaginationProviders(owner string, offset, limit int, field, value, sortField, sortOrder string) []*Provider {
providers := []*Provider{}
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
err := session.Where("owner = ? or owner = ? ", "admin", owner).Find(&providers)
if err != nil {
panic(err)
}
return providers
}
func GetPaginationGlobalProviders(offset, limit int, field, value, sortField, sortOrder string) []*Provider {
providers := []*Provider{}
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
err := session.Find(&providers)
if err != nil {
panic(err)
@ -126,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)
@ -174,6 +207,13 @@ func UpdateProvider(id string, provider *Provider) bool {
return false
}
if name != provider.Name {
err := providerChangeTrigger(name, provider.Name)
if err != nil {
return false
}
}
session := adapter.Engine.ID(core.PK{owner, name}).AllCols()
if provider.ClientSecret == "***" {
session = session.Omit("client_secret")
@ -228,7 +268,7 @@ func (p *Provider) GetId() string {
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
}
func GetCaptchaProviderByOwnerName(applicationId string) (*Provider, error) {
func GetCaptchaProviderByOwnerName(applicationId, lang string) (*Provider, error) {
owner, name := util.GetOwnerAndNameFromId(applicationId)
provider := Provider{Owner: owner, Name: name, Category: "Captcha"}
existed, err := adapter.Engine.Get(&provider)
@ -237,27 +277,65 @@ func GetCaptchaProviderByOwnerName(applicationId string) (*Provider, error) {
}
if !existed {
return nil, fmt.Errorf("the provider: %s does not exist", applicationId)
return nil, fmt.Errorf(i18n.Translate(lang, "provider:the provider: %s does not exist"), applicationId)
}
return &provider, nil
}
func GetCaptchaProviderByApplication(applicationId, isCurrentProvider string) (*Provider, error) {
func GetCaptchaProviderByApplication(applicationId, isCurrentProvider, lang string) (*Provider, error) {
if isCurrentProvider == "true" {
return GetCaptchaProviderByOwnerName(applicationId)
return GetCaptchaProviderByOwnerName(applicationId, lang)
}
application := GetApplication(applicationId)
if application == nil || len(application.Providers) == 0 {
return nil, fmt.Errorf("invalid application id")
return nil, fmt.Errorf(i18n.Translate(lang, "provider:Invalid application id"))
}
for _, provider := range application.Providers {
if provider.Provider == nil {
continue
}
if provider.Provider.Category == "Captcha" {
return GetCaptchaProviderByOwnerName(fmt.Sprintf("%s/%s", provider.Provider.Owner, provider.Provider.Name))
return GetCaptchaProviderByOwnerName(fmt.Sprintf("%s/%s", provider.Provider.Owner, provider.Provider.Name), lang)
}
}
return nil, nil
}
func providerChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
var applications []*Application
err = adapter.Engine.Find(&applications)
if err != nil {
return err
}
for i := 0; i < len(applications); i++ {
providers := applications[i].Providers
for j := 0; j < len(providers); j++ {
if providers[j].Name == oldName {
providers[j].Name = newName
}
}
applications[i].Providers = providers
_, err = session.Where("name=?", applications[i].Name).Update(applications[i])
if err != nil {
return err
}
}
resource := new(Resource)
resource.Provider = newName
_, err = session.Where("provider=?", oldName).Update(resource)
if err != nil {
return err
}
return session.Commit()
}

View File

@ -15,12 +15,15 @@
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"`
Prompted bool `json:"prompted"`
AlertType string `json:"alertType"`
Rule string `json:"rule"`
Provider *Provider `json:"provider"`
}

View File

@ -18,7 +18,7 @@ import (
"fmt"
"strings"
"github.com/astaxie/beego/context"
"github.com/beego/beego/context"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
)
@ -101,9 +101,9 @@ func AddRecord(record *Record) bool {
return affected != 0
}
func GetRecordCount(field, value string) int {
func GetRecordCount(field, value string, filterRecord *Record) int {
session := GetSession("", -1, -1, field, value, "", "")
count, err := session.Count(&Record{})
count, err := session.Count(filterRecord)
if err != nil {
panic(err)
}
@ -121,10 +121,10 @@ func GetRecords() []*Record {
return records
}
func GetPaginationRecords(offset, limit int, field, value, sortField, sortOrder string) []*Record {
func GetPaginationRecords(offset, limit int, field, value, sortField, sortOrder string, filterRecord *Record) []*Record {
records := []*Record{}
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
err := session.Find(&records)
err := session.Find(&records, filterRecord)
if err != nil {
panic(err)
}

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(100) 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

@ -16,6 +16,7 @@ package object
import (
"fmt"
"strings"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
@ -94,6 +95,13 @@ func UpdateRole(id string, role *Role) bool {
return false
}
if name != role.Name {
err := roleChangeTrigger(name, role.Name)
if err != nil {
return false
}
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(role)
if err != nil {
panic(err)
@ -133,3 +141,54 @@ func GetRolesByUser(userId string) []*Role {
return roles
}
func roleChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
var roles []*Role
err = adapter.Engine.Find(&roles)
if err != nil {
return err
}
for _, role := range roles {
for j, u := range role.Roles {
split := strings.Split(u, "/")
if split[1] == oldName {
split[1] = newName
role.Roles[j] = split[0] + "/" + split[1]
}
}
_, err = session.Where("name=?", role.Name).Update(role)
if err != nil {
return err
}
}
var permissions []*Permission
err = adapter.Engine.Find(&permissions)
if err != nil {
return err
}
for _, permission := range permissions {
for j, u := range permission.Roles {
// u = organization/username
split := strings.Split(u, "/")
if split[1] == oldName {
split[1] = newName
permission.Roles[j] = split[0] + "/" + split[1]
}
}
_, err = session.Where("name=?", permission.Name).Update(permission)
if err != nil {
return err
}
}
return session.Commit()
}

View File

@ -240,8 +240,8 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
}
// 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 "", "", fmt.Errorf("err: Issuer URI: %s doesn't exist in the allowed Redirect URI list", authnRequest.Issuer.Url)
}
// get certificate string
@ -251,6 +251,11 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
_, originBackend := getOriginFromHost(host)
// redirect Url (Assertion Consumer Url)
if application.SamlReplyUrl != "" {
authnRequest.AssertionConsumerServiceURL = application.SamlReplyUrl
}
// build signedResponse
samlResponse, _ := NewSamlResponse(user, originBackend, certificate, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer.Url, authnRequest.ID, application.RedirectUris)
randomKeyStore := &X509Key{

View File

@ -24,6 +24,7 @@ import (
"strings"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
saml2 "github.com/russellhaering/gosaml2"
dsig "github.com/russellhaering/goxmldsig"
)
@ -41,10 +42,10 @@ func ParseSamlResponse(samlResponse string, providerType string) (string, error)
return assertionInfo.NameID, nil
}
func GenerateSamlLoginUrl(id, relayState string) (string, string, error) {
func GenerateSamlLoginUrl(id, relayState, lang string) (string, string, error) {
provider := GetProvider(id)
if provider.Category != "SAML" {
return "", "", fmt.Errorf("provider %s's category is not SAML", 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 {

View File

@ -21,6 +21,7 @@ import (
"strings"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/storage"
"github.com/casdoor/casdoor/util"
)
@ -53,8 +54,8 @@ func escapePath(path string) string {
return res
}
func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
escapedPath := escapePath(fullFilePath)
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)
host := ""
@ -69,7 +70,7 @@ func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
host = util.UrlJoin(provider.Domain, "/files")
}
if provider.Type == "Azure Blob" {
host = fmt.Sprintf("%s/%s", host, provider.Bucket)
host = util.UrlJoin(host, provider.Bucket)
}
fileUrl := util.UrlJoin(host, escapePath(objectKey))
@ -92,7 +93,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 {
@ -126,11 +127,16 @@ func UploadFileSafe(provider *Provider, fullFilePath string, fileBuffer *bytes.B
return fileUrl, objectKey, nil
}
func DeleteFile(provider *Provider, objectKey string) error {
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, "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("the provider type: %s is not supported", provider.Type)
return fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type)
}
if provider.Domain == "" {

View File

@ -37,7 +37,16 @@ func (syncer *Syncer) getOriginalUsers() ([]*OriginalUser, error) {
return nil, err
}
return syncer.getOriginalUsersFromMap(results), nil
// Memory leak problem handling
// https://github.com/casdoor/casdoor/issues/1256
users := syncer.getOriginalUsersFromMap(results)
for _, m := range results {
for k := range m {
delete(m, k)
}
}
return users, nil
}
func (syncer *Syncer) getOriginalUserMap() ([]*OriginalUser, map[string]*OriginalUser, error) {
@ -120,7 +129,7 @@ func (syncer *Syncer) updateUserForOriginalFields(user *User) (bool, error) {
}
if user.Avatar != oldUser.Avatar && user.Avatar != "" {
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar)
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, true)
}
columns := syncer.getCasdoorColumns()

View File

@ -18,9 +18,9 @@ import (
"crypto/sha256"
"encoding/base64"
"fmt"
"strings"
"time"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/idp"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
@ -168,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 {
@ -238,25 +242,18 @@ func GetTokenByTokenAndApplication(token string, application string) *Token {
return &tokenResult
}
func CheckOAuthLogin(clientId string, responseType string, redirectUri string, scope string, state string) (string, *Application) {
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("error: grant_type: %s is not supported in this application", 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 "Invalid client_id", 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("Redirect URI: \"%s\" doesn't exist in the allowed Redirect URI list", 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
@ -264,11 +261,11 @@ func CheckOAuthLogin(clientId string, responseType string, redirectUri string, s
return "", application
}
func GetOAuthCode(userId string, clientId string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, host string) *Code {
func GetOAuthCode(userId string, clientId string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, host string, lang string) *Code {
user := GetUser(userId)
if user == nil {
return &Code{
Message: fmt.Sprintf("The user: %s doesn't exist", userId),
Message: fmt.Sprintf("token:The user: %s doesn't exist", userId),
Code: "",
}
}
@ -279,7 +276,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
}
}
msg, application := CheckOAuthLogin(clientId, responseType, redirectUri, scope, state)
msg, application := CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, lang)
if msg != "" {
return &Code{
Message: msg,
@ -322,7 +319,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
}
}
func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, username string, password string, host string, tag string, avatar string) interface{} {
func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, username string, password string, host string, tag string, avatar string, lang string) interface{} {
application := GetApplicationByClientId(clientId)
if application == nil {
return &TokenError{
@ -353,7 +350,7 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
if tag == "wechat_miniprogram" {
// Wechat Mini Program
token, tokenError = GetWechatMiniProgramToken(application, code, host, username, avatar)
token, tokenError = GetWechatMiniProgramToken(application, code, host, username, avatar, lang)
}
if tokenError != nil {
@ -559,7 +556,7 @@ func GetPasswordToken(application *Application, username string, password string
ErrorDescription: "the user does not exist",
}
}
msg := CheckPassword(user, password)
msg := CheckPassword(user, password, "en")
if msg != "" {
return nil, &TokenError{
Error: InvalidGrant,
@ -669,7 +666,7 @@ func GetTokenByUser(application *Application, user *User, scope string, host str
// GetWechatMiniProgramToken
// Wechat Mini Program flow
func GetWechatMiniProgramToken(application *Application, code string, host string, username string, avatar string) (*Token, *TokenError) {
func GetWechatMiniProgramToken(application *Application, code string, host string, username string, avatar string, lang string) (*Token, *TokenError) {
mpProvider := GetWechatMiniProgramProvider(application)
if mpProvider == nil {
return nil, &TokenError{
@ -677,7 +674,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 {
@ -703,7 +700,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
}
// Add new user
var name string
if username != "" {
if CheckUsername(username, lang) == "" {
name = username
} else {
name = fmt.Sprintf("wechat-%s", openId)

View File

@ -24,9 +24,10 @@ import (
type Claims struct {
*User
Nonce string `json:"nonce,omitempty"`
Tag string `json:"tag,omitempty"`
Scope string `json:"scope,omitempty"`
TokenType string `json:"tokenType,omitempty"`
Nonce string `json:"nonce,omitempty"`
Tag string `json:"tag,omitempty"`
Scope string `json:"scope,omitempty"`
jwt.RegisteredClaims
}
@ -37,8 +38,9 @@ type UserShort struct {
type ClaimsShort struct {
*UserShort
Nonce string `json:"nonce,omitempty"`
Scope string `json:"scope,omitempty"`
TokenType string `json:"tokenType,omitempty"`
Nonce string `json:"nonce,omitempty"`
Scope string `json:"scope,omitempty"`
jwt.RegisteredClaims
}
@ -53,6 +55,7 @@ func getShortUser(user *User) *UserShort {
func getShortClaims(claims Claims) ClaimsShort {
res := ClaimsShort{
UserShort: getShortUser(claims.User),
TokenType: claims.TokenType,
Nonce: claims.Nonce,
Scope: claims.Scope,
RegisteredClaims: claims.RegisteredClaims,
@ -72,8 +75,9 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
jti := fmt.Sprintf("%s/%s", application.Owner, name)
claims := Claims{
User: user,
Nonce: nonce,
User: user,
TokenType: "access-token",
Nonce: nonce,
// FIXME: A workaround for custom claim by reusing `tag` in user info
Tag: user.Tag,
Scope: scope,
@ -97,10 +101,12 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
claimsShort.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsShort.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
} else {
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claims.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
}

View File

@ -37,7 +37,7 @@ func generateRsaKeys(bitSize int, expireInYears int, commonName string, organiza
// Encode private key to PKCS#1 ASN.1 PEM.
privateKeyPem := pem.EncodeToMemory(
&pem.Block{
Type: "PRIVATE KEY",
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
},
)

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