Compare commits

...

276 Commits

Author SHA1 Message Date
3b2820cbe3 feat: make redirect_uri really optional in logout route (#2342) 2023-09-18 21:47:49 +08:00
764e88f603 Change MFA issuer 2023-09-18 17:40:11 +08:00
7f298efebc feat: fix Apple OAuth issue (#2338)
* feat: fix sign in with apple bug

* fix username
2023-09-18 17:04:03 +08:00
0fc48bb6cd Remove escapePath() to fix Unicode resource filenames 2023-09-17 21:31:22 +08:00
c3b3840994 fix: fix update score permission check (#2335)
* fix: Fixed the missing permission check when updating the score field.
* Update object/user_util.go
2023-09-16 21:06:20 +08:00
eacc3fae5a fix: handle more errors in downloadImage() 2023-09-15 22:53:09 +08:00
ce7a2e924b feat: fix XML format issue in GenerateCasToken() 2023-09-15 22:38:04 +08:00
ece060d03d feat: fix XML bug in GenerateCasToken() 2023-09-15 18:57:59 +08:00
1276da4daa Check old password for normal user in SetPassword() 2023-09-15 10:21:02 +08:00
616629ef99 Refactor CheckLoginPermission() code 2023-09-15 02:47:53 +08:00
b633ecdcf2 Fix bug that cannot access application's public certificate for non "admin" owner 2023-09-15 00:56:40 +08:00
a12ba7fb85 feat: allow CORS for UserInfo API in OIDC (#2313) 2023-09-13 18:11:13 +08:00
08a0092974 feat: fix alipay payment provider (#2330)
* feat: support alipay payment provider

* feat: update notify params

* feat: update root cert

* feat: update ProviderEditPage.js

* feat: gofumpt
2023-09-13 17:30:51 +08:00
bb04b10e8b Fix JSON issue in GenerateCasToken() 2023-09-13 16:45:11 +08:00
ea1414dfd0 Fix typo 2023-09-13 00:19:32 +08:00
32a8a028d5 Set TOTP issuer to Casdoor 2023-09-12 23:56:39 +08:00
0fe34c2f53 Fix the issue that database syncer can't work with null-fields on source table 2023-09-12 16:06:44 +08:00
dc57c476b7 feat: support acs email provider (#2323)
* feat: support acs email provider

* feat: support acs email provider

* hide Test SMTP Connection button

* fix name acs
2023-09-12 02:13:37 +08:00
a7cb202ee9 feat: fix JSON tag of EmailVerified (#2322)
Signed-off-by: Cattī Crūdēlēs <17695588+wzy9607@users.noreply.github.com>
2023-09-11 18:33:24 +08:00
e5e264628e Remove "RUN mkdir tempFiles" 2023-09-09 20:24:18 +08:00
8d4127f744 feat: improve dashboard UI for mobile devices (#2320) 2023-09-09 16:17:24 +08:00
1305899060 Fix "app" user API denied issue 2023-09-09 15:44:36 +08:00
411a85c7ab Remove useless GetMaxLenStr() 2023-09-09 15:40:35 +08:00
f39358e122 Improve SMS Test's initial value 2023-09-09 02:38:15 +08:00
a84752bbb5 Update go-sms-sender to v0.14.0 2023-09-09 02:15:38 +08:00
e9d8ab8cdb fix: hide tour component for mobile (#2317) 2023-09-08 22:53:46 +08:00
d12088e8e7 feat: fix bug in pricing when signup by phone (#2316)
* fix: fix bug in pricing

* fix: remove log
2023-09-08 21:03:30 +08:00
c62588f9bc Add EmailVerified to UserInfo 2023-09-08 18:27:14 +08:00
16cd09d175 feat: support wechat pay (#2312)
* feat: support wechat pay

* feat: support wechat pay

* feat: update wechatpay.go

* feat: add router /qrcode
2023-09-07 15:45:54 +08:00
7318ee6e3a Improve LocalFileSystemProvider's error handling 2023-09-07 10:49:39 +08:00
3459ef1479 Improve termsOfUse UI and error handling 2023-09-07 10:33:20 +08:00
ca6b27f922 feat: fix notification provider frontend bug and twitter error (#2310) 2023-09-06 23:41:34 +08:00
e528e8883b Add "localhost" to IsRedirectUriValid() 2023-09-06 21:14:58 +08:00
b7cd604e56 Mask user in GenerateCasToken() 2023-09-06 18:36:55 +08:00
3c2fd574a6 Refactor GenerateCasToken() 2023-09-06 18:35:13 +08:00
a9de7d3aef Add groups to permission 2023-09-06 00:10:33 +08:00
9820801634 Make Product's Providers longer (255) 2023-09-05 20:24:24 +08:00
c6e422c3a8 feat: add multiple notification providers (#2302)
* feat: support dingtalk notification provider

* feat: support lark notification provider

* feat: support microsoft teams notification provider

* feat: support bark notification provider

* feat: support pushover notification provider

* feat: support pushbullet notification provider

* feat: support slack notification provider

* feat: support webpush notification provider

* fix go-test error

* update notify repository

* feat: support discord notification provider

* feat: support google chat notification provider

* feat: support Line notification provider

* feat: support matrix notification provider

* feat: support twitter notification provider

* fix lint

* add no proxy provider

* update setting.js

* update social_teams
2023-09-05 17:05:34 +08:00
bc8e9cfd64 feat: storage provider's domain initial value bug (#2303) 2023-09-05 14:53:32 +08:00
c1eae9fcd8 Fix TotpMfa's Verify() 2023-09-04 19:21:26 +08:00
6dae6e4954 docs: fix all dead links (#2297)
https://github.com/Selflocking/linkchecker/actions/runs/6058177987
2023-09-03 21:19:23 +08:00
559a91e8ee feat: fix bug that failed to set password after changing username (#2296)
* fix: failed to set password after changing username

When we add a new member to an organization using Casdoor, Casdoor will automatically generate a member with a random username, such as "user_qvducc". When we change the username, for example, to "yunshu", an issue arises where we are unable to successfully edit the password. This is because Casdoor searches for a user based on `owner/username`, and before any changes are saved, the username in the database remains "user_qvducc". However, the frontend uses `orgName/yunshu` instead of `orgName/user_qvducc` to send the request to change the password. As a result, the backend cannot find the user and the password change fails.

* Update user.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-09-03 00:04:48 +08:00
b0aaf09ef1 Add 7 new i18n languages 2023-09-02 18:49:43 +08:00
7e2f67c49a Fix i18n error 2023-09-02 18:33:19 +08:00
e584a6a111 Support using "?allowEmpty=1" to bypass empty displayName check in update-user API 2023-09-02 11:59:07 +08:00
6700d2e244 fix: show error when frontend HTML entry does not exist (#2289)
* fix: add response when web file not found

The error flow is as follows:

Assuming my directory structure is as follows:

```tree
├── GitHub
│   ├── casdoor  # code repository
              ├── casdoor # compiled binary file
```

Execute the program in the `GitHub` directory:

```bash
./casdoor/casdoor
```

The working directory at this time is `GitHub`.

According to the code:

```go
func StaticFilter(ctx *context.Context) {
	urlPath := ctx.Request.URL.Path

   /// omitted

	path := "web/build"
	if urlPath == "/" {
		path += "/index.html"
	} else {
		path += urlPath
	}

	if !util.FileExist(path) {
		path = "web/build/index.html"
	}
	if !util.FileExist(path) {
		return
	}

    /// omitted
}
```

If the user accesses `/`, according to this code, the returned value is actually `web/build/index.html`. But the current directory is GitHub, and there is no `web/build/index.html` file. According to the following code, it will directly return:

```go
	if !util.FileExist(path) {
		return
	}
```

Then in `main.go`:

```go
	beego.InsertFilter("*", beego.BeforeRouter, routers.StaticFilter)
	beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter)
	beego.InsertFilter("*", beego.BeforeRouter, routers.CorsFilter)
	beego.InsertFilter("*", beego.BeforeRouter, routers.ApiFilter)
	beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter)
	beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)
```

The introduction of `beego.InsertFilter` is as follows:

```
func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App

InsertFilter adds a FilterFunc with pattern condition and action constant. The pos means action constant including beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
```

When the `params` parameter is `false`, it runs multiple filters. The default is `true`.

So normally, if

```go
beego.InsertFilter("*", beego.BeforeRouter, routers.StaticFilter)
```

response something, the following filters will not be executed. But because the file does not exist, the function directly returns, causing the subsequent filters to continue executing. When it reaches

```go
beego.InsertFilter("*", beego.BeforeRouter, routers.ApiFilter)
```

it will start to check permissions:

```
subOwner = anonymous, subName = anonymous, method = GET, urlPath = /login, obj.Owner = , obj.Name = , result = deny
```

Then it will report this error:

```json
{
    "status": "error",
    "msg": "Unauthorized operation",
    "data": null,
    "data2": null
}
```

The solution should be:

```go
func StaticFilter(ctx *context.Context) {
	urlPath := ctx.Request.URL.Path

   /// omitted

	path := "web/build"
	if urlPath == "/" {
		path += "/index.html"
	} else {
		path += urlPath
	}

	if !util.FileExist(path) {
		// todo: response error: page not found
		return
	}

    /// omitted
}
```

* Update static_filter.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-09-02 00:06:04 +08:00
0c5c308071 fix: sendCasAuthenticationResponseErr when pgtUrlObj if not valid url (#2287)
* fix: sendCasAuthenticationResponseErr when pgtUrlObj if not valid url

check pgtUrlObj.Scheme first will cause panic if url.Parse returns error.

* Update cas.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-09-01 22:26:57 +08:00
0b859197da Fix CAS "/proxyValidate" API 2023-09-01 21:47:26 +08:00
3078409343 Add CertPublicKey to Application 2023-09-01 21:16:51 +08:00
bbf2db2e00 feat: support to use a different db schema for pg (#2281) 2023-09-01 18:02:13 +08:00
0c7b911ce7 Fix enforcer edit page logic 2023-09-01 01:30:50 +08:00
2cc55715ac Add app.conf existence check 2023-09-01 01:25:45 +08:00
c829bf1769 Fix DummyPaymentProvider's return URL 2023-09-01 01:25:15 +08:00
ec956c12ca Fix Email duplicated issue in update-user 2023-08-31 23:44:40 +08:00
d3d4646c56 feat: fix can not create db when using pg with a dbname in DSN (#2280)
* fix: can not create db when using pg with a dbname in DSN

* Update ormer.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-31 18:05:38 +08:00
669ac7c618 Don't encrypt user pass when user.PasswordType is non-empty when adding users 2023-08-31 17:49:36 +08:00
6715efd781 Fix enforcer edit page 2023-08-31 17:32:36 +08:00
953be4a7b6 feat: support subscription periods (yearly/monthly) (#2265)
* feat: support year/month subscription

* feat: add GetPrice() for plan

* feat: add GetDuration

* feat: gofumpt

* feat: add subscription mode for pricing

* feat: restrict auto create product operation

* fix: format code

* feat: add period for plan,remove period from pricing

* feat: format code

* feat: remove space

* feat: remove period in signup page
2023-08-30 17:13:45 +08:00
943cc43427 Fix payment list and product edit actions 2023-08-28 21:01:23 +08:00
1e5ce7a045 Fix crash in syncUsersNoError() 2023-08-28 01:51:06 +08:00
7a85b74573 fix: fix tour disabled state (#2264)
* fix: distinguish between pages that can tour or not

* Update OpenTour.js

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-27 23:18:14 +08:00
7e349c1768 feat: fix crash bug in getSteps() 2023-08-27 21:58:58 +08:00
b19be2df88 fix: change the id to key in syncer (#2263) 2023-08-27 20:57:27 +08:00
fc3866db1c Use XORM grammar in syncer 2023-08-27 18:15:23 +08:00
bf2bb31e41 Add sslMode for syncer 2023-08-27 17:07:19 +08:00
ec8bd6f01d feat: add tour for list pages (#2243) 2023-08-27 16:40:31 +08:00
98722fd681 Fix crash in app list page for normal user 2023-08-27 11:31:48 +08:00
221c55aa93 Fix yarn build cmd 2023-08-27 11:17:18 +08:00
988b26b3c2 Return error for RunSyncer() 2023-08-27 02:22:37 +08:00
7e3c361ce7 Add all webhook events 2023-08-26 23:50:24 +08:00
a637707e77 Fix null bug in IsAdminOrSelf() 2023-08-26 10:39:46 +08:00
7970edeaa7 feat: password and invitation code verification rules (#2258) 2023-08-25 21:16:21 +08:00
9da2f0775f fix: fix bug in Pricing (#2255) 2023-08-25 19:27:46 +08:00
739a9bcd0d feat: add CasvisorUrl 2023-08-25 11:56:12 +08:00
fb0949b9ed Fix docker cannot get version bug 2023-08-25 11:49:47 +08:00
27ed901167 Restrict sysinfo page to global admin 2023-08-25 11:20:11 +08:00
ceab662b88 Remove dup swagger page 2023-08-25 11:09:59 +08:00
05b2f00057 feat: support Pricings flow (#2250)
* feat: fix price display

* feat: support subscription

* feat: fix select-plan-> signup -> buy-plan -> login flow

* feat: support paid-user to login and jump to the pricing page

* feat: support more subscription state

* feat: add payment providers for plan

* feat: format code

* feat: gofumpt

* feat: redirect to buy-plan-result page when user have pending subscription

* feat: response err when pricing don't exit

* Update PricingListPage.js

* Update ProductBuyPage.js

* Update LoginPage.js

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-24 23:20:50 +08:00
8073dfa88c Remove tmpFiles folder usage 2023-08-24 22:03:36 +08:00
1eeeb64a0c Add checkModel() for UserGroupEnforcer 2023-08-24 18:22:23 +08:00
f5e0461cae feat: add invitation code for signup feature (#2249)
* feat: add invitation code for signup feature

* feat: add invitation code for signup feature
2023-08-24 13:42:17 +08:00
a0c5eb241f feat: add fields to syncer (PreferredMfaType, TotpSecret, SignupApplication) #2239 (#2245) 2023-08-23 21:40:00 +08:00
4d8edcc446 fix: dropped controllers err (#2244)
Signed-off-by: Lars Lehtonen <lars.lehtonen@gmail.com>
2023-08-23 21:37:51 +08:00
2b23c04f49 fix: add SignupApplication and type for user synced from LDAP (#2240) 2023-08-21 22:52:35 +08:00
e60ee52d91 feat: replace satori/go.uuid with google/uuid (#2238) 2023-08-21 13:58:15 +08:00
c54b54ca19 fix: Adjust custom http to notification provider (#2237)
* feat: Adjust custom http to notification provider

* fix go linter

* update ProviderEditPage

* update ProviderEditPage
2023-08-20 21:04:30 +08:00
f0e097e138 feat: fix home page (#2236)
* fix: home page

* fix: home page
2023-08-20 00:58:39 +08:00
25ec1bdfa8 Fix bug in getUserOrganization() 2023-08-20 00:53:51 +08:00
ea7718d7b7 Use Casvisor for records 2023-08-20 00:44:01 +08:00
463fa8b636 Add ormer_session.go 2023-08-19 18:41:08 +08:00
11895902f4 Move getCreateDatabaseFlag() to ormer 2023-08-19 16:44:34 +08:00
15269d3315 Refactor out conf_quota.go 2023-08-19 16:39:21 +08:00
4468859795 Improve sendTest msg 2023-08-19 12:47:51 +08:00
914128a78a fix: Support Telegram Notification provider (#2225)
* fear: support telegram provider

* fix: fix telegram logo

* fix: fix telegram bot package

* Update telegram.go

* Update notification.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-19 12:33:00 +08:00
e5a189e0f4 fix: remove isGlobalAdmin field in user (#2235)
* refactor: remove isGlobalAdmin field in user

* fix: upload xlsx

* fix: remove field in account table
2023-08-19 12:23:15 +08:00
a07216d0e1 Improve contentType parsing in downloadImage() 2023-08-19 02:35:45 +08:00
fec54944dd feat: fix CAS login bug (#2230)
* fix: cas login

* fix: cas login

* feat: rollback get-default-app change

* fix : move cas restrict logic to GetApplicationLogin()

* fix: format code

* fix: fix getOAuthGetParameters for cas

* fix: fix getOAuthGetParameters for cas

* fix: cas login
2023-08-19 01:15:41 +08:00
a2db61cc1a chore: Revert "feat: restrict redirectUrls for CAS login" (#2234)
This reverts commit b7a37126ad.
2023-08-19 00:30:35 +08:00
134541acde chore: put some dev dependency package to right place (#2232) 2023-08-18 22:17:16 +08:00
59fca0342e chore: fix yarn build warning (#2231) 2023-08-18 21:25:57 +08:00
abfc464155 Remove isEnabled for model, adapter and enforcer, improve UI 2023-08-18 19:22:47 +08:00
a41f6880a2 feat: move policy table from adapter to enforcer and improve it (#2228)
* feat: improve policiy table

* feat: add connection test in AdapterEditPage.js

* feat: update button style
2023-08-18 19:00:21 +08:00
d12117324c feat: support admin to enable MFA for other users (#2221)
* feat: support admin enable user sms and email mfa

* chore: update ci

* chore: update ci
2023-08-17 17:19:24 +08:00
1a6c9fbf69 Fix typo in README 2023-08-17 14:47:09 +08:00
dd60d79af9 Fix typo in README 2023-08-17 14:46:10 +08:00
73d314c7fe Add MfaTotpPeriodInSeconds param 2023-08-16 21:48:54 +08:00
27959e0f6f fix: fix crash in UserEditPage.js 2023-08-16 15:57:48 +08:00
47f40c5b24 feat: support 3 more UI languages (#2218)
Signed-off-by: baihhh <2542274498@qq.com>
2023-08-16 15:54:34 +08:00
2ff9020884 feat: support Stripe payment provider (#2204)
* feat: add stripe payment provider

* feat: support stripe payment

* feat: delete todo comment

* feat: remove description struct

* feat: change outOrderId->orderId
2023-08-15 00:16:30 +08:00
abaf4ca8d9 Make GetDashboard() faster 2023-08-14 15:43:09 +08:00
8ff0cfd6ec feat: support dashboard in homepage (#2207)
* feat: support dashboard

* feat: support dashboard
2023-08-14 15:31:29 +08:00
7a2a40edcc Improve table columns 2023-08-14 12:19:02 +08:00
b7a001ea39 Fix property empty issue 2023-08-14 12:09:50 +08:00
891e8e21d8 feat: support Web3-Onboard provider (#2209)
* feat: add Web3-Onboard idp

* feat: update Web3-Onboard logo

* feat: update package.json

* feat: remove unused package

* feat: add yarn build param --max_old_space_size=4096

* feat: remove log

* feat: add Wallet configure

* feat: remove hardware wallets
2023-08-13 23:58:57 +08:00
80b0d26813 fix: synchronize update the syncers (#2201)
Signed-off-by: baihhh <2542274498@qq.com>
2023-08-13 22:30:57 +08:00
db4ac60bb6 feat: fix LDAP mobile field incorrect mapped (#2206) 2023-08-12 13:45:26 +08:00
33a922f026 Add custom HTTP SMS provider 2023-08-12 12:52:53 +08:00
9f65053d04 Improve i18n 2023-08-12 02:44:38 +08:00
be969e5efa Fix typo 2023-08-11 22:18:35 +08:00
9156bd426b ci: Show provider.displayName in signin button 2023-08-11 16:29:52 +08:00
fe4a4328aa feat: refactor code in InitApi() 2023-08-11 16:17:29 +08:00
9899022bcd fix: check enforcer should not be nil (#2199)
* fix: check enforcer should not be nil

* fix: check enforcer should not be nil

* Update user.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-11 12:31:49 +08:00
1a9d02be46 feat: use the casbin model to store relationships between users and groups (#2178)
* fix:reslove conflict

* fix: remove interface
2023-08-11 10:59:18 +08:00
eafaa135b4 Change builtInAvailableField back to 5 2023-08-11 02:45:11 +08:00
6746551447 Improve error message in InitEnforcer() 2023-08-11 02:36:29 +08:00
3cb46c3628 Add isKey to syncer's table 2023-08-09 00:33:04 +08:00
558bcf95d6 feat: save policy in adapter edit page (#2190)
* fix: save policy in adapter

* fix: disable edit for builtin adapter
2023-08-09 00:12:53 +08:00
bb937c30c1 Fix empty cert in getPaymentProvider() 2023-08-08 22:37:48 +08:00
8dfdf7f767 ci: add GoogleCloud and QiNiu in Storage (#2188)
* feat: add GoogleCloud and QiNiu in Storage

Signed-off-by: baihhh <2542274498@qq.com>

* Update qiniu_cloud.go

* Update storage.go

---------

Signed-off-by: baihhh <2542274498@qq.com>
Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-08 22:34:55 +08:00
62b2082e82 Add getUserOrganization() to user edit page 2023-08-08 21:58:27 +08:00
a1806439f8 Add UserPrincipalName and MemberOf to get-ldap-users API 2023-08-08 20:18:47 +08:00
01e58158b7 feat: Remove useless code 2023-08-08 19:16:55 +08:00
15427ad9d6 fix: fix add provider error (#2184) 2023-08-07 17:22:32 +08:00
d058f78dc6 fix: fix broken links (#2181) 2023-08-07 01:02:03 +08:00
fd9dbf8251 feat: add multiple SMS providers (#2182)
* feat: add amazon sns and azure acs provider

* feat: add msg91 sms provider

* feat: add infobip sms provider

* feat: add ucloud sms provider

* feat: add baidu cloud sms provider

* fix: fix logo and azure acs
2023-08-07 00:59:17 +08:00
3220a04fa9 fix: use org/groupName replace groupName (#2180) 2023-08-06 20:16:44 +08:00
f06a4990bd fix: rename in init.go (#2179)
* fix: rename in init.go

* fix: remove blank line

* fix: remove blank line

* Update init.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-08-06 13:07:30 +08:00
9df7de5f27 Improve menu icons 2023-08-05 18:00:24 +08:00
56c808c091 Improve menu 2023-08-05 17:41:35 +08:00
9fd2421564 Update @ant-design/cssinjs dependency to avoid build error 2023-08-04 01:22:57 +08:00
689d45c7fa feat: fix org name cannot be changed bug 2023-08-03 18:48:37 +08:00
c24343bd53 Fix XxxChangeTrigger() doesn't return error bug 2023-08-03 18:45:49 +08:00
979f43638d Change builtInAvailableField to 10 2023-08-03 18:17:15 +08:00
685a4514cd fix: revert adapter port vartype to int (#2174) 2023-08-03 09:35:16 +08:00
a05ca3af24 feat: use role ID to search in GetPermissionsAndRolesByUser() (#2170) 2023-08-02 20:58:06 +08:00
c6f301ff9e Support svg in downloadImage() 2023-07-31 20:23:28 +08:00
d7b2bcf288 feat: support payment cancel state (#2165) 2023-07-31 15:24:13 +08:00
67ac3d6d21 Fix typo 2023-07-31 15:23:44 +08:00
912d5c6a7f fix: support enforcerId parameter in Enforce API (#2164) 2023-07-31 00:20:53 +08:00
32fbb5b534 Support custom provider for storage API 2023-07-30 23:19:45 +08:00
21004f3009 Fix GetResources() missing items bug 2023-07-30 22:47:14 +08:00
463bacd53b Add GetDirectResources() 2023-07-30 22:01:10 +08:00
78dc660041 feat: support 3 more language (#2163)
Signed-off-by: baihhh <2542274498@qq.com>
2023-07-30 20:45:47 +08:00
2fb9674171 Fix file not exist panic in StaticFilter() 2023-07-30 19:03:21 +08:00
55c522d3b7 Improve provider type input box 2023-07-30 17:31:36 +08:00
f879170663 Remove AI related code 2023-07-30 14:39:27 +08:00
12e5d9b583 Remove adapter.file 2023-07-30 12:08:05 +08:00
eefa1e6df4 fix: fix paypal payment provider and refactor payment code (#2159)
* feat: support paypal payment provider

* feat: support paypal flow

* feat: use owner replace org for payment

* feat: update paypal logic

* feat: gofumpt

* feat: update payment

* fix: fix notify

* feat: delete log
2023-07-30 11:54:42 +08:00
026fb207b3 fix: remove model in adapter page (#2161) 2023-07-29 23:42:08 +08:00
ea10f8e615 feat: make hard-coded authz adapter editable, rename adapter to ormer (#2149)
* refactor: rename casbinAdapter to casdoorAdapter

* feat: add initEnforcer

* fix: router

* refactor: make hard-coded code configurable

* fix: data type

* feat: support sqlite3

* feat: disable delete and edit name for built in resources

* feat: optimize code

* fix: init

* fix: e2e

* fix: remove datasourcename

* fix: revert rename

* refactor: change all ORM's Adatper to Ormer

* refactor: name
2023-07-29 15:07:04 +08:00
74b058aa3f Fix sync-ldap-users() bug, brought by: 666ff48837 2023-07-29 13:14:55 +08:00
6c628d7893 Fix static path not changed bug in makeGzipResponse() 2023-07-29 12:23:48 +08:00
a38896e4d8 Improve swagger docs 2023-07-29 11:35:03 +08:00
5f054c4989 Fix product links 2023-07-28 15:08:45 +08:00
fb16d8cee6 fix: not set count of enforcers to the response (#2155) 2023-07-28 14:46:11 +08:00
5e4ba4f338 feat: add authorize button and defaultValue (#2152)
Signed-off-by: baihhh <2542274498@qq.com>
2023-07-27 23:55:35 +08:00
ca47af2ee1 Make post_logout_redirect_uri optional for logout 2023-07-27 23:26:30 +08:00
59da104463 fix: update ldap admin pwd only if changed (#2146)
* fix ldap pwd update

* fix: linter

* fix: simplify check
2023-07-27 17:49:15 +08:00
c5bb916651 fix: fix response data in PricingPage.js (#2143) 2023-07-27 10:46:31 +08:00
e98264f957 fix: application fails to call /api/get-resources (#2139)
just like other apis, resource.go.GetResources() no longer calls ApiController.RequireSignedInUser() to auth or check
2023-07-26 17:19:00 +08:00
6a952952a8 fix: unmask application for org admin (#2138)
* feat: unmask application with user admin

* Update application.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-07-26 17:17:49 +08:00
ba8a0f36be Support custom actions in permission edit page 2023-07-26 14:49:45 +08:00
b5e9084e5d feat: en/decodeURI in permission/role name (#2137) 2023-07-26 13:08:35 +08:00
55d5ae10f2 fix: fix infinite loop in containsRole() (#2136) 2023-07-25 20:53:08 +08:00
6986dad295 Use arg to control createDatabaseForPostgres() 2023-07-25 18:36:15 +08:00
949feb18af feat: add basic enforcer manager (#2130)
* feat: add basic enforcer manager

* chore: generate swagger
2023-07-25 17:17:59 +08:00
d1f88ca9b8 feat: support google one tap signin (#2131)
* feat: add google one tap support

* feat: gofumpt

* feat: add google provider rule conf

* feat: update i18n
2023-07-25 15:49:15 +08:00
bfe8e5f3e7 fix: fix response data assignment error (#2129) 2023-07-25 13:52:31 +08:00
702ee6acd0 Print log for StartLdapServer()'s error 2023-07-25 01:49:43 +08:00
0a9587901a fix: fix response data assignment error in ApplicationEditPage.js (#2126) 2023-07-24 20:09:09 +08:00
577bd6ce58 feat: fix response data assignment error (#2123) 2023-07-24 14:52:30 +08:00
3c4112dd44 refactor: optimize the code to getEnforcer (#2120) 2023-07-24 14:02:34 +08:00
b7a37126ad feat: restrict redirectUrls for CAS login (#2118)
* feat: support cas restricted login

* feat: add cas login i18n

* feat: add CheckCasService for all cas api

* feat: gofumpt

* feat: replace 404

* feat: reuse i18n

* feat: delete CheckCasService

* Update token_cas.go

* Update LoginPage.js

* Update token_cas.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-07-24 11:47:31 +08:00
8669d5bb0d chore: hide field of IntranetEndpoint in Tencent COS storage provider (#2117) 2023-07-23 19:02:42 +08:00
aee3ea4981 feat: improve TermsOfUse UI in mobile (#2106)
* style: Mobile interface adaptation

Signed-off-by: baihhh <2542274498@qq.com>

* Update index.css

---------

Signed-off-by: baihhh <2542274498@qq.com>
Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-07-23 15:28:13 +08:00
516f4b7569 Fix response of /api/get-sorted-users and /api/get-user-count 2023-07-23 14:46:38 +08:00
7d7ca10481 fix: hide fields of minio storage provider (#2115)
* feat: hide field of minio storage provider

* feat: hide field of domain in minio storage provider
2023-07-23 14:40:30 +08:00
a9d4978a0f chore: hide fields of local file system storage provider (#2109)
* style: adjust local file system storage

* style: disable domain when use local file system
2023-07-23 11:48:15 +08:00
09f40bb5ce Fix id of "/api/get-resource" API 2023-07-23 11:33:48 +08:00
a6f803aff1 feat: refactor code to use responseOK everywhere (#2111)
* refactor: use responseOK return frontend format json data

* revert handle error

* revert handle error
2023-07-23 09:49:16 +08:00
fc9528be43 Add createDatabaseForPostgres() 2023-07-22 16:19:13 +08:00
58e8f9f90b feat: fix Effect in Casbin rule (#2103)
* fix: Add `Effect` to Casbin rule of role

fix: https://github.com/casdoor/casdoor/issues/2102

* Update permission_enforcer.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-07-21 18:01:37 +08:00
e850e33f37 Fix error message of missing cert when login 2023-07-20 19:45:22 +08:00
d7110ff8bf feat: support MetaMask provider (#2084)
* feat: add metamask provider

* feat: add eth login

* feat: check eth sign

* feat: finish metamask signin/signup

* feat: support MetaMask provider link/unlink

* feat: update web/craco.config.js to handle polyfill

* feat: gofumpt idp/metamask.go

* feat: update MetaMask logo path

* feat: support MetaMask avatar
2023-07-20 17:51:36 +08:00
f923a8f0d7 fix: provide detailed description of ldap in swagger (#2094)
* provide detailed description of ldap in swagger

* modify the directory of swagger

fix: provide detailed description of ldap in swagger
2023-07-20 12:32:48 +08:00
7bfb74ba18 Fix typo 2023-07-19 19:34:43 +08:00
38f031bc86 Show access secret if isAdminOrSelf is true in get-user and get-account APIs 2023-07-19 19:14:53 +08:00
5c441d195c Add Effect to Casbin rule of add-permission 2023-07-19 18:52:22 +08:00
0639564d27 fix: check group name cannot be same as organization name (#2090) 2023-07-19 11:37:28 +08:00
6c647818ca feat: add "Sender number" input for Twilio SMS provider 2023-07-18 22:46:56 +08:00
8bc73d17aa feat: fix bug that themeEditor can not load saved theme data (#2085) 2023-07-17 22:57:55 +08:00
1f37c80177 feat: refactor code to add getStorageProvider() 2023-07-17 15:59:37 +08:00
7924fca403 fix: hidden bug of "like" query (#2082) 2023-07-16 17:11:32 +08:00
bd06996bab Improve CorsFilter for login API 2023-07-15 19:29:48 +08:00
19ab168b12 Fix panic in func (c *ApiController) GetUser() if no user exists in DB 2023-07-14 20:57:59 +08:00
854a74b73e feat: fix the error when user uploads avatar to minio (https) (#2078)
* fix: Error reported when user uploads avatar to minio (https)

* Update provider.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-07-14 15:58:30 +08:00
beefb0b432 fix: fix event-stream streaming output in prod mode (#2076) 2023-07-14 11:59:26 +08:00
d8969e6652 Support EnableSigninSession after SAML login 2023-07-14 11:27:18 +08:00
666ff48837 Use id param in /sync-ldap-users API 2023-07-13 00:14:18 +08:00
0a0c1b4788 Fix "Groups is immutable" bug when updating a user 2023-07-13 00:03:18 +08:00
438c999e11 Add password mask to /get-ldaps and /get-ldap APIs 2023-07-12 23:21:47 +08:00
a193ceb33d Fix bug in TestDeployStaticFiles() 2023-07-12 23:11:02 +08:00
caec1d1bac Only consider x509 certs in /.well-known/jwks API 2023-07-12 22:39:39 +08:00
0d48da24dc feat: fix wrong rowKey for tables (#2070) 2023-07-12 21:12:36 +08:00
de9eeaa1ef fix: init groups modify rule with admin (#2054) 2023-07-11 09:49:49 +08:00
ae6e35ee73 feat: fix bug that the password input disappears in login window (#2051)
Signed-off-by: baihhh <2542274498@qq.com>
2023-07-08 23:46:31 +08:00
a58df645bf fix: fix state after mfa is enabled (#2050) 2023-07-08 22:35:31 +08:00
68417a2d7a fix: /api/upload-resource panics when parsing file_type (#2046) 2023-07-07 16:18:25 +08:00
9511fae9d9 docs: add swagger docs for Resource-API (#2044)
swagger files are all auto generated.
2023-07-07 14:28:10 +08:00
347d3d2b53 feat: fix bugs in MFA (#2033)
* fix: prompt mfa binding

* fix: clean session when leave promptpage

* fix: css

* fix: force enable mfa

* fix: add prompt rule

* fix: refactor directory structure

* fix: prompt notification

* fix: fix some bug and clean code

* fix: rebase

* fix: improve notification

* fix: i18n

* fix: router

* fix: prompt

* fix: remove localStorage
2023-07-07 12:30:07 +08:00
6edfc08b28 Refactor the code 2023-07-07 00:13:05 +08:00
bc1c4d32f0 feat: user can upload ID card info (#2040)
* feat:user can upload ID card(#1999)

Signed-off-by: baihhh <2542274498@qq.com>

* feat: user can upload ID card, add diff languages

Signed-off-by: baihhh <2542274498@qq.com>

---------

Signed-off-by: baihhh <2542274498@qq.com>
2023-07-06 20:36:32 +08:00
96250aa70a docs: replace gitter links with discord (#2041) 2023-07-06 18:16:16 +08:00
3d4ca1adb1 feat: support custom user mapping (#2029)
* feat: support custom user mapping

* fix: parse id to string

* Update data.json

* Update data.json

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-07-05 20:35:02 +08:00
ba97458edd feat: fix StaticFilter issue 2023-07-05 17:54:39 +08:00
855259c6e7 feat: improve getOriginFromHost() for local machine name 2023-07-05 09:51:08 +08:00
28297e06f7 feat: IntrospectToken return the right Jti (JWT ID instead of User Id) (#2035) 2023-07-03 19:01:06 +08:00
f3aed0b6a8 Fix null panic in GetOrganizationByUser() 2023-07-03 14:56:14 +08:00
35e1f8538e feat: fix panic when url.Parse() fails to parse URL (#2034) 2023-07-03 12:35:22 +08:00
30a14ff54a Fix null issue in getDefaultApplication() 2023-07-02 09:44:48 +08:00
1ab7a54133 Add DefaultApplication to conf 2023-07-02 09:15:22 +08:00
0e2dad35f3 Improve OrganizationSelect width 2023-06-30 02:04:44 +08:00
d31077a510 Remove conf values 2023-06-30 01:38:48 +08:00
eee9b8b9fe feat: add organization context select box for admin (#2013)
* feat: organization as context

* feat: organization as context with backend filtration

* Update app.conf

* update app.conf and hide organization select for mobile.

---------

Co-authored-by: dplynsky <dplynsky@ptsecurity.com>
Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-06-30 01:32:34 +08:00
91cb5f393a fix: fix Swagger docs page (#2025)
Signed-off-by: baihhh <2542274498@qq.com>
2023-06-30 00:48:39 +08:00
807aea5ec7 feat: add tags to application (#2027)
* feat: add tags to application

* fix: fix for merge master

* feat: update i18n(backend&frontend) for application tags
2023-06-30 00:04:12 +08:00
1c42b6e395 fix: refactor the idp and regex code (#2030)
* refactor: validate util and idp

* chore: clean code

* chore: clean code
2023-06-29 21:44:14 +08:00
49a73f8138 fix: getOrganization without pagination for global admin (#2028)
* fix: getOrganization without pagination for global admin return only built-in org

* fix gofumpt
2023-06-29 18:56:19 +08:00
55784c68a3 Fix bug in /get-organizations API for org admin 2023-06-28 09:19:39 +08:00
8080b10b3b feat: show code signin page with password disabled (#2021) 2023-06-28 00:38:48 +08:00
cd7589775c feat: replace all panic by response err (#1993)
* fix: missing return after response error

* feat: handle error in frontend

* feat: disable loading and catch org edit error

* chore: i18 for error message

* chore: remove break line

* feat: application catching error
2023-06-27 21:33:47 +08:00
0a8c2a35fe feat: add TOTP multi-factor authentication (#2014)
* feat: add totp multi-factor authentication

* feat: add license

* feat:i18n and update yarn.lock

* feat:i18n

* fix: i18n
2023-06-24 18:39:54 +08:00
d1e734e4ce fix: set the default value of user.Groups for syncer (#2016)
fix: set the default value of user.Groups for syncer
2023-06-24 18:29:50 +08:00
68f032b54d fix: add isReadOnly for syncer (#2015)
* feat: add read only mod for syncer

* feat: change readOnlyEnable to isReadOnly
2023-06-24 17:56:41 +08:00
1780620ef4 feat: handle error when permission not found (#2012) 2023-06-24 00:30:43 +08:00
5c968ed1ce Fix avatar cannot show issue 2023-06-23 15:53:41 +08:00
4016fc0f65 Add EnableChatPages to Conf 2023-06-23 11:35:34 +08:00
463b3ad976 fix: refactor and optimize Enforce() API (#2009) 2023-06-22 17:45:24 +08:00
b817a55f9f Fix error handling in SetPassword() 2023-06-22 14:51:56 +08:00
2c2ddfbb92 feat: optimize batch-enforce (#1997) 2023-06-22 14:40:09 +08:00
cadb533595 fix: unsafe verification username in CheckUsername (#2006)
* Customization of the initialization file

* Unsafe verification username in CheckUsername
2023-06-21 23:20:23 +08:00
a3b0f1fc74 feat: add owner to getUserByWechatId() 2023-06-21 21:29:53 +08:00
c391af4552 feat: improve MFA by using user's own Email and Phone (#2002)
* refactor: mfa

* fix: clean code

* fix: clean code

* fix: fix crash and improve robot
2023-06-21 18:56:37 +08:00
6ebca6dbe7 fix: Gosec/sec fixes (#2004)
* Customization of the initialization file

* fix: G601 (CWE-118): Implicit memory aliasing in for loop

* fix: G304 (CWE-22): Potential file inclusion via variable

* fix: G110 (CWE-409): Potential DoS vulnerability via decompression bomb
2023-06-21 18:55:20 +08:00
d505a4bf2d Remove org API calls in PasswordModal page 2023-06-21 00:49:03 +08:00
812bc5f6b2 Fix "nu" bug in GetLanguage() 2023-06-20 21:16:01 +08:00
f6f4d44444 feat: remove url.JoinPath() to be compatible with Go 1.17 (#1995) 2023-06-20 17:44:40 +08:00
926e73ed1b fix: fix "Accept-Language" parsing in request (#1996) 2023-06-20 17:43:48 +08:00
65716af89e feat: deprecate the user group relation table (#1990)
* fix: deprecate the user group relation table

* fix: clean code

* fix: fix trigger

* Update group.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-06-19 19:08:45 +08:00
d9c4f401e3 Fix error in downloadImage() 2023-06-19 17:52:01 +08:00
58aa7dba6a Fix groups in GetUserInfo() 2023-06-19 11:06:55 +08:00
29fc820578 Set User.groups to [] 2023-06-19 09:42:17 +08:00
d0ac265c91 fix: Deprecate the id field in group (#1987) 2023-06-18 23:33:13 +08:00
3562c36817 feat: Revert "fix: fix URL path in MinIO storage provider" (#1988)
This reverts commit 3699177837.
2023-06-18 23:08:40 +08:00
7884e10ca3 Refactor adapter's owner and organization 2023-06-18 00:22:12 +08:00
12dee8afd3 Fix null options in checkPasswordComplexity() 2023-06-17 22:38:02 +08:00
ac4b870309 Improve getFaviconFileBuffer() 2023-06-17 12:50:01 +08:00
b9140e2d5a Refactor refreshAvatar() 2023-06-17 11:43:46 +08:00
501f0dc74f Add user_avatar.go 2023-06-17 01:25:15 +08:00
a932b76fba Remove useless check in SetPassword() 2023-06-17 00:58:31 +08:00
0f57ac297b ci: add password complexity options to organization edit page (#1949)
* Support uploading roles and permissions via xlsx file.

* Template xlsx file for uploading users and permissions.

* reformat according to gofumpt.

* fix typo.

* add password complexity options to organization edit page.

* add password complexity options to organization edit page.

* Fixed Typos.

* Fixed Typos.

* feat:add password complexity options to organization edit page

* Auto generate i18n fields.

* Refactor code according to instructions

* Support autocheck passwd complexity in frontend when setting passwd in user edit page.

* feat:Backend Support for password validation in signup and forget page.

* feat:Frontend Support for password validation in signup and forget page.

* Add default password complex option & Update historical empty filed with default option.

* Migrator for field `password_complex_options` in org table.

* feat: support frontend password complex option check in user_edit/forget/signup page.

* frontend update for user edit page

* update i18n file

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-06-17 00:07:36 +08:00
edc6aa0d50 feat: get all role/permission of an user (#1978) 2023-06-16 22:44:21 +08:00
ebc0e0f2c9 Update i18n words 2023-06-16 22:06:54 +08:00
63dd2e781e Update backend i18n files 2023-06-16 21:55:08 +08:00
b01ba792bb Rename to accessSecret 2023-06-16 20:42:15 +08:00
98fb9f25b0 feat: fix bug that users in role don't work for permissions (#1977)
* feat: fix check login permission

* feat: fix check login permission
2023-06-16 20:14:27 +08:00
371 changed files with 37075 additions and 11436 deletions

View File

@ -110,7 +110,7 @@ jobs:
with: with:
start: yarn start start: yarn start
wait-on: 'http://localhost:7001' wait-on: 'http://localhost:7001'
wait-on-timeout: 180 wait-on-timeout: 210
working-directory: ./web working-directory: ./web
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3

View File

@ -64,7 +64,6 @@ COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf
COPY --from=BACK /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt COPY --from=BACK /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt
COPY --from=FRONT /web/build ./web/build COPY --from=FRONT /web/build ./web/build
RUN mkdir tempFiles
ENTRYPOINT ["/bin/bash"] ENTRYPOINT ["/bin/bash"]
CMD ["/docker-entrypoint.sh"] CMD ["/docker-entrypoint.sh"]

View File

@ -11,7 +11,7 @@
<img alt="GitHub Workflow Status (branch)" src="https://github.com/casdoor/casdoor/workflows/Build/badge.svg?style=flat-square"> <img alt="GitHub Workflow Status (branch)" src="https://github.com/casdoor/casdoor/workflows/Build/badge.svg?style=flat-square">
</a> </a>
<a href="https://github.com/casdoor/casdoor/releases/latest"> <a href="https://github.com/casdoor/casdoor/releases/latest">
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/casbin/casdoor.svg"> <img alt="GitHub Release" src="https://img.shields.io/github/v/release/casdoor/casdoor.svg">
</a> </a>
<a href="https://hub.docker.com/repository/docker/casbin/casdoor"> <a href="https://hub.docker.com/repository/docker/casbin/casdoor">
<img alt="Docker Image Version (latest semver)" src="https://img.shields.io/badge/Docker%20Hub-latest-brightgreen"> <img alt="Docker Image Version (latest semver)" src="https://img.shields.io/badge/Docker%20Hub-latest-brightgreen">
@ -23,22 +23,22 @@
<img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/casdoor/casdoor?style=flat-square"> <img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="https://github.com/casdoor/casdoor/blob/master/LICENSE"> <a href="https://github.com/casdoor/casdoor/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/casbin/casdoor?style=flat-square" alt="license"> <img src="https://img.shields.io/github/license/casdoor/casdoor?style=flat-square" alt="license">
</a> </a>
<a href="https://github.com/casdoor/casdoor/issues"> <a href="https://github.com/casdoor/casdoor/issues">
<img alt="GitHub issues" src="https://img.shields.io/github/issues/casbin/casdoor?style=flat-square"> <img alt="GitHub issues" src="https://img.shields.io/github/issues/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="#"> <a href="#">
<img alt="GitHub stars" src="https://img.shields.io/github/stars/casbin/casdoor?style=flat-square"> <img alt="GitHub stars" src="https://img.shields.io/github/stars/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="https://github.com/casdoor/casdoor/network"> <a href="https://github.com/casdoor/casdoor/network">
<img alt="GitHub forks" src="https://img.shields.io/github/forks/casbin/casdoor?style=flat-square"> <img alt="GitHub forks" src="https://img.shields.io/github/forks/casdoor/casdoor?style=flat-square">
</a> </a>
<a href="https://crowdin.com/project/casdoor-site"> <a href="https://crowdin.com/project/casdoor-site">
<img alt="Crowdin" src="https://badges.crowdin.net/casdoor-site/localized.svg"> <img alt="Crowdin" src="https://badges.crowdin.net/casdoor-site/localized.svg">
</a> </a>
<a href="https://gitter.im/casbin/casdoor"> <a href="https://discord.gg/5rPsrAzK7S">
<img alt="Gitter" src="https://badges.gitter.im/casbin/casdoor.svg"> <img alt="Discord" src="https://img.shields.io/discord/1022748306096537660?style=flat-square&logo=discord&label=discord&color=5865F2">
</a> </a>
</p> </p>
@ -71,7 +71,7 @@ https://casdoor.org/docs/category/integrations
## How to contact? ## How to contact?
- Gitter: https://gitter.im/casbin/casdoor - Discord: https://discord.gg/5rPsrAzK7S
- Forum: https://forum.casbin.com - Forum: https://forum.casbin.com
- Contact: https://tawk.to/chat/623352fea34c2456412b8c51/1fuc7od6e - Contact: https://tawk.to/chat/623352fea34c2456412b8c51/1fuc7od6e

141
ai/ai.go
View File

@ -1,141 +0,0 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ai
import (
"context"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/sashabaranov/go-openai"
)
func queryAnswer(authToken string, question string, timeout int) (string, error) {
// fmt.Printf("Question: %s\n", question)
client := getProxyClientFromToken(authToken)
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(2+timeout*2)*time.Second)
defer cancel()
resp, err := client.CreateChatCompletion(
ctx,
openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: question,
},
},
},
)
if err != nil {
return "", err
}
res := resp.Choices[0].Message.Content
res = strings.Trim(res, "\n")
// fmt.Printf("Answer: %s\n\n", res)
return res, nil
}
func QueryAnswerSafe(authToken string, question string) string {
var res string
var err error
for i := 0; i < 10; i++ {
res, err = queryAnswer(authToken, question, i)
if err != nil {
if i > 0 {
fmt.Printf("\tFailed (%d): %s\n", i+1, err.Error())
}
} else {
break
}
}
if err != nil {
panic(err)
}
return res
}
func QueryAnswerStream(authToken string, question string, writer io.Writer, builder *strings.Builder) error {
client := getProxyClientFromToken(authToken)
ctx := context.Background()
flusher, ok := writer.(http.Flusher)
if !ok {
return fmt.Errorf("writer does not implement http.Flusher")
}
// https://platform.openai.com/tokenizer
// https://github.com/pkoukk/tiktoken-go#available-encodings
promptTokens, err := getTokenSize(openai.GPT3TextDavinci003, question)
if err != nil {
return err
}
// https://platform.openai.com/docs/models/gpt-3-5
maxTokens := 4097 - promptTokens
respStream, err := client.CreateCompletionStream(
ctx,
openai.CompletionRequest{
Model: openai.GPT3TextDavinci003,
Prompt: question,
MaxTokens: maxTokens,
Stream: true,
},
)
if err != nil {
return err
}
defer respStream.Close()
isLeadingReturn := true
for {
completion, streamErr := respStream.Recv()
if streamErr != nil {
if streamErr == io.EOF {
break
}
return streamErr
}
data := completion.Choices[0].Text
if isLeadingReturn && len(data) != 0 {
if strings.Count(data, "\n") == len(data) {
continue
} else {
isLeadingReturn = false
}
}
fmt.Printf("%s", data)
// Write the streamed data as Server-Sent Events
if _, err = fmt.Fprintf(writer, "data: %s\n\n", data); err != nil {
return err
}
flusher.Flush()
// Append the response to the strings.Builder
builder.WriteString(data)
}
return nil
}

View File

@ -18,60 +18,21 @@ import (
"strings" "strings"
"github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
stringadapter "github.com/qiangmzsx/string-adapter/v2" stringadapter "github.com/qiangmzsx/string-adapter/v2"
) )
var Enforcer *casbin.Enforcer var Enforcer *casbin.Enforcer
func InitAuthz() { func InitApi() {
var err error e, err := object.GetInitializedEnforcer(util.GetId("built-in", "api-enforcer-built-in"))
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
driverName := conf.GetConfigString("driverName")
dataSourceName := conf.GetConfigRealDataSourceName(driverName)
a, err := xormadapter.NewAdapterWithTableName(driverName, dataSourceName, "casbin_rule", tableNamePrefix, true)
if err != nil {
panic(err)
}
modelText := `
[request_definition]
r = subOwner, subName, method, urlPath, objOwner, objName
[policy_definition]
p = subOwner, subName, method, urlPath, objOwner, objName
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = (r.subOwner == p.subOwner || p.subOwner == "*") && \
(r.subName == p.subName || p.subName == "*" || r.subName != "anonymous" && p.subName == "!anonymous") && \
(r.method == p.method || p.method == "*") && \
(r.urlPath == p.urlPath || p.urlPath == "*") && \
(r.objOwner == p.objOwner || p.objOwner == "*") && \
(r.objName == p.objName || p.objName == "*") || \
(r.subOwner == r.objOwner && r.subName == r.objName)
`
m, err := model.NewModelFromString(modelText)
if err != nil {
panic(err)
}
Enforcer, err = casbin.NewEnforcer(m, a)
if err != nil { if err != nil {
panic(err) panic(err)
} }
Enforcer = e.Enforcer
Enforcer.ClearPolicy() Enforcer.ClearPolicy()
// if len(Enforcer.GetPolicy()) == 0 { // if len(Enforcer.GetPolicy()) == 0 {
@ -85,6 +46,7 @@ p, *, *, POST, /api/login, *, *
p, *, *, GET, /api/get-app-login, *, * p, *, *, GET, /api/get-app-login, *, *
p, *, *, POST, /api/logout, *, * p, *, *, POST, /api/logout, *, *
p, *, *, GET, /api/logout, *, * p, *, *, GET, /api/logout, *, *
p, *, *, POST, /api/callback, *, *
p, *, *, GET, /api/get-account, *, * p, *, *, GET, /api/get-account, *, *
p, *, *, GET, /api/userinfo, *, * p, *, *, GET, /api/userinfo, *, *
p, *, *, GET, /api/user, *, * p, *, *, GET, /api/user, *, *
@ -126,6 +88,8 @@ p, *, *, GET, /api/get-prometheus-info, *, *
p, *, *, *, /api/metrics, *, * p, *, *, *, /api/metrics, *, *
p, *, *, GET, /api/get-pricing, *, * p, *, *, GET, /api/get-pricing, *, *
p, *, *, GET, /api/get-plan, *, * p, *, *, GET, /api/get-plan, *, *
p, *, *, GET, /api/get-subscription, *, *
p, *, *, GET, /api/get-provider, *, *
p, *, *, GET, /api/get-organization-names, *, * p, *, *, GET, /api/get-organization-names, *, *
` `
@ -158,6 +122,10 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
panic(err) panic(err)
} }
if subOwner == "app" {
return true
}
if user != nil && user.IsAdmin && (subOwner == objOwner || (objOwner == "admin")) { if user != nil && user.IsAdmin && (subOwner == objOwner || (objOwner == "admin")) {
return true return true
} }

View File

@ -15,7 +15,7 @@
package conf package conf
import ( import (
"encoding/json" "fmt"
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
@ -24,15 +24,6 @@ import (
"github.com/beego/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() { func init() {
// this array contains the beego configuration items that may be modified via env // this array contains the beego configuration items that may be modified via env
presetConfigItems := []string{"httpport", "appname"} presetConfigItems := []string{"httpport", "appname"}
@ -44,17 +35,6 @@ func init() {
} }
} }
} }
initQuota()
}
func initQuota() {
res := beego.AppConfig.String("quota")
if res != "" {
err := json.Unmarshal([]byte(res), quota)
if err != nil {
panic(err)
}
}
} }
func GetConfigString(key string) string { func GetConfigString(key string) string {
@ -67,7 +47,7 @@ func GetConfigString(key string) string {
if key == "staticBaseUrl" { if key == "staticBaseUrl" {
res = "https://cdn.casbin.org" res = "https://cdn.casbin.org"
} else if key == "logConfig" { } else if key == "logConfig" {
res = "{\"filename\": \"logs/casdoor.log\", \"maxdays\":99999, \"perm\":\"0770\"}" res = fmt.Sprintf("{\"filename\": \"logs/%s.log\", \"maxdays\":99999, \"perm\":\"0770\"}", beego.AppConfig.String("appname"))
} }
} }
@ -110,7 +90,7 @@ func GetLanguage(language string) string {
return "en" return "en"
} }
if len(language) != 2 { if len(language) != 2 || language == "nu" {
return "en" return "en"
} else { } else {
return language return language
@ -129,10 +109,6 @@ func GetConfigBatchSize() int {
return res return res
} }
func GetConfigQuota() *Quota {
return quota
}
func GetConfigRealDataSourceName(driverName string) string { func GetConfigRealDataSourceName(driverName string) string {
var dataSourceName string var dataSourceName string
if driverName != "mysql" { if driverName != "mysql" {

48
conf/conf_quota.go Normal file
View File

@ -0,0 +1,48 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package conf
import (
"encoding/json"
"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() {
initQuota()
}
func initQuota() {
res := beego.AppConfig.String("quota")
if res != "" {
err := json.Unmarshal([]byte(res), quota)
if err != nil {
panic(err)
}
}
}
func GetConfigQuota() *Quota {
return quota
}

View File

@ -146,12 +146,22 @@ func (c *ApiController) Signup() {
return return
} }
userType := "normal-user"
if authForm.Plan != "" && authForm.Pricing != "" {
err = object.CheckPricingAndPlan(authForm.Organization, authForm.Pricing, authForm.Plan)
if err != nil {
c.ResponseError(err.Error())
return
}
userType = "paid-user"
}
user := &object.User{ user := &object.User{
Owner: authForm.Organization, Owner: authForm.Organization,
Name: username, Name: username,
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
Id: id, Id: id,
Type: "normal-user", Type: userType,
Password: authForm.Password, Password: authForm.Password,
DisplayName: authForm.Name, DisplayName: authForm.Name,
Avatar: organization.DefaultAvatar, Avatar: organization.DefaultAvatar,
@ -164,7 +174,6 @@ func (c *ApiController) Signup() {
Region: authForm.Region, Region: authForm.Region,
Score: initScore, Score: initScore,
IsAdmin: false, IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false, IsForbidden: false,
IsDeleted: false, IsDeleted: false,
SignupApplication: application.Name, SignupApplication: application.Name,
@ -204,7 +213,7 @@ func (c *ApiController) Signup() {
return return
} }
if application.HasPromptPage() { if application.HasPromptPage() && user.Type == "normal-user" {
// The prompt page needs the user to be signed in // The prompt page needs the user to be signed in
c.SetSessionUsername(user.GetId()) c.SetSessionUsername(user.GetId())
} }
@ -221,15 +230,6 @@ func (c *ApiController) Signup() {
return return
} }
isSignupFromPricing := authForm.Plan != "" && authForm.Pricing != ""
if isSignupFromPricing {
_, err = object.Subscribe(organization.Name, user.Name, authForm.Plan, authForm.Pricing)
if err != nil {
c.ResponseError(err.Error())
return
}
}
record := object.NewRecord(c.Ctx) record := object.NewRecord(c.Ctx)
record.Organization = application.Organization record.Organization = application.Organization
record.User = user.Name record.User = user.Name
@ -283,10 +283,11 @@ func (c *ApiController) Logout() {
c.ResponseOk(user, application.HomepageUrl) c.ResponseOk(user, application.HomepageUrl)
return return
} else { } else {
if redirectUri == "" { // "post_logout_redirect_uri" has been made optional, see: https://github.com/casdoor/casdoor/issues/2151
c.ResponseError(c.T("general:Missing parameter") + ": post_logout_redirect_uri") // if redirectUri == "" {
return // c.ResponseError(c.T("general:Missing parameter") + ": post_logout_redirect_uri")
} // return
// }
if accessToken == "" { if accessToken == "" {
c.ResponseError(c.T("general:Missing parameter") + ": id_token_hint") c.ResponseError(c.T("general:Missing parameter") + ": id_token_hint")
return return
@ -308,27 +309,32 @@ func (c *ApiController) Logout() {
return return
} }
if application.IsRedirectUriValid(redirectUri) { if user == "" {
if user == "" { user = util.GetId(token.Organization, token.User)
user = util.GetId(token.Organization, token.User) }
}
c.ClearUserSession() c.ClearUserSession()
// TODO https://github.com/casdoor/casdoor/pull/1494#discussion_r1095675265 // TODO https://github.com/casdoor/casdoor/pull/1494#discussion_r1095675265
owner, username := util.GetOwnerAndNameFromId(user) owner, username := util.GetOwnerAndNameFromId(user)
_, err := object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID()) _, err = object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID())
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return
}
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
if redirectUri == "" {
c.ResponseOk()
return
} else {
if application.IsRedirectUriValid(redirectUri) {
c.Ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?state=%s", strings.TrimRight(redirectUri, "/"), state))
} else {
c.ResponseError(fmt.Sprintf(c.T("token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri))
return return
} }
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
c.Ctx.Redirect(http.StatusFound, fmt.Sprintf("%s?state=%s", strings.TrimRight(redirectUri, "/"), state))
} else {
c.ResponseError(fmt.Sprintf(c.T("token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri))
return
} }
} }
} }
@ -361,8 +367,11 @@ func (c *ApiController) GetAccount() {
return return
} }
user.Permissions = object.GetMaskedPermissions(user.Permissions) if user != nil {
user.Roles = object.GetMaskedRoles(user.Roles) user.Permissions = object.GetMaskedPermissions(user.Permissions)
user.Roles = object.GetMaskedRoles(user.Roles)
user.MultiFactorAuths = object.GetAllMfaProps(user, true)
}
organization, err := object.GetMaskedOrganization(object.GetOrganizationByUser(user)) organization, err := object.GetMaskedOrganization(object.GetOrganizationByUser(user))
if err != nil { if err != nil {
@ -370,7 +379,8 @@ func (c *ApiController) GetAccount() {
return return
} }
u, err := object.GetMaskedUser(user) isAdminOrSelf := c.IsAdminOrSelf(user)
u, err := object.GetMaskedUser(user, isAdminOrSelf)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

145
controllers/adapter.go Normal file
View File

@ -0,0 +1,145 @@
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// GetAdapters
// @Title GetAdapters
// @Tag Adapter API
// @Description get adapters
// @Param owner query string true "The owner of adapters"
// @Success 200 {array} object.Adapter The Response object
// @router /get-adapters [get]
func (c *ApiController) GetAdapters() {
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, err := object.GetAdapters(owner)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(adapters)
} else {
limit := util.ParseInt(limit)
count, err := object.GetAdapterCount(owner, field, value)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
adapters, err := object.GetPaginationAdapters(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(adapters, paginator.Nums())
}
}
// GetAdapter
// @Title GetAdapter
// @Tag Adapter API
// @Description get adapter
// @Param id query string true "The id ( owner/name ) of the adapter"
// @Success 200 {object} object.Adapter The Response object
// @router /get-adapter [get]
func (c *ApiController) GetAdapter() {
id := c.Input().Get("id")
adapter, err := object.GetAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(adapter)
}
// UpdateAdapter
// @Title UpdateAdapter
// @Tag Adapter API
// @Description update adapter
// @Param id query string true "The id ( owner/name ) of the adapter"
// @Param body body object.Adapter true "The details of the adapter"
// @Success 200 {object} controllers.Response The Response object
// @router /update-adapter [post]
func (c *ApiController) UpdateAdapter() {
id := c.Input().Get("id")
var adapter object.Adapter
err := json.Unmarshal(c.Ctx.Input.RequestBody, &adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateAdapter(id, &adapter))
c.ServeJSON()
}
// AddAdapter
// @Title AddAdapter
// @Tag Adapter API
// @Description add adapter
// @Param body body object.Adapter true "The details of the adapter"
// @Success 200 {object} controllers.Response The Response object
// @router /add-adapter [post]
func (c *ApiController) AddAdapter() {
var adapter object.Adapter
err := json.Unmarshal(c.Ctx.Input.RequestBody, &adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddAdapter(&adapter))
c.ServeJSON()
}
// DeleteAdapter
// @Title DeleteAdapter
// @Tag Adapter API
// @Description delete adapter
// @Param body body object.Adapter true "The details of the adapter"
// @Success 200 {object} controllers.Response The Response object
// @router /delete-adapter [post]
func (c *ApiController) DeleteAdapter() {
var adapter object.Adapter
err := json.Unmarshal(c.Ctx.Input.RequestBody, &adapter)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteAdapter(&adapter))
c.ServeJSON()
}

View File

@ -48,27 +48,27 @@ func (c *ApiController) GetApplications() {
} else { } else {
applications, err = object.GetOrganizationApplications(owner, organization) applications, err = object.GetOrganizationApplications(owner, organization)
} }
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.ResponseOk(object.GetMaskedApplications(applications, userId))
c.Data["json"] = object.GetMaskedApplications(applications, userId)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetApplicationCount(owner, field, value) count, err := object.GetApplicationCount(owner, field, value)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
paginator := pagination.SetPaginator(c.Ctx, limit, count) paginator := pagination.SetPaginator(c.Ctx, limit, count)
app, err := object.GetPaginationApplications(owner, paginator.Offset(), limit, field, value, sortField, sortOrder) application, err := object.GetPaginationApplications(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
applications := object.GetMaskedApplications(app, userId) applications := object.GetMaskedApplications(application, userId)
c.ResponseOk(applications, paginator.Nums()) c.ResponseOk(applications, paginator.Nums())
} }
} }
@ -83,13 +83,34 @@ func (c *ApiController) GetApplications() {
func (c *ApiController) GetApplication() { func (c *ApiController) GetApplication() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
id := c.Input().Get("id") id := c.Input().Get("id")
app, err := object.GetApplication(id)
application, err := object.GetApplication(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = object.GetMaskedApplication(app, userId) if c.Input().Get("withKey") != "" && application != nil && application.Cert != "" {
c.ServeJSON() cert, err := object.GetCert(util.GetId(application.Owner, application.Cert))
if err != nil {
c.ResponseError(err.Error())
return
}
if cert == nil {
cert, err = object.GetCert(util.GetId(application.Organization, application.Cert))
if err != nil {
c.ResponseError(err.Error())
return
}
}
if cert != nil {
application.CertPublicKey = cert.Certificate
}
}
c.ResponseOk(object.GetMaskedApplication(application, userId))
} }
// GetUserApplication // GetUserApplication
@ -102,23 +123,24 @@ func (c *ApiController) GetApplication() {
func (c *ApiController) GetUserApplication() { func (c *ApiController) GetUserApplication() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
id := c.Input().Get("id") id := c.Input().Get("id")
user, err := object.GetUser(id) user, err := object.GetUser(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
if user == nil { if user == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), id)) c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), id))
return return
} }
app, err := object.GetApplicationByUser(user) application, err := object.GetApplicationByUser(user)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = object.GetMaskedApplication(app, userId) c.ResponseOk(object.GetMaskedApplication(application, userId))
c.ServeJSON()
} }
// GetOrganizationApplications // GetOrganizationApplications
@ -147,11 +169,11 @@ func (c *ApiController) GetOrganizationApplications() {
if limit == "" || page == "" { if limit == "" || page == "" {
applications, err := object.GetOrganizationApplications(owner, organization) applications, err := object.GetOrganizationApplications(owner, organization)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = object.GetMaskedApplications(applications, userId) c.ResponseOk(object.GetMaskedApplications(applications, userId))
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
@ -162,13 +184,13 @@ func (c *ApiController) GetOrganizationApplications() {
} }
paginator := pagination.SetPaginator(c.Ctx, limit, count) paginator := pagination.SetPaginator(c.Ctx, limit, count)
app, err := object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder) application, err := object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
applications := object.GetMaskedApplications(app, userId) applications := object.GetMaskedApplications(application, userId)
c.ResponseOk(applications, paginator.Nums()) c.ResponseOk(applications, paginator.Nums())
} }
} }

View File

@ -20,6 +20,7 @@ import (
"encoding/xml" "encoding/xml"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http"
"net/url" "net/url"
"strconv" "strconv"
"strings" "strings"
@ -59,7 +60,7 @@ func tokenToResponse(token *object.Token) *Response {
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *form.AuthForm) (resp *Response) { func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *form.AuthForm) (resp *Response) {
userId := user.GetId() userId := user.GetId()
allowed, err := object.CheckAccessPermission(userId, application) allowed, err := object.CheckLoginPermission(userId, application)
if err != nil { if err != nil {
c.ResponseError(err.Error(), nil) c.ResponseError(err.Error(), nil)
return return
@ -69,10 +70,53 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
return return
} }
if form.Password != "" && user.IsMfaEnabled() { // check user's tag
c.setMfaSessionData(&object.MfaSessionData{UserId: userId}) if !user.IsGlobalAdmin() && !user.IsAdmin && len(application.Tags) > 0 {
resp = &Response{Status: object.NextMfa, Data: user.GetPreferMfa(true)} // only users with the tag that is listed in the application tags can login
return if !util.InSlice(application.Tags, user.Tag) {
c.ResponseError(fmt.Sprintf(c.T("auth:User's tag: %s is not listed in the application's tags"), user.Tag))
return
}
}
// check whether paid-user have active subscription
if user.Type == "paid-user" {
subscriptions, err := object.GetSubscriptionsByUser(user.Owner, user.Name)
if err != nil {
c.ResponseError(err.Error())
return
}
existActiveSubscription := false
for _, subscription := range subscriptions {
if subscription.State == object.SubStateActive {
existActiveSubscription = true
break
}
}
if !existActiveSubscription {
// check pending subscription
for _, sub := range subscriptions {
if sub.State == object.SubStatePending {
c.ResponseOk("BuyPlanResult", sub)
return
}
}
// paid-user does not have active or pending subscription, find the default pricing of application
pricing, err := object.GetApplicationDefaultPricing(application.Organization, application.Name)
if err != nil {
c.ResponseError(err.Error())
return
}
if pricing == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"), user.Name, application.Name))
return
} else {
// let the paid-user select plan
c.ResponseOk("SelectPlan", pricing)
return
}
}
} }
if form.Type == ResponseTypeLogin { if form.Type == ResponseTypeLogin {
@ -120,6 +164,11 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
return return
} }
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]string{"redirectUrl": redirectUrl, "method": method}} resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]string{"redirectUrl": redirectUrl, "method": method}}
if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in
c.SetSessionUsername(userId)
}
} else if form.Type == ResponseTypeCas { } else if form.Type == ResponseTypeCas {
// not oauth but CAS SSO protocol // not oauth but CAS SSO protocol
service := c.Input().Get("service") service := c.Input().Get("service")
@ -132,11 +181,11 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
resp.Data = st resp.Data = st
} }
} }
if application.EnableSigninSession || application.HasPromptPage() { if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in // The prompt page needs the user to be signed in
c.SetSessionUsername(userId) c.SetSessionUsername(userId)
} }
} else { } else {
resp = wrapErrorResponse(fmt.Errorf("unknown response type: %s", form.Type)) resp = wrapErrorResponse(fmt.Errorf("unknown response type: %s", form.Type))
} }
@ -179,11 +228,34 @@ func (c *ApiController) GetApplicationLogin() {
redirectUri := c.Input().Get("redirectUri") redirectUri := c.Input().Get("redirectUri")
scope := c.Input().Get("scope") scope := c.Input().Get("scope")
state := c.Input().Get("state") state := c.Input().Get("state")
id := c.Input().Get("id")
loginType := c.Input().Get("type")
msg, application, err := object.CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, c.GetAcceptLanguage()) var application *object.Application
if err != nil { var msg string
c.ResponseError(err.Error()) var err error
return if loginType == "code" {
msg, application, err = object.CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
} else if loginType == "cas" {
application, err = object.GetApplication(id)
if err != nil {
c.ResponseError(err.Error())
return
}
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), id))
return
}
err = object.CheckCasLogin(application, c.GetAcceptLanguage(), redirectUri)
if err != nil {
c.ResponseError(err.Error())
return
}
} }
application = object.GetMaskedApplication(application, "") application = object.GetMaskedApplication(application, "")
@ -238,7 +310,7 @@ func isProxyProviderType(providerType string) bool {
// @Param code_challenge_method query string false code_challenge_method // @Param code_challenge_method query string false code_challenge_method
// @Param code_challenge query string false code_challenge // @Param code_challenge query string false code_challenge
// @Param form body controllers.AuthForm true "Login information" // @Param form body controllers.AuthForm true "Login information"
// @Success 200 {object} Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /login [post] // @router /login [post]
func (c *ApiController) Login() { func (c *ApiController) Login() {
resp := &Response{} resp := &Response{}
@ -344,17 +416,26 @@ func (c *ApiController) Login() {
return return
} }
resp = c.HandleLoggedIn(application, user, &authForm)
organization, err := object.GetOrganizationByUser(user) organization, err := object.GetOrganizationByUser(user)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
} }
if user != nil && organization.HasRequiredMfa() && !user.IsMfaEnabled() { if object.IsNeedPromptMfa(organization, user) {
resp.Msg = object.RequiredMfa // The prompt page needs the user to be signed in
c.SetSessionUsername(user.GetId())
c.ResponseOk(object.RequiredMfa)
return
} }
if user.IsMfaEnabled() {
c.setMfaUserSession(user.GetId())
c.ResponseOk(object.NextMfa, user.GetPreferredMfaProps(true))
return
}
resp = c.HandleLoggedIn(application, user, &authForm)
record := object.NewRecord(c.Ctx) record := object.NewRecord(c.Ctx)
record.Organization = application.Organization record.Organization = application.Organization
record.User = user.Name record.User = user.Name
@ -405,17 +486,10 @@ func (c *ApiController) Login() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
} else if provider.Category == "OAuth" { } else if provider.Category == "OAuth" || provider.Category == "Web3" {
// OAuth // OAuth
idpInfo := object.FromProviderToIdpInfo(c.Ctx, provider)
clientId := provider.ClientId idProvider := idp.GetIdProvider(idpInfo, authForm.RedirectUri)
clientSecret := provider.ClientSecret
if provider.Type == "WeChat" && strings.Contains(c.Ctx.Request.UserAgent(), "MicroMessenger") {
clientId = provider.ClientId2
clientSecret = provider.ClientSecret2
}
idProvider := idp.GetIdProvider(provider.Type, provider.SubType, clientId, clientSecret, provider.AppId, authForm.RedirectUri, provider.Domain, provider.CustomAuthUrl, provider.CustomTokenUrl, provider.CustomUserInfoUrl)
if idProvider == nil { if idProvider == nil {
c.ResponseError(fmt.Sprintf(c.T("storage:The provider type: %s is not supported"), provider.Type)) c.ResponseError(fmt.Sprintf(c.T("storage:The provider type: %s is not supported"), provider.Type))
return return
@ -455,7 +529,7 @@ func (c *ApiController) Login() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
} else if provider.Category == "OAuth" { } else if provider.Category == "OAuth" || provider.Category == "Web3" {
user, err = object.GetUserByField(application.Organization, provider.Type, userInfo.Id) user, err = object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
@ -476,7 +550,7 @@ func (c *ApiController) Login() {
record.Organization = application.Organization record.Organization = application.Organization
record.User = user.Name record.User = user.Name
util.SafeGoroutine(func() { object.AddRecord(record) }) util.SafeGoroutine(func() { object.AddRecord(record) })
} else if provider.Category == "OAuth" { } else if provider.Category == "OAuth" || provider.Category == "Web3" {
// Sign up via OAuth // Sign up via OAuth
if application.EnableLinkWithEmail { if application.EnableLinkWithEmail {
if userInfo.Email != "" { if userInfo.Email != "" {
@ -556,7 +630,6 @@ func (c *ApiController) Login() {
Region: userInfo.CountryCode, Region: userInfo.CountryCode,
Score: initScore, Score: initScore,
IsAdmin: false, IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false, IsForbidden: false,
IsDeleted: false, IsDeleted: false,
SignupApplication: application.Name, SignupApplication: application.Name,
@ -647,28 +720,38 @@ func (c *ApiController) Login() {
resp = &Response{Status: "error", Msg: "Failed to link user account", Data: isLinked} resp = &Response{Status: "error", Msg: "Failed to link user account", Data: isLinked}
} }
} }
} else if c.getMfaSessionData() != nil { } else if c.getMfaUserSession() != "" {
mfaSession := c.getMfaSessionData() user, err := object.GetUser(c.getMfaUserSession())
user, err := object.GetUser(mfaSession.UserId)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if user == nil {
c.ResponseError("expired user session")
return
}
if authForm.Passcode != "" { if authForm.Passcode != "" {
MfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferMfa(false)) mfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferredMfaProps(false))
err = MfaUtil.Verify(authForm.Passcode) if mfaUtil == nil {
c.ResponseError("Invalid multi-factor authentication type")
return
}
err = mfaUtil.Verify(authForm.Passcode)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
} } else if authForm.RecoveryCode != "" {
if authForm.RecoveryCode != "" { err = object.MfaRecover(user, authForm.RecoveryCode)
err = object.RecoverTfs(user, authForm.RecoveryCode)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
} else {
c.ResponseError("missing passcode or recovery code")
return
} }
application, err := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application)) application, err := object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
@ -683,6 +766,7 @@ func (c *ApiController) Login() {
} }
resp = c.HandleLoggedIn(application, user, &authForm) resp = c.HandleLoggedIn(application, user, &authForm)
c.setMfaUserSession("")
record := object.NewRecord(c.Ctx) record := object.NewRecord(c.Ctx)
record.Organization = application.Organization record.Organization = application.Organization
@ -751,7 +835,8 @@ func (c *ApiController) HandleSamlLogin() {
func (c *ApiController) HandleOfficialAccountEvent() { func (c *ApiController) HandleOfficialAccountEvent() {
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body) respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
var data struct { var data struct {
@ -761,7 +846,8 @@ func (c *ApiController) HandleOfficialAccountEvent() {
} }
err = xml.Unmarshal(respBytes, &data) err = xml.Unmarshal(respBytes, &data)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
lock.Lock() lock.Lock()
@ -811,3 +897,16 @@ func (c *ApiController) GetCaptchaStatus() {
} }
c.ResponseOk(captchaEnabled) c.ResponseOk(captchaEnabled)
} }
// Callback
// @Title Callback
// @Tag Callback API
// @Description Get Login Error Counts
// @router /api/Callback [post]
func (c *ApiController) Callback() {
code := c.GetString("code")
state := c.GetString("state")
frontendCallbackUrl := fmt.Sprintf("/callback?code=%s&state=%s", code, state)
c.Ctx.Redirect(http.StatusFound, frontendCallbackUrl)
}

View File

@ -55,6 +55,22 @@ func (c *ApiController) IsAdmin() bool {
return isGlobalAdmin || user.IsAdmin return isGlobalAdmin || user.IsAdmin
} }
func (c *ApiController) IsAdminOrSelf(user2 *object.User) bool {
isGlobalAdmin, user := c.isGlobalAdmin()
if isGlobalAdmin || (user != nil && user.IsAdmin) {
return true
}
if user == nil || user2 == nil {
return false
}
if user.Owner == user2.Owner && user.Name == user2.Name {
return true
}
return false
}
func (c *ApiController) isGlobalAdmin() (bool, *object.User) { func (c *ApiController) isGlobalAdmin() (bool, *object.User) {
username := c.GetSessionUsername() username := c.GetSessionUsername()
if strings.HasPrefix(username, "app/") { if strings.HasPrefix(username, "app/") {
@ -67,7 +83,7 @@ func (c *ApiController) isGlobalAdmin() (bool, *object.User) {
return false, nil return false, nil
} }
return user.Owner == "built-in" || user.IsGlobalAdmin, user return user.IsGlobalAdmin(), user
} }
func (c *ApiController) getCurrentUser() *object.User { func (c *ApiController) getCurrentUser() *object.User {
@ -79,7 +95,8 @@ func (c *ApiController) getCurrentUser() *object.User {
} else { } else {
user, err = object.GetUser(userId) user, err = object.GetUser(userId)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return nil
} }
} }
return user return user
@ -112,7 +129,8 @@ func (c *ApiController) GetSessionApplication() *object.Application {
} }
application, err := object.GetApplicationByClientId(clientId.(string)) application, err := object.GetApplicationByClientId(clientId.(string))
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return nil
} }
return application return application
@ -176,20 +194,16 @@ func (c *ApiController) SetSessionData(s *SessionData) {
c.SetSession("SessionData", util.StructToJson(s)) c.SetSession("SessionData", util.StructToJson(s))
} }
func (c *ApiController) setMfaSessionData(data *object.MfaSessionData) { func (c *ApiController) setMfaUserSession(userId string) {
c.SetSession(object.MfaSessionUserId, data.UserId) c.SetSession(object.MfaSessionUserId, userId)
} }
func (c *ApiController) getMfaSessionData() *object.MfaSessionData { func (c *ApiController) getMfaUserSession() string {
userId := c.GetSession(object.MfaSessionUserId) userId := c.Ctx.Input.CruSession.Get(object.MfaSessionUserId)
if userId == nil { if userId == nil {
return nil return ""
} }
return userId.(string)
data := &object.MfaSessionData{
UserId: userId.(string),
}
return data
} }
func (c *ApiController) setExpireForSession() { func (c *ApiController) setExpireForSession() {

View File

@ -35,6 +35,11 @@ const (
UnauthorizedService string = "UNAUTHORIZED_SERVICE" UnauthorizedService string = "UNAUTHORIZED_SERVICE"
) )
func queryUnescape(service string) string {
s, _ := url.QueryUnescape(service)
return s
}
func (c *RootController) CasValidate() { func (c *RootController) CasValidate() {
ticket := c.Input().Get("ticket") ticket := c.Input().Get("ticket")
service := c.Input().Get("service") service := c.Input().Get("service")
@ -60,24 +65,25 @@ func (c *RootController) CasServiceValidate() {
if !strings.HasPrefix(ticket, "ST") { if !strings.HasPrefix(ticket, "ST") {
c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format) c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format)
} }
c.CasP3ServiceAndProxyValidate() c.CasP3ProxyValidate()
} }
func (c *RootController) CasProxyValidate() { func (c *RootController) CasProxyValidate() {
// https://apereo.github.io/cas/6.6.x/protocol/CAS-Protocol-Specification.html#26-proxyvalidate-cas-20
// "/proxyValidate" should accept both service tickets and proxy tickets.
c.CasP3ProxyValidate()
}
func (c *RootController) CasP3ServiceValidate() {
ticket := c.Input().Get("ticket") ticket := c.Input().Get("ticket")
format := c.Input().Get("format") format := c.Input().Get("format")
if !strings.HasPrefix(ticket, "PT") { if !strings.HasPrefix(ticket, "ST") {
c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format) c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format)
} }
c.CasP3ServiceAndProxyValidate() c.CasP3ProxyValidate()
} }
func queryUnescape(service string) string { func (c *RootController) CasP3ProxyValidate() {
s, _ := url.QueryUnescape(service)
return s
}
func (c *RootController) CasP3ServiceAndProxyValidate() {
ticket := c.Input().Get("ticket") ticket := c.Input().Get("ticket")
format := c.Input().Get("format") format := c.Input().Get("format")
service := c.Input().Get("service") service := c.Input().Get("service")
@ -115,15 +121,17 @@ func (c *RootController) CasP3ServiceAndProxyValidate() {
pgtiou := serviceResponse.Success.ProxyGrantingTicket pgtiou := serviceResponse.Success.ProxyGrantingTicket
// todo: check whether it is https // todo: check whether it is https
pgtUrlObj, err := url.Parse(pgtUrl) pgtUrlObj, err := url.Parse(pgtUrl)
if err != nil {
c.sendCasAuthenticationResponseErr(InvalidProxyCallback, err.Error(), format)
return
}
if pgtUrlObj.Scheme != "https" { if pgtUrlObj.Scheme != "https" {
c.sendCasAuthenticationResponseErr(InvalidProxyCallback, "callback is not https", format) c.sendCasAuthenticationResponseErr(InvalidProxyCallback, "callback is not https", format)
return return
} }
// make a request to pgturl passing pgt and pgtiou // make a request to pgturl passing pgt and pgtiou
if err != nil {
c.sendCasAuthenticationResponseErr(InternalError, err.Error(), format)
return
}
param := pgtUrlObj.Query() param := pgtUrlObj.Query()
param.Add("pgtId", pgt) param.Add("pgtId", pgt)
param.Add("pgtIou", pgtiou) param.Add("pgtIou", pgtiou)
@ -263,7 +271,6 @@ func (c *RootController) sendCasAuthenticationResponseErr(code, msg, format stri
Message: msg, Message: msg,
}, },
} }
if format == "json" { if format == "json" {
c.Data["json"] = serviceResponse c.Data["json"] = serviceResponse
c.ServeJSON() c.ServeJSON()

View File

@ -1,198 +0,0 @@
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
)
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")
organization := c.Input().Get("organization")
if limit == "" || page == "" {
adapters, err := object.GetCasbinAdapters(owner, organization)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(adapters)
} else {
limit := util.ParseInt(limit)
count, err := object.GetCasbinAdapterCount(owner, organization, field, value)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
adapters, err := object.GetPaginationCasbinAdapters(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(adapters, paginator.Nums())
}
}
func (c *ApiController) GetCasbinAdapter() {
id := c.Input().Get("id")
adapter, err := object.GetCasbinAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
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, err := object.GetCasbinAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
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, err := object.GetCasbinAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
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, err := object.GetCasbinAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
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, err := object.GetCasbinAdapter(id)
if err != nil {
c.ResponseError(err.Error())
return
}
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()
}

262
controllers/casbin_api.go Normal file
View File

@ -0,0 +1,262 @@
// 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/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// Enforce
// @Title Enforce
// @Tag Enforce API
// @Description Call Casbin Enforce API
// @Param body body object.CasbinRequest true "Casbin request"
// @Param permissionId query string false "permission id"
// @Param modelId query string false "model id"
// @Param resourceId query string false "resource id"
// @Success 200 {object} controllers.Response The Response object
// @router /enforce [post]
func (c *ApiController) Enforce() {
permissionId := c.Input().Get("permissionId")
modelId := c.Input().Get("modelId")
resourceId := c.Input().Get("resourceId")
enforcerId := c.Input().Get("enforcerId")
var request object.CasbinRequest
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
if err != nil {
c.ResponseError(err.Error())
return
}
if enforcerId != "" {
enforcer, err := object.GetInitializedEnforcer(enforcerId)
if err != nil {
c.ResponseError(err.Error())
return
}
res, err := enforcer.Enforce(request...)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(res)
return
}
if permissionId != "" {
permission, err := object.GetPermission(permissionId)
if err != nil {
c.ResponseError(err.Error())
return
}
res := []bool{}
if permission == nil {
res = append(res, false)
} else {
enforceResult, err := object.Enforce(permission, &request)
if err != nil {
c.ResponseError(err.Error())
return
}
res = append(res, enforceResult)
}
c.ResponseOk(res)
return
}
permissions := []*object.Permission{}
if modelId != "" {
owner, modelName := util.GetOwnerAndNameFromId(modelId)
permissions, err = object.GetPermissionsByModel(owner, modelName)
if err != nil {
c.ResponseError(err.Error())
return
}
} else if resourceId != "" {
permissions, err = object.GetPermissionsByResource(resourceId)
if err != nil {
c.ResponseError(err.Error())
return
}
} else {
c.ResponseError(c.T("general:Missing parameter"))
return
}
res := []bool{}
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
for _, permissionIds := range listPermissionIdMap {
firstPermission, err := object.GetPermission(permissionIds[0])
if err != nil {
c.ResponseError(err.Error())
return
}
enforceResult, err := object.Enforce(firstPermission, &request, permissionIds...)
if err != nil {
c.ResponseError(err.Error())
return
}
res = append(res, enforceResult)
}
c.ResponseOk(res)
}
// BatchEnforce
// @Title BatchEnforce
// @Tag Enforce API
// @Description Call Casbin BatchEnforce API
// @Param body body object.CasbinRequest true "array of casbin requests"
// @Param permissionId query string false "permission id"
// @Param modelId query string false "model id"
// @Success 200 {object} controllers.Response The Response object
// @router /batch-enforce [post]
func (c *ApiController) BatchEnforce() {
permissionId := c.Input().Get("permissionId")
modelId := c.Input().Get("modelId")
enforcerId := c.Input().Get("enforcerId")
var requests []object.CasbinRequest
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
if err != nil {
c.ResponseError(err.Error())
return
}
if enforcerId != "" {
enforcer, err := object.GetInitializedEnforcer(enforcerId)
if err != nil {
c.ResponseError(err.Error())
return
}
res, err := enforcer.BatchEnforce(requests)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(res)
return
}
if permissionId != "" {
permission, err := object.GetPermission(permissionId)
if err != nil {
c.ResponseError(err.Error())
return
}
res := [][]bool{}
if permission == nil {
l := len(requests)
resRequest := make([]bool, l)
for i := 0; i < l; i++ {
resRequest[i] = false
}
res = append(res, resRequest)
} else {
enforceResult, err := object.BatchEnforce(permission, &requests)
if err != nil {
c.ResponseError(err.Error())
return
}
res = append(res, enforceResult)
}
c.ResponseOk(res)
return
}
permissions := []*object.Permission{}
if modelId != "" {
owner, modelName := util.GetOwnerAndNameFromId(modelId)
permissions, err = object.GetPermissionsByModel(owner, modelName)
if err != nil {
c.ResponseError(err.Error())
return
}
} else {
c.ResponseError(c.T("general:Missing parameter"))
return
}
res := [][]bool{}
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
for _, permissionIds := range listPermissionIdMap {
firstPermission, err := object.GetPermission(permissionIds[0])
if err != nil {
c.ResponseError(err.Error())
return
}
enforceResult, err := object.BatchEnforce(firstPermission, &requests, permissionIds...)
if err != nil {
c.ResponseError(err.Error())
return
}
res = append(res, enforceResult)
}
c.ResponseOk(res)
}
func (c *ApiController) GetAllObjects() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("general:Please login first"))
return
}
c.ResponseOk(object.GetAllObjects(userId))
}
func (c *ApiController) GetAllActions() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("general:Please login first"))
return
}
c.ResponseOk(object.GetAllActions(userId))
}
func (c *ApiController) GetAllRoles() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("general:Please login first"))
return
}
c.ResponseOk(object.GetAllRoles(userId))
}

View File

@ -41,22 +41,24 @@ func (c *ApiController) GetCerts() {
if limit == "" || page == "" { if limit == "" || page == "" {
maskedCerts, err := object.GetMaskedCerts(object.GetCerts(owner)) maskedCerts, err := object.GetMaskedCerts(object.GetCerts(owner))
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = maskedCerts c.ResponseOk(maskedCerts)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetCertCount(owner, field, value) count, err := object.GetCertCount(owner, field, value)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
paginator := pagination.SetPaginator(c.Ctx, limit, count) paginator := pagination.SetPaginator(c.Ctx, limit, count)
certs, err := object.GetMaskedCerts(object.GetPaginationCerts(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)) certs, err := object.GetMaskedCerts(object.GetPaginationCerts(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.ResponseOk(certs, paginator.Nums()) c.ResponseOk(certs, paginator.Nums())
@ -80,22 +82,24 @@ func (c *ApiController) GetGlobleCerts() {
if limit == "" || page == "" { if limit == "" || page == "" {
maskedCerts, err := object.GetMaskedCerts(object.GetGlobleCerts()) maskedCerts, err := object.GetMaskedCerts(object.GetGlobleCerts())
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = maskedCerts c.ResponseOk(maskedCerts)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetGlobalCertsCount(field, value) count, err := object.GetGlobalCertsCount(field, value)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
paginator := pagination.SetPaginator(c.Ctx, limit, count) paginator := pagination.SetPaginator(c.Ctx, limit, count)
certs, err := object.GetMaskedCerts(object.GetPaginationGlobalCerts(paginator.Offset(), limit, field, value, sortField, sortOrder)) certs, err := object.GetMaskedCerts(object.GetPaginationGlobalCerts(paginator.Offset(), limit, field, value, sortField, sortOrder))
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.ResponseOk(certs, paginator.Nums()) c.ResponseOk(certs, paginator.Nums())
@ -113,11 +117,11 @@ func (c *ApiController) GetCert() {
id := c.Input().Get("id") id := c.Input().Get("id")
cert, err := object.GetCert(id) cert, err := object.GetCert(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = object.GetMaskedCert(cert) c.ResponseOk(object.GetMaskedCert(cert))
c.ServeJSON()
} }
// UpdateCert // UpdateCert

View File

@ -1,145 +0,0 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// GetChats
// @Title GetChats
// @Tag Chat API
// @Description get chats
// @Param owner query string true "The owner of chats"
// @Success 200 {array} object.Chat The Response object
// @router /get-chats [get]
func (c *ApiController) GetChats() {
owner := "admin"
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 == "" {
maskedChats, err := object.GetMaskedChats(object.GetChats(owner))
if err != nil {
panic(err)
}
c.Data["json"] = maskedChats
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetChatCount(owner, field, value)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
chats, err := object.GetMaskedChats(object.GetPaginationChats(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(chats, paginator.Nums())
}
}
// GetChat
// @Title GetChat
// @Tag Chat API
// @Description get chat
// @Param id query string true "The id ( owner/name ) of the chat"
// @Success 200 {object} object.Chat The Response object
// @router /get-chat [get]
func (c *ApiController) GetChat() {
id := c.Input().Get("id")
maskedChat, err := object.GetMaskedChat(object.GetChat(id))
if err != nil {
panic(err)
}
c.Data["json"] = maskedChat
c.ServeJSON()
}
// UpdateChat
// @Title UpdateChat
// @Tag Chat API
// @Description update chat
// @Param id query string true "The id ( owner/name ) of the chat"
// @Param body body object.Chat true "The details of the chat"
// @Success 200 {object} controllers.Response The Response object
// @router /update-chat [post]
func (c *ApiController) UpdateChat() {
id := c.Input().Get("id")
var chat object.Chat
err := json.Unmarshal(c.Ctx.Input.RequestBody, &chat)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateChat(id, &chat))
c.ServeJSON()
}
// AddChat
// @Title AddChat
// @Tag Chat API
// @Description add chat
// @Param body body object.Chat true "The details of the chat"
// @Success 200 {object} controllers.Response The Response object
// @router /add-chat [post]
func (c *ApiController) AddChat() {
var chat object.Chat
err := json.Unmarshal(c.Ctx.Input.RequestBody, &chat)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddChat(&chat))
c.ServeJSON()
}
// DeleteChat
// @Title DeleteChat
// @Tag Chat API
// @Description delete chat
// @Param body body object.Chat true "The details of the chat"
// @Success 200 {object} controllers.Response The Response object
// @router /delete-chat [post]
func (c *ApiController) DeleteChat() {
var chat object.Chat
err := json.Unmarshal(c.Ctx.Input.RequestBody, &chat)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteChat(&chat))
c.ServeJSON()
}

View File

@ -1,4 +1,4 @@
// Copyright 2022 The Casdoor Authors. All Rights Reserved. // Copyright 2023 The Casdoor Authors. All Rights Reserved.
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -17,162 +17,223 @@ package controllers
import ( import (
"encoding/json" "encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
xormadapter "github.com/casdoor/xorm-adapter/v3"
) )
// Enforce // GetEnforcers
// @Title Enforce // @Title GetEnforcers
// @Tag Enforce API // @Tag Enforcer API
// @Description Call Casbin Enforce API // @Description get enforcers
// @Param body body object.CasbinRequest true "Casbin request" // @Param owner query string true "The owner of enforcers"
// @Param permissionId query string false "permission id" // @Success 200 {array} object.Enforcer
// @Param modelId query string false "model id" // @router /get-enforcers [get]
// @Param resourceId query string false "resource id" func (c *ApiController) GetEnforcers() {
// @Success 200 {object} controllers.Response The Response object owner := c.Input().Get("owner")
// @router /enforce [post] limit := c.Input().Get("pageSize")
func (c *ApiController) Enforce() { page := c.Input().Get("p")
permissionId := c.Input().Get("permissionId") field := c.Input().Get("field")
modelId := c.Input().Get("modelId") value := c.Input().Get("value")
resourceId := c.Input().Get("resourceId") sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
var request object.CasbinRequest if limit == "" || page == "" {
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request) enforcers, err := object.GetEnforcers(owner)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(enforcers)
} else {
limit := util.ParseInt(limit)
count, err := object.GetEnforcerCount(owner, field, value)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
enforcers, err := object.GetPaginationEnforcers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(enforcers, paginator.Nums())
}
}
// GetEnforcer
// @Title GetEnforcer
// @Tag Enforcer API
// @Description get enforcer
// @Param id query string true "The id ( owner/name ) of enforcer"
// @Success 200 {object} object
// @router /get-enforcer [get]
func (c *ApiController) GetEnforcer() {
id := c.Input().Get("id")
loadModelCfg := c.Input().Get("loadModelCfg")
enforcer, err := object.GetEnforcer(id)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if permissionId != "" { if loadModelCfg == "true" && enforcer.Model != "" {
enforceResult, err := object.Enforce(permissionId, &request) err := enforcer.LoadModelCfg()
if err != nil { if err != nil {
c.ResponseError(err.Error())
return return
} }
res := []bool{}
res = append(res, enforceResult)
c.ResponseOk(res)
return
} }
permissions := []*object.Permission{} c.ResponseOk(enforcer)
if modelId != "" {
owner, modelName := util.GetOwnerAndNameFromId(modelId)
permissions, err = object.GetPermissionsByModel(owner, modelName)
if err != nil {
c.ResponseError(err.Error())
return
}
} else if resourceId != "" {
permissions, err = object.GetPermissionsByResource(resourceId)
if err != nil {
c.ResponseError(err.Error())
return
}
} else {
c.ResponseError(c.T("general:Missing parameter"))
return
}
res := []bool{}
for _, permission := range permissions {
enforceResult, err := object.Enforce(permission.GetId(), &request)
if err != nil {
c.ResponseError(err.Error())
return
}
res = append(res, enforceResult)
}
c.ResponseOk(res)
} }
// BatchEnforce // UpdateEnforcer
// @Title BatchEnforce // @Title UpdateEnforcer
// @Tag Enforce API // @Tag Enforcer API
// @Description Call Casbin BatchEnforce API // @Description update enforcer
// @Param body body object.CasbinRequest true "array of casbin requests" // @Param id query string true "The id ( owner/name ) of enforcer"
// @Param permissionId query string false "permission id" // @Param enforcer body object true "The enforcer object"
// @Param modelId query string false "model id" // @Success 200 {object} object
// @Success 200 {object} controllers.Response The Response object // @router /update-enforcer [post]
// @router /batch-enforce [post] func (c *ApiController) UpdateEnforcer() {
func (c *ApiController) BatchEnforce() { id := c.Input().Get("id")
permissionId := c.Input().Get("permissionId")
modelId := c.Input().Get("modelId")
var requests []object.CasbinRequest enforcer := object.Enforcer{}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests) err := json.Unmarshal(c.Ctx.Input.RequestBody, &enforcer)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if permissionId != "" { c.Data["json"] = wrapActionResponse(object.UpdateEnforcer(id, &enforcer))
enforceResult, err := object.BatchEnforce(permissionId, &requests) c.ServeJSON()
}
// AddEnforcer
// @Title AddEnforcer
// @Tag Enforcer API
// @Description add enforcer
// @Param enforcer body object true "The enforcer object"
// @Success 200 {object} object
// @router /add-enforcer [post]
func (c *ApiController) AddEnforcer() {
enforcer := object.Enforcer{}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &enforcer)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddEnforcer(&enforcer))
c.ServeJSON()
}
// DeleteEnforcer
// @Title DeleteEnforcer
// @Tag Enforcer API
// @Description delete enforcer
// @Param body body object.Enforce true "The enforcer object"
// @Success 200 {object} object
// @router /delete-enforcer [post]
func (c *ApiController) DeleteEnforcer() {
var enforcer object.Enforcer
err := json.Unmarshal(c.Ctx.Input.RequestBody, &enforcer)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteEnforcer(&enforcer))
c.ServeJSON()
}
func (c *ApiController) GetPolicies() {
id := c.Input().Get("id")
adapterId := c.Input().Get("adapterId")
if adapterId != "" {
adapter, err := object.GetAdapter(adapterId)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
err = adapter.InitAdapter()
res := [][]bool{}
res = append(res, enforceResult)
c.ResponseOk(res)
return
}
permissions := []*object.Permission{}
if modelId != "" {
owner, modelName := util.GetOwnerAndNameFromId(modelId)
permissions, err = object.GetPermissionsByModel(owner, modelName)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
} else { c.ResponseOk()
c.ResponseError(c.T("general:Missing parameter"))
return return
} }
res := [][]bool{} policies, err := object.GetPolicies(id)
for _, permission := range permissions { if err != nil {
enforceResult, err := object.BatchEnforce(permission.GetId(), &requests) c.ResponseError(err.Error())
if err != nil {
c.ResponseError(err.Error())
return
}
res = append(res, enforceResult)
}
c.ResponseOk(res)
}
func (c *ApiController) GetAllObjects() {
userId := c.GetSessionUsername()
if userId == "" {
c.ResponseError(c.T("general:Please login first"))
return return
} }
c.ResponseOk(object.GetAllObjects(userId)) c.ResponseOk(policies)
} }
func (c *ApiController) GetAllActions() { func (c *ApiController) UpdatePolicy() {
userId := c.GetSessionUsername() id := c.Input().Get("id")
if userId == "" {
c.ResponseError(c.T("general:Please login first")) var policies []xormadapter.CasbinRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policies)
if err != nil {
c.ResponseError(err.Error())
return return
} }
c.ResponseOk(object.GetAllActions(userId)) affected, err := object.UpdatePolicy(id, util.CasbinToSlice(policies[0]), util.CasbinToSlice(policies[1]))
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
} }
func (c *ApiController) GetAllRoles() { func (c *ApiController) AddPolicy() {
userId := c.GetSessionUsername() id := c.Input().Get("id")
if userId == "" {
c.ResponseError(c.T("general:Please login first")) var policy xormadapter.CasbinRule
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
if err != nil {
c.ResponseError(err.Error())
return return
} }
c.ResponseOk(object.GetAllRoles(userId)) affected, err := object.AddPolicy(id, util.CasbinToSlice(policy))
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}
func (c *ApiController) RemovePolicy() {
id := c.Input().Get("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(id, util.CasbinToSlice(policy))
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
} }

View File

@ -0,0 +1,35 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controllers
import "github.com/casdoor/casdoor/object"
// GetDashboard
// @Title GetDashboard
// @Tag GetDashboard API
// @Description get information of dashboard
// @Success 200 {object} controllers.Response The Response object
// @router /get-dashboard [get]
func (c *ApiController) GetDashboard() {
owner := c.Input().Get("owner")
data, err := object.GetDashboard(owner)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(data)
}

View File

@ -82,9 +82,9 @@ func (c *ApiController) GetGroup() {
group, err := object.GetGroup(id) group, err := object.GetGroup(id)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
} else { return
c.ResponseOk(group)
} }
c.ResponseOk(group)
} }
// UpdateGroup // UpdateGroup

View File

@ -38,8 +38,11 @@ type LdapSyncResp struct {
} }
// GetLdapUsers // GetLdapUsers
// @Tag Account API
// @Title GetLdapser // @Title GetLdapser
// @Tag Account API
// @Description get ldap users
// Param id string true "id"
// @Success 200 {object} LdapResp The Response object
// @router /get-ldap-users [get] // @router /get-ldap-users [get]
func (c *ApiController) GetLdapUsers() { func (c *ApiController) GetLdapUsers() {
id := c.Input().Get("id") id := c.Input().Get("id")
@ -94,18 +97,24 @@ func (c *ApiController) GetLdapUsers() {
} }
// GetLdaps // GetLdaps
// @Tag Account API
// @Title GetLdaps // @Title GetLdaps
// @Tag Account API
// @Description get ldaps
// @Param owner query string false "owner"
// @Success 200 {array} object.Ldap The Response object
// @router /get-ldaps [get] // @router /get-ldaps [get]
func (c *ApiController) GetLdaps() { func (c *ApiController) GetLdaps() {
owner := c.Input().Get("owner") owner := c.Input().Get("owner")
c.ResponseOk(object.GetLdaps(owner)) c.ResponseOk(object.GetMaskedLdaps(object.GetLdaps(owner)))
} }
// GetLdap // GetLdap
// @Tag Account API
// @Title GetLdap // @Title GetLdap
// @Tag Account API
// @Description get ldap
// @Param id query string true "id"
// @Success 200 {object} object.Ldap The Response object
// @router /get-ldap [get] // @router /get-ldap [get]
func (c *ApiController) GetLdap() { func (c *ApiController) GetLdap() {
id := c.Input().Get("id") id := c.Input().Get("id")
@ -116,12 +125,20 @@ func (c *ApiController) GetLdap() {
} }
_, name := util.GetOwnerAndNameFromId(id) _, name := util.GetOwnerAndNameFromId(id)
c.ResponseOk(object.GetLdap(name)) ldap, err := object.GetLdap(name)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(object.GetMaskedLdap(ldap))
} }
// AddLdap // AddLdap
// @Tag Account API
// @Title AddLdap // @Title AddLdap
// @Tag Account API
// @Description add ldap
// @Param body body object.Ldap true "The details of the ldap"
// @Success 200 {object} controllers.Response The Response object
// @router /add-ldap [post] // @router /add-ldap [post]
func (c *ApiController) AddLdap() { func (c *ApiController) AddLdap() {
var ldap object.Ldap var ldap object.Ldap
@ -160,8 +177,11 @@ func (c *ApiController) AddLdap() {
} }
// UpdateLdap // UpdateLdap
// @Tag Account API
// @Title UpdateLdap // @Title UpdateLdap
// @Tag Account API
// @Description update ldap
// @Param body body object.Ldap true "The details of the ldap"
// @Success 200 {object} controllers.Response The Response object
// @router /update-ldap [post] // @router /update-ldap [post]
func (c *ApiController) UpdateLdap() { func (c *ApiController) UpdateLdap() {
var ldap object.Ldap var ldap object.Ldap
@ -198,8 +218,11 @@ func (c *ApiController) UpdateLdap() {
} }
// DeleteLdap // DeleteLdap
// @Tag Account API
// @Title DeleteLdap // @Title DeleteLdap
// @Tag Account API
// @Description delete ldap
// @Param body body object.Ldap true "The details of the ldap"
// @Success 200 {object} controllers.Response The Response object
// @router /delete-ldap [post] // @router /delete-ldap [post]
func (c *ApiController) DeleteLdap() { func (c *ApiController) DeleteLdap() {
var ldap object.Ldap var ldap object.Ldap
@ -222,12 +245,16 @@ func (c *ApiController) DeleteLdap() {
} }
// SyncLdapUsers // SyncLdapUsers
// @Tag Account API
// @Title SyncLdapUsers // @Title SyncLdapUsers
// @Tag Account API
// @Description sync ldap users
// @Param id query string true "id"
// @Success 200 {object} LdapSyncResp The Response object
// @router /sync-ldap-users [post] // @router /sync-ldap-users [post]
func (c *ApiController) SyncLdapUsers() { func (c *ApiController) SyncLdapUsers() {
owner := c.Input().Get("owner") id := c.Input().Get("id")
ldapId := c.Input().Get("ldapId")
owner, ldapId := util.GetOwnerAndNameFromId(id)
var users []object.LdapUser var users []object.LdapUser
err := json.Unmarshal(c.Ctx.Input.RequestBody, &users) err := json.Unmarshal(c.Ctx.Input.RequestBody, &users)
if err != nil { if err != nil {

View File

@ -45,13 +45,13 @@ func (c *ApiController) Unlink() {
// the user will be unlinked from the provider // the user will be unlinked from the provider
unlinkedUser := form.User unlinkedUser := form.User
if user.Id != unlinkedUser.Id && !user.IsGlobalAdmin { 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. // if the user is not the same as the one we are unlinking, we need to make sure the user is the global admin.
c.ResponseError(c.T("link: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 return
} }
if user.Id == unlinkedUser.Id && !user.IsGlobalAdmin { if user.Id == unlinkedUser.Id && !user.IsGlobalAdmin() {
// if the user is unlinking themselves, should check the provider can be unlinked, if not, we should return an error. // if the user is unlinking themselves, should check the provider can be unlinked, if not, we should return an error.
application, err := object.GetApplicationByUser(user) application, err := object.GetApplicationByUser(user)
if err != nil { if err != nil {

View File

@ -1,310 +0,0 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controllers
import (
"encoding/json"
"fmt"
"strings"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/ai"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// GetMessages
// @Title GetMessages
// @Tag Message API
// @Description get messages
// @Param owner query string true "The owner of messages"
// @Success 200 {array} object.Message The Response object
// @router /get-messages [get]
func (c *ApiController) GetMessages() {
owner := c.Input().Get("owner")
organization := c.Input().Get("organization")
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")
chat := c.Input().Get("chat")
if limit == "" || page == "" {
var messages []*object.Message
var err error
if chat == "" {
messages, err = object.GetMessages(owner)
} else {
messages, err = object.GetChatMessages(chat)
}
if err != nil {
panic(err)
}
c.Data["json"] = object.GetMaskedMessages(messages)
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
count, err := object.GetMessageCount(owner, organization, field, value)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
paginationMessages, err := object.GetPaginationMessages(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
return
}
messages := object.GetMaskedMessages(paginationMessages)
c.ResponseOk(messages, paginator.Nums())
}
}
// GetMessage
// @Title GetMessage
// @Tag Message API
// @Description get message
// @Param id query string true "The id ( owner/name ) of the message"
// @Success 200 {object} object.Message The Response object
// @router /get-message [get]
func (c *ApiController) GetMessage() {
id := c.Input().Get("id")
message, err := object.GetMessage(id)
if err != nil {
panic(err)
}
c.Data["json"] = object.GetMaskedMessage(message)
c.ServeJSON()
}
func (c *ApiController) ResponseErrorStream(errorText string) {
event := fmt.Sprintf("event: myerror\ndata: %s\n\n", errorText)
_, err := c.Ctx.ResponseWriter.Write([]byte(event))
if err != nil {
panic(err)
}
}
// GetMessageAnswer
// @Title GetMessageAnswer
// @Tag Message API
// @Description get message answer
// @Param id query string true "The id ( owner/name ) of the message"
// @Success 200 {object} object.Message The Response object
// @router /get-message-answer [get]
func (c *ApiController) GetMessageAnswer() {
id := c.Input().Get("id")
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")
message, err := object.GetMessage(id)
if err != nil {
c.ResponseError(err.Error())
return
}
if message == nil {
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The message: %s is not found"), id))
return
}
if message.Author != "AI" || message.ReplyTo == "" || message.Text != "" {
c.ResponseErrorStream(c.T("chat:The message is invalid"))
return
}
chatId := util.GetId("admin", message.Chat)
chat, err := object.GetChat(chatId)
if err != nil {
c.ResponseError(err.Error())
return
}
if chat == nil || chat.Organization != message.Organization {
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The chat: %s is not found"), chatId))
return
}
if chat.Type != "AI" {
c.ResponseErrorStream(c.T("chat:The chat type must be \"AI\""))
return
}
questionMessage, err := object.GetMessage(message.ReplyTo)
if questionMessage == nil {
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The message: %s is not found"), id))
return
}
providerId := util.GetId(chat.Owner, chat.User2)
provider, err := object.GetProvider(providerId)
if err != nil {
c.ResponseError(err.Error())
return
}
if provider == nil {
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The provider: %s is not found"), providerId))
return
}
if provider.Category != "AI" || provider.ClientSecret == "" {
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The provider: %s is invalid"), providerId))
return
}
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")
authToken := provider.ClientSecret
question := questionMessage.Text
var stringBuilder strings.Builder
fmt.Printf("Question: [%s]\n", questionMessage.Text)
fmt.Printf("Answer: [")
err = ai.QueryAnswerStream(authToken, question, c.Ctx.ResponseWriter, &stringBuilder)
if err != nil {
c.ResponseErrorStream(err.Error())
return
}
fmt.Printf("]\n")
event := fmt.Sprintf("event: end\ndata: %s\n\n", "end")
_, err = c.Ctx.ResponseWriter.Write([]byte(event))
if err != nil {
panic(err)
}
answer := stringBuilder.String()
message.Text = answer
_, err = object.UpdateMessage(message.GetId(), message)
if err != nil {
panic(err)
}
}
// UpdateMessage
// @Title UpdateMessage
// @Tag Message API
// @Description update message
// @Param id query string true "The id ( owner/name ) of the message"
// @Param body body object.Message true "The details of the message"
// @Success 200 {object} controllers.Response The Response object
// @router /update-message [post]
func (c *ApiController) UpdateMessage() {
id := c.Input().Get("id")
var message object.Message
err := json.Unmarshal(c.Ctx.Input.RequestBody, &message)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateMessage(id, &message))
c.ServeJSON()
}
// AddMessage
// @Title AddMessage
// @Tag Message API
// @Description add message
// @Param body body object.Message true "The details of the message"
// @Success 200 {object} controllers.Response The Response object
// @router /add-message [post]
func (c *ApiController) AddMessage() {
var message object.Message
err := json.Unmarshal(c.Ctx.Input.RequestBody, &message)
if err != nil {
c.ResponseError(err.Error())
return
}
var chat *object.Chat
if message.Chat != "" {
chatId := util.GetId("admin", message.Chat)
chat, err = object.GetChat(chatId)
if err != nil {
c.ResponseError(err.Error())
return
}
if chat == nil || chat.Organization != message.Organization {
c.ResponseError(fmt.Sprintf(c.T("chat:The chat: %s is not found"), chatId))
return
}
}
affected, err := object.AddMessage(&message)
if err != nil {
c.ResponseError(err.Error())
return
}
if affected {
if chat != nil && chat.Type == "AI" {
answerMessage := &object.Message{
Owner: message.Owner,
Name: fmt.Sprintf("message_%s", util.GetRandomName()),
CreatedTime: util.GetCurrentTimeEx(message.CreatedTime),
Organization: message.Organization,
Chat: message.Chat,
ReplyTo: message.GetId(),
Author: "AI",
Text: "",
}
_, err = object.AddMessage(answerMessage)
if err != nil {
c.ResponseError(err.Error())
return
}
}
}
c.Data["json"] = wrapActionResponse(affected)
c.ServeJSON()
}
// DeleteMessage
// @Title DeleteMessage
// @Tag Message API
// @Description delete message
// @Param body body object.Message true "The details of the message"
// @Success 200 {object} controllers.Response The Response object
// @router /delete-message [post]
func (c *ApiController) DeleteMessage() {
var message object.Message
err := json.Unmarshal(c.Ctx.Input.RequestBody, &message)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteMessage(&message))
c.ServeJSON()
}

View File

@ -17,7 +17,6 @@ package controllers
import ( import (
"net/http" "net/http"
"github.com/beego/beego"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
@ -29,12 +28,12 @@ import (
// @param owner form string true "owner of user" // @param owner form string true "owner of user"
// @param name form string true "name of user" // @param name form string true "name of user"
// @param type form string true "MFA auth type" // @param type form string true "MFA auth type"
// @Success 200 {object} The Response object // @Success 200 {object} controllers.Response The Response object
// @router /mfa/setup/initiate [post] // @router /mfa/setup/initiate [post]
func (c *ApiController) MfaSetupInitiate() { func (c *ApiController) MfaSetupInitiate() {
owner := c.Ctx.Request.Form.Get("owner") owner := c.Ctx.Request.Form.Get("owner")
name := c.Ctx.Request.Form.Get("name") name := c.Ctx.Request.Form.Get("name")
authType := c.Ctx.Request.Form.Get("type") mfaType := c.Ctx.Request.Form.Get("mfaType")
userId := util.GetId(owner, name) userId := util.GetId(owner, name)
if len(userId) == 0 { if len(userId) == 0 {
@ -42,10 +41,11 @@ func (c *ApiController) MfaSetupInitiate() {
return return
} }
MfaUtil := object.GetMfaUtil(authType, nil) MfaUtil := object.GetMfaUtil(mfaType, nil)
if MfaUtil == nil { if MfaUtil == nil {
c.ResponseError("Invalid auth type") c.ResponseError("Invalid auth type")
} }
user, err := object.GetUser(userId) user, err := object.GetUser(userId)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
@ -57,10 +57,7 @@ func (c *ApiController) MfaSetupInitiate() {
return return
} }
issuer := beego.AppConfig.String("appname") mfaProps, err := MfaUtil.Initiate(c.Ctx, user.GetId())
accountName := user.GetId()
mfaProps, err := MfaUtil.Initiate(c.Ctx, issuer, accountName)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -79,16 +76,20 @@ func (c *ApiController) MfaSetupInitiate() {
// @Success 200 {object} Response object // @Success 200 {object} Response object
// @router /mfa/setup/verify [post] // @router /mfa/setup/verify [post]
func (c *ApiController) MfaSetupVerify() { func (c *ApiController) MfaSetupVerify() {
authType := c.Ctx.Request.Form.Get("type") mfaType := c.Ctx.Request.Form.Get("mfaType")
passcode := c.Ctx.Request.Form.Get("passcode") passcode := c.Ctx.Request.Form.Get("passcode")
if authType == "" || passcode == "" { if mfaType == "" || passcode == "" {
c.ResponseError("missing auth type or passcode") c.ResponseError("missing auth type or passcode")
return return
} }
MfaUtil := object.GetMfaUtil(authType, nil) mfaUtil := object.GetMfaUtil(mfaType, nil)
if mfaUtil == nil {
c.ResponseError("Invalid multi-factor authentication type")
return
}
err := MfaUtil.SetupVerify(c.Ctx, passcode) err := mfaUtil.SetupVerify(c.Ctx, passcode)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
} else { } else {
@ -108,7 +109,7 @@ func (c *ApiController) MfaSetupVerify() {
func (c *ApiController) MfaSetupEnable() { func (c *ApiController) MfaSetupEnable() {
owner := c.Ctx.Request.Form.Get("owner") owner := c.Ctx.Request.Form.Get("owner")
name := c.Ctx.Request.Form.Get("name") name := c.Ctx.Request.Form.Get("name")
authType := c.Ctx.Request.Form.Get("type") mfaType := c.Ctx.Request.Form.Get("mfaType")
user, err := object.GetUser(util.GetId(owner, name)) user, err := object.GetUser(util.GetId(owner, name))
if err != nil { if err != nil {
@ -121,8 +122,13 @@ func (c *ApiController) MfaSetupEnable() {
return return
} }
twoFactor := object.GetMfaUtil(authType, nil) mfaUtil := object.GetMfaUtil(mfaType, nil)
err = twoFactor.Enable(c.Ctx, user) if mfaUtil == nil {
c.ResponseError("Invalid multi-factor authentication type")
return
}
err = mfaUtil.Enable(c.Ctx, user)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -137,11 +143,9 @@ func (c *ApiController) MfaSetupEnable() {
// @Description: Delete MFA // @Description: Delete MFA
// @param owner form string true "owner of user" // @param owner form string true "owner of user"
// @param name form string true "name of user" // @param name form string true "name of user"
// @param id form string true "id of user's MFA props"
// @Success 200 {object} Response object // @Success 200 {object} Response object
// @router /delete-mfa/ [post] // @router /delete-mfa/ [post]
func (c *ApiController) DeleteMfa() { func (c *ApiController) DeleteMfa() {
id := c.Ctx.Request.Form.Get("id")
owner := c.Ctx.Request.Form.Get("owner") owner := c.Ctx.Request.Form.Get("owner")
name := c.Ctx.Request.Form.Get("name") name := c.Ctx.Request.Form.Get("name")
userId := util.GetId(owner, name) userId := util.GetId(owner, name)
@ -151,28 +155,18 @@ func (c *ApiController) DeleteMfa() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if user == nil { if user == nil {
c.ResponseError("User doesn't exist") c.ResponseError("User doesn't exist")
return return
} }
mfaProps := user.MultiFactorAuths[:0] err = object.DisabledMultiFactorAuth(user)
i := 0
for _, mfaProp := range mfaProps {
if mfaProp.Id != id {
mfaProps[i] = mfaProp
i++
}
}
user.MultiFactorAuths = mfaProps
_, err = object.UpdateUser(userId, user, []string{"multi_factor_auths"}, user.IsAdminUser())
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
c.ResponseOk(user.MultiFactorAuths) c.ResponseOk(object.GetAllMfaProps(user, true))
} }
// SetPreferredMfa // SetPreferredMfa
@ -185,7 +179,7 @@ func (c *ApiController) DeleteMfa() {
// @Success 200 {object} Response object // @Success 200 {object} Response object
// @router /set-preferred-mfa [post] // @router /set-preferred-mfa [post]
func (c *ApiController) SetPreferredMfa() { func (c *ApiController) SetPreferredMfa() {
id := c.Ctx.Request.Form.Get("id") mfaType := c.Ctx.Request.Form.Get("mfaType")
owner := c.Ctx.Request.Form.Get("owner") owner := c.Ctx.Request.Form.Get("owner")
name := c.Ctx.Request.Form.Get("name") name := c.Ctx.Request.Form.Get("name")
userId := util.GetId(owner, name) userId := util.GetId(owner, name)
@ -195,29 +189,15 @@ func (c *ApiController) SetPreferredMfa() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if user == nil { if user == nil {
c.ResponseError("User doesn't exist") c.ResponseError("User doesn't exist")
return return
} }
mfaProps := user.MultiFactorAuths err = object.SetPreferredMultiFactorAuth(user, mfaType)
for i, mfaProp := range user.MultiFactorAuths {
if mfaProp.Id == id {
mfaProps[i].IsPreferred = true
} else {
mfaProps[i].IsPreferred = false
}
}
_, err = object.UpdateUser(userId, user, []string{"multi_factor_auths"}, user.IsAdminUser())
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
c.ResponseOk(object.GetAllMfaProps(user, true))
for i, mfaProp := range mfaProps {
mfaProps[i] = object.GetMaskedProps(mfaProp)
}
c.ResponseOk(mfaProps)
} }

View File

@ -41,11 +41,11 @@ func (c *ApiController) GetModels() {
if limit == "" || page == "" { if limit == "" || page == "" {
models, err := object.GetModels(owner) models, err := object.GetModels(owner)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = models c.ResponseOk(models)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetModelCount(owner, field, value) count, err := object.GetModelCount(owner, field, value)
@ -77,11 +77,11 @@ func (c *ApiController) GetModel() {
model, err := object.GetModel(id) model, err := object.GetModel(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = model c.ResponseOk(model)
c.ServeJSON()
} }
// UpdateModel // UpdateModel

View File

@ -37,17 +37,26 @@ func (c *ApiController) GetOrganizations() {
value := c.Input().Get("value") value := c.Input().Get("value")
sortField := c.Input().Get("sortField") sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder") sortOrder := c.Input().Get("sortOrder")
organizationName := c.Input().Get("organizationName")
isGlobalAdmin := c.IsGlobalAdmin()
if limit == "" || page == "" { if limit == "" || page == "" {
maskedOrganizations, err := object.GetMaskedOrganizations(object.GetOrganizations(owner)) var maskedOrganizations []*object.Organization
if err != nil { var err error
panic(err)
if isGlobalAdmin {
maskedOrganizations, err = object.GetMaskedOrganizations(object.GetOrganizations(owner))
} else {
maskedOrganizations, err = object.GetMaskedOrganizations(object.GetOrganizations(owner, c.getCurrentUser().Owner))
} }
c.Data["json"] = maskedOrganizations if err != nil {
c.ServeJSON() c.ResponseError(err.Error())
return
}
c.ResponseOk(maskedOrganizations)
} else { } else {
isGlobalAdmin := c.IsGlobalAdmin()
if !isGlobalAdmin { if !isGlobalAdmin {
maskedOrganizations, err := object.GetMaskedOrganizations(object.GetOrganizations(owner, c.getCurrentUser().Owner)) maskedOrganizations, err := object.GetMaskedOrganizations(object.GetOrganizations(owner, c.getCurrentUser().Owner))
if err != nil { if err != nil {
@ -64,7 +73,7 @@ func (c *ApiController) GetOrganizations() {
} }
paginator := pagination.SetPaginator(c.Ctx, limit, count) paginator := pagination.SetPaginator(c.Ctx, limit, count)
organizations, err := object.GetMaskedOrganizations(object.GetPaginationOrganizations(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)) organizations, err := object.GetMaskedOrganizations(object.GetPaginationOrganizations(owner, organizationName, paginator.Offset(), limit, field, value, sortField, sortOrder))
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

View File

@ -31,7 +31,6 @@ import (
// @router /get-payments [get] // @router /get-payments [get]
func (c *ApiController) GetPayments() { func (c *ApiController) GetPayments() {
owner := c.Input().Get("owner") owner := c.Input().Get("owner")
organization := c.Input().Get("organization")
limit := c.Input().Get("pageSize") limit := c.Input().Get("pageSize")
page := c.Input().Get("p") page := c.Input().Get("p")
field := c.Input().Get("field") field := c.Input().Get("field")
@ -42,22 +41,24 @@ func (c *ApiController) GetPayments() {
if limit == "" || page == "" { if limit == "" || page == "" {
payments, err := object.GetPayments(owner) payments, err := object.GetPayments(owner)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = payments c.ResponseOk(payments)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetPaymentCount(owner, organization, field, value) count, err := object.GetPaymentCount(owner, field, value)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
paginator := pagination.SetPaginator(c.Ctx, limit, count) paginator := pagination.SetPaginator(c.Ctx, limit, count)
payments, err := object.GetPaginationPayments(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder) payments, err := object.GetPaginationPayments(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.ResponseOk(payments, paginator.Nums()) c.ResponseOk(payments, paginator.Nums())
@ -75,10 +76,9 @@ func (c *ApiController) GetPayments() {
// @router /get-user-payments [get] // @router /get-user-payments [get]
func (c *ApiController) GetUserPayments() { func (c *ApiController) GetUserPayments() {
owner := c.Input().Get("owner") owner := c.Input().Get("owner")
organization := c.Input().Get("organization")
user := c.Input().Get("user") user := c.Input().Get("user")
payments, err := object.GetUserPayments(owner, organization, user) payments, err := object.GetUserPayments(owner, user)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -99,11 +99,11 @@ func (c *ApiController) GetPayment() {
payment, err := object.GetPayment(id) payment, err := object.GetPayment(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = payment c.ResponseOk(payment)
c.ServeJSON()
} }
// UpdatePayment // UpdatePayment
@ -175,23 +175,17 @@ func (c *ApiController) DeletePayment() {
// @router /notify-payment [post] // @router /notify-payment [post]
func (c *ApiController) NotifyPayment() { func (c *ApiController) NotifyPayment() {
owner := c.Ctx.Input.Param(":owner") owner := c.Ctx.Input.Param(":owner")
providerName := c.Ctx.Input.Param(":provider")
productName := c.Ctx.Input.Param(":product")
paymentName := c.Ctx.Input.Param(":payment") paymentName := c.Ctx.Input.Param(":payment")
orderId := c.Ctx.Input.Param("order")
body := c.Ctx.Input.RequestBody body := c.Ctx.Input.RequestBody
err, errorResponse := object.NotifyPayment(c.Ctx.Request, body, owner, providerName, productName, paymentName, orderId) payment, err := object.NotifyPayment(body, owner, paymentName)
_, err2 := c.Ctx.ResponseWriter.Write([]byte(errorResponse))
if err2 != nil {
panic(err2)
}
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.ResponseOk(payment)
} }
// InvoicePayment // InvoicePayment

View File

@ -41,22 +41,24 @@ func (c *ApiController) GetPermissions() {
if limit == "" || page == "" { if limit == "" || page == "" {
permissions, err := object.GetPermissions(owner) permissions, err := object.GetPermissions(owner)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = permissions c.ResponseOk(permissions)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetPermissionCount(owner, field, value) count, err := object.GetPermissionCount(owner, field, value)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
paginator := pagination.SetPaginator(c.Ctx, limit, count) paginator := pagination.SetPaginator(c.Ctx, limit, count)
permissions, err := object.GetPaginationPermissions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder) permissions, err := object.GetPaginationPermissions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.ResponseOk(permissions, paginator.Nums()) c.ResponseOk(permissions, paginator.Nums())
@ -82,7 +84,6 @@ func (c *ApiController) GetPermissionsBySubmitter() {
} }
c.ResponseOk(permissions, len(permissions)) c.ResponseOk(permissions, len(permissions))
return
} }
// GetPermissionsByRole // GetPermissionsByRole
@ -101,7 +102,6 @@ func (c *ApiController) GetPermissionsByRole() {
} }
c.ResponseOk(permissions, len(permissions)) c.ResponseOk(permissions, len(permissions))
return
} }
// GetPermission // GetPermission
@ -116,11 +116,11 @@ func (c *ApiController) GetPermission() {
permission, err := object.GetPermission(id) permission, err := object.GetPermission(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = permission c.ResponseOk(permission)
c.ServeJSON()
} }
// UpdatePermission // UpdatePermission

View File

@ -16,6 +16,7 @@ package controllers
import ( import (
"fmt" "fmt"
"os"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
@ -32,16 +33,15 @@ func (c *ApiController) UploadPermissions() {
} }
fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename)) fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename))
path := util.GetUploadXlsxPath(fileId) path := util.GetUploadXlsxPath(fileId)
util.EnsureFileFolderExists(path) defer os.Remove(path)
err = saveFile(path, &file) err = saveFile(path, &file)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
affected, err := object.UploadPermissions(owner, fileId) affected, err := object.UploadPermissions(owner, path)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
} }

View File

@ -16,6 +16,7 @@ package controllers
import ( import (
"encoding/json" "encoding/json"
"fmt"
"github.com/beego/beego/utils/pagination" "github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
@ -41,11 +42,11 @@ func (c *ApiController) GetPlans() {
if limit == "" || page == "" { if limit == "" || page == "" {
plans, err := object.GetPlans(owner) plans, err := object.GetPlans(owner)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = plans c.ResponseOk(plans)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetPlanCount(owner, field, value) count, err := object.GetPlanCount(owner, field, value)
@ -79,24 +80,28 @@ func (c *ApiController) GetPlan() {
plan, err := object.GetPlan(id) plan, err := object.GetPlan(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
}
if plan == nil {
c.ResponseError(fmt.Sprintf(c.T("plan:The plan: %s does not exist"), id))
return
} }
if includeOption { if includeOption {
options, err := object.GetPermissionsByRole(plan.Role) options, err := object.GetPermissionsByRole(plan.Role)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
for _, option := range options { for _, option := range options {
plan.Options = append(plan.Options, option.DisplayName) plan.Options = append(plan.Options, option.DisplayName)
} }
c.Data["json"] = plan c.ResponseOk(plan)
} else { } else {
c.Data["json"] = plan c.ResponseOk(plan)
} }
c.ServeJSON()
} }
// UpdatePlan // UpdatePlan
@ -109,14 +114,29 @@ func (c *ApiController) GetPlan() {
// @router /update-plan [post] // @router /update-plan [post]
func (c *ApiController) UpdatePlan() { func (c *ApiController) UpdatePlan() {
id := c.Input().Get("id") id := c.Input().Get("id")
owner := util.GetOwnerFromId(id)
var plan object.Plan var plan object.Plan
err := json.Unmarshal(c.Ctx.Input.RequestBody, &plan) err := json.Unmarshal(c.Ctx.Input.RequestBody, &plan)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if plan.Product != "" {
productId := util.GetId(owner, plan.Product)
product, err := object.GetProduct(productId)
if err != nil {
c.ResponseError(err.Error())
return
}
if product != nil {
object.UpdateProductForPlan(&plan, product)
_, err = object.UpdateProduct(productId, product)
if err != nil {
c.ResponseError(err.Error())
return
}
}
}
c.Data["json"] = wrapActionResponse(object.UpdatePlan(id, &plan)) c.Data["json"] = wrapActionResponse(object.UpdatePlan(id, &plan))
c.ServeJSON() c.ServeJSON()
} }
@ -135,7 +155,14 @@ func (c *ApiController) AddPlan() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
// Create a related product for plan
product := object.CreateProductForPlan(&plan)
_, err = object.AddProduct(product)
if err != nil {
c.ResponseError(err.Error())
return
}
plan.Product = product.Name
c.Data["json"] = wrapActionResponse(object.AddPlan(&plan)) c.Data["json"] = wrapActionResponse(object.AddPlan(&plan))
c.ServeJSON() c.ServeJSON()
} }
@ -154,7 +181,13 @@ func (c *ApiController) DeletePlan() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if plan.Product != "" {
_, err = object.DeleteProduct(&object.Product{Owner: plan.Owner, Name: plan.Product})
if err != nil {
c.ResponseError(err.Error())
return
}
}
c.Data["json"] = wrapActionResponse(object.DeletePlan(&plan)) c.Data["json"] = wrapActionResponse(object.DeletePlan(&plan))
c.ServeJSON() c.ServeJSON()
} }

View File

@ -16,6 +16,7 @@ package controllers
import ( import (
"encoding/json" "encoding/json"
"fmt"
"github.com/beego/beego/utils/pagination" "github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
@ -41,11 +42,11 @@ func (c *ApiController) GetPricings() {
if limit == "" || page == "" { if limit == "" || page == "" {
pricings, err := object.GetPricings(owner) pricings, err := object.GetPricings(owner)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = pricings c.ResponseOk(pricings)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetPricingCount(owner, field, value) count, err := object.GetPricingCount(owner, field, value)
@ -70,18 +71,21 @@ func (c *ApiController) GetPricings() {
// @Tag Pricing API // @Tag Pricing API
// @Description get pricing // @Description get pricing
// @Param id query string true "The id ( owner/name ) of the pricing" // @Param id query string true "The id ( owner/name ) of the pricing"
// @Success 200 {object} object.pricing The Response object // @Success 200 {object} object.Pricing The Response object
// @router /get-pricing [get] // @router /get-pricing [get]
func (c *ApiController) GetPricing() { func (c *ApiController) GetPricing() {
id := c.Input().Get("id") id := c.Input().Get("id")
pricing, err := object.GetPricing(id) pricing, err := object.GetPricing(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
if pricing == nil {
c.Data["json"] = pricing c.ResponseError(fmt.Sprintf(c.T("pricing:The pricing: %s does not exist"), id))
c.ServeJSON() return
}
c.ResponseOk(pricing)
} }
// UpdatePricing // UpdatePricing

View File

@ -42,11 +42,11 @@ func (c *ApiController) GetProducts() {
if limit == "" || page == "" { if limit == "" || page == "" {
products, err := object.GetProducts(owner) products, err := object.GetProducts(owner)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = products c.ResponseOk(products)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetProductCount(owner, field, value) count, err := object.GetProductCount(owner, field, value)
@ -78,16 +78,17 @@ func (c *ApiController) GetProduct() {
product, err := object.GetProduct(id) product, err := object.GetProduct(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
err = object.ExtendProductWithProviders(product) err = object.ExtendProductWithProviders(product)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = product c.ResponseOk(product)
c.ServeJSON()
} }
// UpdateProduct // UpdateProduct
@ -160,10 +161,17 @@ func (c *ApiController) DeleteProduct() {
// @router /buy-product [post] // @router /buy-product [post]
func (c *ApiController) BuyProduct() { func (c *ApiController) BuyProduct() {
id := c.Input().Get("id") id := c.Input().Get("id")
providerName := c.Input().Get("providerName")
host := c.Ctx.Request.Host host := c.Ctx.Request.Host
providerName := c.Input().Get("providerName")
userId := c.GetSessionUsername() // buy `pricingName/planName` for `paidUserName`
pricingName := c.Input().Get("pricingName")
planName := c.Input().Get("planName")
paidUserName := c.Input().Get("userName")
owner, _ := util.GetOwnerAndNameFromId(id)
userId := util.GetId(owner, paidUserName)
if paidUserName == "" {
userId = c.GetSessionUsername()
}
if userId == "" { if userId == "" {
c.ResponseError(c.T("general:Please login first")) c.ResponseError(c.T("general:Please login first"))
return return
@ -174,17 +182,16 @@ func (c *ApiController) BuyProduct() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if user == nil { if user == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId)) c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
return return
} }
payUrl, orderId, err := object.BuyProduct(id, providerName, user, host) payment, err := object.BuyProduct(id, user, providerName, pricingName, planName, host)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
c.ResponseOk(payUrl, orderId) c.ResponseOk(payment)
} }

View File

@ -46,7 +46,8 @@ func (c *ApiController) GetProviders() {
if limit == "" || page == "" { if limit == "" || page == "" {
providers, err := object.GetProviders(owner) providers, err := object.GetProviders(owner)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.ResponseOk(object.GetMaskedProviders(providers, isMaskEnabled)) c.ResponseOk(object.GetMaskedProviders(providers, isMaskEnabled))
@ -92,7 +93,8 @@ func (c *ApiController) GetGlobalProviders() {
if limit == "" || page == "" { if limit == "" || page == "" {
globalProviders, err := object.GetGlobalProviders() globalProviders, err := object.GetGlobalProviders()
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.ResponseOk(object.GetMaskedProviders(globalProviders, isMaskEnabled)) c.ResponseOk(object.GetMaskedProviders(globalProviders, isMaskEnabled))

View File

@ -1,116 +0,0 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package controllers
import (
"encoding/json"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
// GetRecords
// @Title GetRecords
// @Tag Record API
// @Description get all records
// @Param pageSize query string true "The size of each page"
// @Param p query string true "The number of the page"
// @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")
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
if limit == "" || page == "" {
records, err := object.GetRecords()
if err != nil {
panic(err)
}
c.Data["json"] = records
c.ServeJSON()
} else {
limit := util.ParseInt(limit)
filterRecord := &object.Record{Organization: organization}
count, err := object.GetRecordCount(field, value, filterRecord)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
records, err := object.GetPaginationRecords(paginator.Offset(), limit, field, value, sortField, sortOrder, filterRecord)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(records, paginator.Nums())
}
}
// GetRecordsByFilter
// @Tag Record API
// @Title GetRecordsByFilter
// @Description get records by filter
// @Param filter body string true "filter Record message"
// @Success 200 {object} object.Record The Response object
// @router /get-records-filter [post]
func (c *ApiController) GetRecordsByFilter() {
body := string(c.Ctx.Input.RequestBody)
record := &object.Record{}
err := util.JsonToStruct(body, record)
if err != nil {
panic(err)
}
records, err := object.GetRecordsByField(record)
if err != nil {
panic(err)
}
c.Data["json"] = records
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

@ -29,9 +29,19 @@ import (
) )
// GetResources // GetResources
// @router /get-resources [get]
// @Tag Resource API // @Tag Resource API
// @Title GetResources // @Title GetResources
// @Description get resources
// @Param owner query string true "Owner"
// @Param user query string true "User"
// @Param pageSize query integer false "Page Size"
// @Param p query integer false "Page Number"
// @Param field query string false "Field"
// @Param value query string false "Value"
// @Param sortField query string false "Sort Field"
// @Param sortOrder query string false "Sort Order"
// @Success 200 {array} object.Resource The Response object
// @router /get-resources [get]
func (c *ApiController) GetResources() { func (c *ApiController) GetResources() {
owner := c.Input().Get("owner") owner := c.Input().Get("owner")
user := c.Input().Get("user") user := c.Input().Get("user")
@ -42,22 +52,29 @@ func (c *ApiController) GetResources() {
sortField := c.Input().Get("sortField") sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder") sortOrder := c.Input().Get("sortOrder")
userObj, ok := c.RequireSignedInUser() if sortField == "Direct" {
if !ok { provider, err := c.GetProviderFromContext("Storage")
return
}
if userObj.IsAdmin {
user = ""
}
if limit == "" || page == "" {
resources, err := object.GetResources(owner, user)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = resources prefix := sortOrder
c.ServeJSON() resources, err := object.GetDirectResources(owner, user, provider, prefix, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(resources)
} else if limit == "" || page == "" {
resources, err := object.GetResources(owner, user)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(resources)
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetResourceCount(owner, user, field, value) count, err := object.GetResourceCount(owner, user, field, value)
@ -80,22 +97,29 @@ func (c *ApiController) GetResources() {
// GetResource // GetResource
// @Tag Resource API // @Tag Resource API
// @Title GetResource // @Title GetResource
// @Description get resource
// @Param id query string true "The id ( owner/name ) of resource"
// @Success 200 {object} object.Resource The Response object
// @router /get-resource [get] // @router /get-resource [get]
func (c *ApiController) GetResource() { func (c *ApiController) GetResource() {
id := c.Input().Get("id") id := c.Input().Get("id")
resource, err := object.GetResource(id) resource, err := object.GetResource(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = resource c.ResponseOk(resource)
c.ServeJSON()
} }
// UpdateResource // UpdateResource
// @Tag Resource API // @Tag Resource API
// @Title UpdateResource // @Title UpdateResource
// @Description get resource
// @Param id query string true "The id ( owner/name ) of resource"
// @Param resource body object.Resource true "The resource object"
// @Success 200 {object} controllers.Response Success or error
// @router /update-resource [post] // @router /update-resource [post]
func (c *ApiController) UpdateResource() { func (c *ApiController) UpdateResource() {
id := c.Input().Get("id") id := c.Input().Get("id")
@ -114,6 +138,8 @@ func (c *ApiController) UpdateResource() {
// AddResource // AddResource
// @Tag Resource API // @Tag Resource API
// @Title AddResource // @Title AddResource
// @Param resource body object.Resource true "Resource object"
// @Success 200 {object} controllers.Response Success or error
// @router /add-resource [post] // @router /add-resource [post]
func (c *ApiController) AddResource() { func (c *ApiController) AddResource() {
var resource object.Resource var resource object.Resource
@ -130,6 +156,8 @@ func (c *ApiController) AddResource() {
// DeleteResource // DeleteResource
// @Tag Resource API // @Tag Resource API
// @Title DeleteResource // @Title DeleteResource
// @Param resource body object.Resource true "Resource object"
// @Success 200 {object} controllers.Response Success or error
// @router /delete-resource [post] // @router /delete-resource [post]
func (c *ApiController) DeleteResource() { func (c *ApiController) DeleteResource() {
var resource object.Resource var resource object.Resource
@ -139,11 +167,16 @@ func (c *ApiController) DeleteResource() {
return return
} }
if resource.Provider != "" {
c.Input().Set("provider", resource.Provider)
}
c.Input().Set("fullFilePath", resource.Name)
provider, err := c.GetProviderFromContext("Storage") provider, err := c.GetProviderFromContext("Storage")
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
_, resource.Name = refineFullFilePath(resource.Name)
err = object.DeleteFile(provider, resource.Name, c.GetAcceptLanguage()) err = object.DeleteFile(provider, resource.Name, c.GetAcceptLanguage())
if err != nil { if err != nil {
@ -158,6 +191,16 @@ func (c *ApiController) DeleteResource() {
// UploadResource // UploadResource
// @Tag Resource API // @Tag Resource API
// @Title UploadResource // @Title UploadResource
// @Param owner query string true "Owner"
// @Param user query string true "User"
// @Param application query string true "Application"
// @Param tag query string false "Tag"
// @Param parent query string false "Parent"
// @Param fullFilePath query string true "Full File Path"
// @Param createdTime query string false "Created Time"
// @Param description query string false "Description"
// @Param file formData file true "Resource file"
// @Success 200 {object} object.Resource FileUrl, objectKey
// @router /upload-resource [post] // @router /upload-resource [post]
func (c *ApiController) UploadResource() { func (c *ApiController) UploadResource() {
owner := c.Input().Get("owner") owner := c.Input().Get("owner")
@ -193,19 +236,20 @@ func (c *ApiController) UploadResource() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
_, fullFilePath = refineFullFilePath(fullFilePath)
fileType := "unknown" fileType := "unknown"
contentType := header.Header.Get("Content-Type") contentType := header.Header.Get("Content-Type")
fileType, _ = util.GetOwnerAndNameFromId(contentType) fileType, _ = util.GetOwnerAndNameFromIdNoCheck(contentType + "/")
if fileType != "image" && fileType != "video" { if fileType != "image" && fileType != "video" {
ext := filepath.Ext(filename) ext := filepath.Ext(filename)
mimeType := mime.TypeByExtension(ext) mimeType := mime.TypeByExtension(ext)
fileType, _ = util.GetOwnerAndNameFromId(mimeType) fileType, _ = util.GetOwnerAndNameFromIdNoCheck(mimeType + "/")
} }
fullFilePath = object.GetTruncatedPath(provider, fullFilePath, 175) fullFilePath = object.GetTruncatedPath(provider, fullFilePath, 175)
if tag != "avatar" && tag != "termsOfUse" { if tag != "avatar" && tag != "termsOfUse" && !strings.HasPrefix(tag, "idCard") {
ext := filepath.Ext(filepath.Base(fullFilePath)) ext := filepath.Ext(filepath.Base(fullFilePath))
index := len(fullFilePath) - len(ext) index := len(fullFilePath) - len(ext)
for i := 1; ; i++ { for i := 1; ; i++ {
@ -292,7 +336,7 @@ func (c *ApiController) UploadResource() {
return return
} }
_, applicationId := util.GetOwnerAndNameFromIdNoCheck(strings.TrimRight(fullFilePath, ".html")) _, applicationId := util.GetOwnerAndNameFromIdNoCheck(strings.TrimSuffix(fullFilePath, ".html"))
applicationObj, err := object.GetApplication(applicationId) applicationObj, err := object.GetApplication(applicationId)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
@ -305,6 +349,28 @@ func (c *ApiController) UploadResource() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
case "idCardFront", "idCardBack", "idCardWithPerson":
user, err := object.GetUserNoCheck(util.GetId(owner, username))
if err != nil {
c.ResponseError(err.Error())
return
}
if user == nil {
c.ResponseError(c.T("resource:User is nil for tag: avatar"))
return
}
if user.Properties == nil {
user.Properties = map[string]string{}
}
user.Properties[tag] = fileUrl
user.Properties["isIdCardVerified"] = "false"
_, err = object.UpdateUser(user.GetId(), user, []string{"properties"}, false)
if err != nil {
c.ResponseError(err.Error())
return
}
} }
c.ResponseOk(fileUrl, objectKey) c.ResponseOk(fileUrl, objectKey)

View File

@ -41,11 +41,11 @@ func (c *ApiController) GetRoles() {
if limit == "" || page == "" { if limit == "" || page == "" {
roles, err := object.GetRoles(owner) roles, err := object.GetRoles(owner)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = roles c.ResponseOk(roles)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetRoleCount(owner, field, value) count, err := object.GetRoleCount(owner, field, value)
@ -77,11 +77,11 @@ func (c *ApiController) GetRole() {
role, err := object.GetRole(id) role, err := object.GetRole(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = role c.ResponseOk(role)
c.ServeJSON()
} }
// UpdateRole // UpdateRole

View File

@ -16,6 +16,7 @@ package controllers
import ( import (
"fmt" "fmt"
"os"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
@ -32,16 +33,15 @@ func (c *ApiController) UploadRoles() {
} }
fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename)) fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename))
path := util.GetUploadXlsxPath(fileId) path := util.GetUploadXlsxPath(fileId)
util.EnsureFileFolderExists(path) defer os.Remove(path)
err = saveFile(path, &file) err = saveFile(path, &file)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
affected, err := object.UploadRoles(owner, fileId) affected, err := object.UploadRoles(owner, path)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
} }

View File

@ -40,6 +40,10 @@ type SmsForm struct {
OrgId string `json:"organizationId"` // e.g. "admin/built-in" OrgId string `json:"organizationId"` // e.g. "admin/built-in"
} }
type NotificationForm struct {
Content string `json:"content"`
}
// SendEmail // SendEmail
// @Title SendEmail // @Title SendEmail
// @Tag Service API // @Tag Service API
@ -140,10 +144,12 @@ func (c *ApiController) SendSms() {
return return
} }
invalidReceivers := getInvalidSmsReceivers(smsForm) if provider.Type != "Custom HTTP SMS" {
if len(invalidReceivers) != 0 { invalidReceivers := getInvalidSmsReceivers(smsForm)
c.ResponseError(fmt.Sprintf(c.T("service:Invalid phone receivers: %s"), strings.Join(invalidReceivers, ", "))) if len(invalidReceivers) != 0 {
return c.ResponseError(fmt.Sprintf(c.T("service:Invalid phone receivers: %s"), strings.Join(invalidReceivers, ", ")))
return
}
} }
err = object.SendSms(provider, smsForm.Content, smsForm.Receivers...) err = object.SendSms(provider, smsForm.Content, smsForm.Receivers...)
@ -154,3 +160,33 @@ func (c *ApiController) SendSms() {
c.ResponseOk() c.ResponseOk()
} }
// SendNotification
// @Title SendNotification
// @Tag Service API
// @Description This API is not for Casdoor frontend to call, it is for Casdoor SDKs.
// @Param from body controllers.NotificationForm true "Details of the notification request"
// @Success 200 {object} Response object
// @router /api/send-notification [post]
func (c *ApiController) SendNotification() {
provider, err := c.GetProviderFromContext("Notification")
if err != nil {
c.ResponseError(err.Error())
return
}
var notificationForm NotificationForm
err = json.Unmarshal(c.Ctx.Input.RequestBody, &notificationForm)
if err != nil {
c.ResponseError(err.Error())
return
}
err = object.SendNotification(provider, notificationForm.Content)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk()
}

View File

@ -41,11 +41,11 @@ func (c *ApiController) GetSessions() {
if limit == "" || page == "" { if limit == "" || page == "" {
sessions, err := object.GetSessions(owner) sessions, err := object.GetSessions(owner)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = sessions c.ResponseOk(sessions)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetSessionCount(owner, field, value) count, err := object.GetSessionCount(owner, field, value)
@ -76,11 +76,11 @@ func (c *ApiController) GetSingleSession() {
session, err := object.GetSingleSession(id) session, err := object.GetSingleSession(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = session c.ResponseOk(session)
c.ServeJSON()
} }
// UpdateSession // UpdateSession
@ -155,10 +155,9 @@ func (c *ApiController) IsSessionDuplicated() {
isUserSessionDuplicated, err := object.IsSessionDuplicated(id, sessionId) isUserSessionDuplicated, err := object.IsSessionDuplicated(id, sessionId)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = &Response{Status: "ok", Msg: "", Data: isUserSessionDuplicated} c.ResponseOk(isUserSessionDuplicated)
c.ServeJSON()
} }

View File

@ -41,11 +41,11 @@ func (c *ApiController) GetSubscriptions() {
if limit == "" || page == "" { if limit == "" || page == "" {
subscriptions, err := object.GetSubscriptions(owner) subscriptions, err := object.GetSubscriptions(owner)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = subscriptions c.ResponseOk(subscriptions)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetSubscriptionCount(owner, field, value) count, err := object.GetSubscriptionCount(owner, field, value)
@ -70,18 +70,18 @@ func (c *ApiController) GetSubscriptions() {
// @Tag Subscription API // @Tag Subscription API
// @Description get subscription // @Description get subscription
// @Param id query string true "The id ( owner/name ) of the subscription" // @Param id query string true "The id ( owner/name ) of the subscription"
// @Success 200 {object} object.subscription The Response object // @Success 200 {object} object.Subscription The Response object
// @router /get-subscription [get] // @router /get-subscription [get]
func (c *ApiController) GetSubscription() { func (c *ApiController) GetSubscription() {
id := c.Input().Get("id") id := c.Input().Get("id")
subscription, err := object.GetSubscription(id) subscription, err := object.GetSubscription(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = subscription c.ResponseOk(subscription)
c.ServeJSON()
} }
// UpdateSubscription // UpdateSubscription

View File

@ -42,11 +42,11 @@ func (c *ApiController) GetSyncers() {
if limit == "" || page == "" { if limit == "" || page == "" {
organizationSyncers, err := object.GetOrganizationSyncers(owner, organization) organizationSyncers, err := object.GetOrganizationSyncers(owner, organization)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = organizationSyncers c.ResponseOk(organizationSyncers)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetSyncerCount(owner, organization, field, value) count, err := object.GetSyncerCount(owner, organization, field, value)
@ -78,11 +78,11 @@ func (c *ApiController) GetSyncer() {
syncer, err := object.GetSyncer(id) syncer, err := object.GetSyncer(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = syncer c.ResponseOk(syncer)
c.ServeJSON()
} }
// UpdateSyncer // UpdateSyncer
@ -160,7 +160,11 @@ func (c *ApiController) RunSyncer() {
return return
} }
object.RunSyncer(syncer) err = object.RunSyncer(syncer)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk() c.ResponseOk()
} }

View File

@ -47,16 +47,16 @@ func (c *ApiController) GetSystemInfo() {
// @router /get-version-info [get] // @router /get-version-info [get]
func (c *ApiController) GetVersionInfo() { func (c *ApiController) GetVersionInfo() {
versionInfo, err := util.GetVersionInfo() versionInfo, err := util.GetVersionInfo()
if versionInfo.Version != "" {
if versionInfo.Version == "" { c.ResponseOk(versionInfo)
versionInfo, err = util.GetVersionInfoFromFile() return
if err != nil {
c.ResponseError(err.Error())
return
}
} }
versionInfo, err = util.GetVersionInfoFromFile()
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(versionInfo) c.ResponseOk(versionInfo)
} }

View File

@ -43,11 +43,11 @@ func (c *ApiController) GetTokens() {
if limit == "" || page == "" { if limit == "" || page == "" {
token, err := object.GetTokens(owner, organization) token, err := object.GetTokens(owner, organization)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = token c.ResponseOk(token)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetTokenCount(owner, organization, field, value) count, err := object.GetTokenCount(owner, organization, field, value)
@ -78,11 +78,11 @@ func (c *ApiController) GetToken() {
id := c.Input().Get("id") id := c.Input().Get("id")
token, err := object.GetToken(id) token, err := object.GetToken(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = token c.ResponseOk(token)
c.ServeJSON()
} }
// UpdateToken // UpdateToken
@ -156,7 +156,7 @@ func (c *ApiController) DeleteToken() {
// @Success 200 {object} object.TokenWrapper The Response object // @Success 200 {object} object.TokenWrapper The Response object
// @Success 400 {object} object.TokenError The Response object // @Success 400 {object} object.TokenError The Response object
// @Success 401 {object} object.TokenError The Response object // @Success 401 {object} object.TokenError The Response object
// @router /login/oauth/access_token [post] // @router api/login/oauth/access_token [post]
func (c *ApiController) GetOAuthToken() { func (c *ApiController) GetOAuthToken() {
grantType := c.Input().Get("grant_type") grantType := c.Input().Get("grant_type")
refreshToken := c.Input().Get("refresh_token") refreshToken := c.Input().Get("refresh_token")
@ -193,7 +193,8 @@ func (c *ApiController) GetOAuthToken() {
host := c.Ctx.Request.Host host := c.Ctx.Request.Host
oAuthtoken, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage()) oAuthtoken, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage())
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = oAuthtoken c.Data["json"] = oAuthtoken
@ -236,7 +237,8 @@ func (c *ApiController) RefreshToken() {
refreshToken2, err := object.RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host) refreshToken2, err := object.RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = refreshToken2 c.Data["json"] = refreshToken2
@ -276,7 +278,8 @@ func (c *ApiController) IntrospectToken() {
} }
application, err := object.GetApplicationByClientId(clientId) application, err := object.GetApplicationByClientId(clientId)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
if application == nil || application.ClientSecret != clientSecret { if application == nil || application.ClientSecret != clientSecret {
@ -289,7 +292,8 @@ func (c *ApiController) IntrospectToken() {
} }
token, err := object.GetTokenByTokenAndApplication(tokenValue, application.Name) token, err := object.GetTokenByTokenAndApplication(tokenValue, application.Name)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
if token == nil { if token == nil {
@ -319,7 +323,7 @@ func (c *ApiController) IntrospectToken() {
Sub: jwtToken.Subject, Sub: jwtToken.Subject,
Aud: jwtToken.Audience, Aud: jwtToken.Audience,
Iss: jwtToken.Issuer, Iss: jwtToken.Issuer,
Jti: jwtToken.Id, Jti: jwtToken.ID,
} }
c.ServeJSON() c.ServeJSON()
} }

View File

@ -41,11 +41,11 @@ func (c *ApiController) GetGlobalUsers() {
if limit == "" || page == "" { if limit == "" || page == "" {
maskedUsers, err := object.GetMaskedUsers(object.GetGlobalUsers()) maskedUsers, err := object.GetMaskedUsers(object.GetGlobalUsers())
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = maskedUsers c.ResponseOk(maskedUsers)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetGlobalUserCount(field, value) count, err := object.GetGlobalUserCount(field, value)
@ -80,7 +80,7 @@ func (c *ApiController) GetGlobalUsers() {
// @router /get-users [get] // @router /get-users [get]
func (c *ApiController) GetUsers() { func (c *ApiController) GetUsers() {
owner := c.Input().Get("owner") owner := c.Input().Get("owner")
groupId := c.Input().Get("groupId") groupName := c.Input().Get("groupName")
limit := c.Input().Get("pageSize") limit := c.Input().Get("pageSize")
page := c.Input().Get("p") page := c.Input().Get("p")
field := c.Input().Get("field") field := c.Input().Get("field")
@ -89,8 +89,8 @@ func (c *ApiController) GetUsers() {
sortOrder := c.Input().Get("sortOrder") sortOrder := c.Input().Get("sortOrder")
if limit == "" || page == "" { if limit == "" || page == "" {
if groupId != "" { if groupName != "" {
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupId)) maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(util.GetId(owner, groupName)))
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -101,21 +101,21 @@ func (c *ApiController) GetUsers() {
maskedUsers, err := object.GetMaskedUsers(object.GetUsers(owner)) maskedUsers, err := object.GetMaskedUsers(object.GetUsers(owner))
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = maskedUsers c.ResponseOk(maskedUsers)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetUserCount(owner, field, value, groupId) count, err := object.GetUserCount(owner, field, value, groupName)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
paginator := pagination.SetPaginator(c.Ctx, limit, count) paginator := pagination.SetPaginator(c.Ctx, limit, count)
users, err := object.GetPaginationUsers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder, groupId) users, err := object.GetPaginationUsers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder, groupName)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -153,7 +153,8 @@ func (c *ApiController) GetUser() {
if userId != "" && owner != "" { if userId != "" && owner != "" {
userFromUserId, err = object.GetUserByUserId(owner, userId) userFromUserId, err = object.GetUserByUserId(owner, userId)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
id = util.GetId(userFromUserId.Owner, userFromUserId.Name) id = util.GetId(userFromUserId.Owner, userFromUserId.Name)
@ -165,7 +166,8 @@ func (c *ApiController) GetUser() {
organization, err := object.GetOrganization(util.GetId("admin", owner)) organization, err := object.GetOrganization(util.GetId("admin", owner))
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
if !organization.IsProfilePublic { if !organization.IsProfilePublic {
@ -190,21 +192,28 @@ func (c *ApiController) GetUser() {
} }
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
}
if user != nil {
user.MultiFactorAuths = object.GetAllMfaProps(user, true)
} }
err = object.ExtendUserWithRolesAndPermissions(user) err = object.ExtendUserWithRolesAndPermissions(user)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
maskedUser, err := object.GetMaskedUser(user) isAdminOrSelf := c.IsAdminOrSelf(user)
maskedUser, err := object.GetMaskedUser(user, isAdminOrSelf)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = maskedUser c.ResponseOk(maskedUser)
c.ServeJSON()
} }
// UpdateUser // UpdateUser
@ -249,6 +258,13 @@ func (c *ApiController) UpdateUser() {
return return
} }
if c.Input().Get("allowEmpty") == "" {
if user.DisplayName == "" {
c.ResponseError(c.T("user:Display name cannot be empty"))
return
}
}
if msg := object.CheckUpdateUser(oldUser, &user, c.GetAcceptLanguage()); msg != "" { if msg := object.CheckUpdateUser(oldUser, &user, c.GetAcceptLanguage()); msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
return return
@ -410,15 +426,12 @@ func (c *ApiController) SetPassword() {
c.ResponseError(c.T("user:New password cannot contain blank space.")) c.ResponseError(c.T("user:New password cannot contain blank space."))
return return
} }
if len(newPassword) <= 5 {
c.ResponseError(c.T("user:New password must have at least 6 characters"))
return
}
userId := util.GetId(userOwner, userName) userId := util.GetId(userOwner, userName)
requestUserId := c.GetSessionUsername() requestUserId := c.GetSessionUsername()
if requestUserId == "" && code == "" { if requestUserId == "" && code == "" {
c.ResponseError(c.T("general:Please login first"), "Please login first")
return return
} else if code == "" { } else if code == "" {
hasPermission, err := object.CheckUserPermission(requestUserId, userId, true, c.GetAcceptLanguage()) hasPermission, err := object.CheckUserPermission(requestUserId, userId, true, c.GetAcceptLanguage())
@ -428,19 +441,32 @@ func (c *ApiController) SetPassword() {
} }
} else { } else {
if code != c.GetSession("verifiedCode") { if code != c.GetSession("verifiedCode") {
c.ResponseError("") c.ResponseError(c.T("general:Missing parameter"))
return return
} }
c.SetSession("verifiedCode", "") c.SetSession("verifiedCode", "")
} }
targetUser, err := object.GetUser(userId) targetUser, err := object.GetUser(userId)
if targetUser == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
return
}
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if oldPassword != "" { isAdmin := c.IsAdmin()
if isAdmin {
if oldPassword != "" {
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
if msg != "" {
c.ResponseError(msg)
return
}
}
} else {
msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage()) msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
if msg != "" { if msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
@ -448,6 +474,12 @@ func (c *ApiController) SetPassword() {
} }
} }
msg := object.CheckPasswordComplexity(targetUser, newPassword)
if msg != "" {
c.ResponseError(msg)
return
}
targetUser.Password = newPassword targetUser.Password = newPassword
_, err = object.SetUserField(targetUser, "password", targetUser.Password) _, err = object.SetUserField(targetUser, "password", targetUser.Password)
if err != nil { if err != nil {
@ -494,11 +526,11 @@ func (c *ApiController) GetSortedUsers() {
maskedUsers, err := object.GetMaskedUsers(object.GetSortedUsers(owner, sorter, limit)) maskedUsers, err := object.GetMaskedUsers(object.GetSortedUsers(owner, sorter, limit))
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = maskedUsers c.ResponseOk(maskedUsers)
c.ServeJSON()
} }
// GetUserCount // GetUserCount
@ -525,8 +557,7 @@ func (c *ApiController) GetUserCount() {
return return
} }
c.Data["json"] = count c.ResponseOk(count)
c.ServeJSON()
} }
// AddUserkeys // AddUserkeys
@ -554,8 +585,24 @@ func (c *ApiController) AddUserkeys() {
func (c *ApiController) RemoveUserFromGroup() { func (c *ApiController) RemoveUserFromGroup() {
owner := c.Ctx.Request.Form.Get("owner") owner := c.Ctx.Request.Form.Get("owner")
name := c.Ctx.Request.Form.Get("name") name := c.Ctx.Request.Form.Get("name")
groupId := c.Ctx.Request.Form.Get("groupId") groupName := c.Ctx.Request.Form.Get("groupName")
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, groupId)) organization, err := object.GetOrganization(util.GetId("admin", owner))
c.ServeJSON() if err != nil {
return
}
item := object.GetAccountItemByName("Groups", organization)
res, msg := object.CheckAccountItemModifyRule(item, c.IsAdmin(), c.GetAcceptLanguage())
if !res {
c.ResponseError(msg)
return
}
affected, err := object.DeleteGroupForUser(util.GetId(owner, name), groupName)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(affected)
} }

View File

@ -19,13 +19,14 @@ import (
"io" "io"
"mime/multipart" "mime/multipart"
"os" "os"
"path/filepath"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
func saveFile(path string, file *multipart.File) (err error) { func saveFile(path string, file *multipart.File) (err error) {
f, err := os.Create(path) f, err := os.Create(filepath.Clean(path))
if err != nil { if err != nil {
return err return err
} }
@ -47,17 +48,17 @@ func (c *ApiController) UploadUsers() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename))
fileId := fmt.Sprintf("%s_%s_%s", owner, user, util.RemoveExt(header.Filename))
path := util.GetUploadXlsxPath(fileId) path := util.GetUploadXlsxPath(fileId)
util.EnsureFileFolderExists(path) defer os.Remove(path)
err = saveFile(path, &file) err = saveFile(path, &file)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
affected, err := object.UploadUsers(owner, fileId) affected, err := object.UploadUsers(owner, path)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

View File

@ -16,6 +16,7 @@ package controllers
import ( import (
"fmt" "fmt"
"strings"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n" "github.com/casdoor/casdoor/i18n"
@ -55,6 +56,9 @@ func (c *ApiController) T(error string) string {
// GetAcceptLanguage ... // GetAcceptLanguage ...
func (c *ApiController) GetAcceptLanguage() string { func (c *ApiController) GetAcceptLanguage() string {
language := c.Ctx.Request.Header.Get("Accept-Language") language := c.Ctx.Request.Header.Get("Accept-Language")
if len(language) > 2 {
language = language[0:2]
}
return conf.GetLanguage(language) return conf.GetLanguage(language)
} }
@ -94,7 +98,8 @@ func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
user, err := object.GetUser(userId) user, err := object.GetUser(userId)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return nil, false
} }
if user == nil { if user == nil {
@ -139,8 +144,30 @@ func (c *ApiController) IsMaskedEnabled() (bool, bool) {
return true, isMaskEnabled return true, isMaskEnabled
} }
func refineFullFilePath(fullFilePath string) (string, string) {
tokens := strings.Split(fullFilePath, "/")
if len(tokens) >= 2 && tokens[0] == "Direct" && tokens[1] != "" {
providerName := tokens[1]
res := strings.Join(tokens[2:], "/")
return providerName, "/" + res
} else {
return "", fullFilePath
}
}
func (c *ApiController) GetProviderFromContext(category string) (*object.Provider, error) { func (c *ApiController) GetProviderFromContext(category string) (*object.Provider, error) {
providerName := c.Input().Get("provider") providerName := c.Input().Get("provider")
if providerName == "" {
field := c.Input().Get("field")
value := c.Input().Get("value")
if field == "provider" && value != "" {
providerName = value
} else {
fullFilePath := c.Input().Get("fullFilePath")
providerName, _ = refineFullFilePath(fullFilePath)
}
}
if providerName != "" { if providerName != "" {
provider, err := object.GetProvider(util.GetId("admin", providerName)) provider, err := object.GetProvider(util.GetId("admin", providerName))
if err != nil { if err != nil {

View File

@ -93,9 +93,9 @@ func (c *ApiController) SendVerificationCode() {
} }
} }
// mfaSessionData != nil, means method is MfaSetupVerification // mfaUserSession != "", means method is MfaAuthVerification
if mfaSessionData := c.getMfaSessionData(); mfaSessionData != nil { if mfaUserSession := c.getMfaUserSession(); mfaUserSession != "" {
user, err = object.GetUser(mfaSessionData.UserId) user, err = object.GetUser(mfaUserSession)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -129,10 +129,12 @@ func (c *ApiController) SendVerificationCode() {
} else if vform.Method == ResetVerification { } else if vform.Method == ResetVerification {
user = c.getCurrentUser() user = c.getCurrentUser()
} else if vform.Method == MfaAuthVerification { } else if vform.Method == MfaAuthVerification {
mfaProps := user.GetPreferMfa(false) mfaProps := user.GetPreferredMfaProps(false)
if user != nil && util.GetMaskedEmail(mfaProps.Secret) == vform.Dest { if user != nil && util.GetMaskedEmail(mfaProps.Secret) == vform.Dest {
vform.Dest = mfaProps.Secret vform.Dest = mfaProps.Secret
} }
} else if vform.Method == MfaSetupVerification {
c.SetSession(object.MfaDestSession, vform.Dest)
} }
provider, err := application.GetEmailProvider() provider, err := application.GetEmailProvider()
@ -157,12 +159,19 @@ func (c *ApiController) SendVerificationCode() {
} }
vform.CountryCode = user.GetCountryCode(vform.CountryCode) vform.CountryCode = user.GetCountryCode(vform.CountryCode)
} else if vform.Method == ResetVerification { } else if vform.Method == ResetVerification || vform.Method == MfaSetupVerification {
if user = c.getCurrentUser(); user != nil { if vform.CountryCode == "" {
vform.CountryCode = user.GetCountryCode(vform.CountryCode) if user = c.getCurrentUser(); user != nil {
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
}
}
if vform.Method == MfaSetupVerification {
c.SetSession(object.MfaCountryCodeSession, vform.CountryCode)
c.SetSession(object.MfaDestSession, vform.Dest)
} }
} else if vform.Method == MfaAuthVerification { } else if vform.Method == MfaAuthVerification {
mfaProps := user.GetPreferMfa(false) mfaProps := user.GetPreferredMfaProps(false)
if user != nil && util.GetMaskedPhone(mfaProps.Secret) == vform.Dest { if user != nil && util.GetMaskedPhone(mfaProps.Secret) == vform.Dest {
vform.Dest = mfaProps.Secret vform.Dest = mfaProps.Secret
} }
@ -184,11 +193,6 @@ func (c *ApiController) SendVerificationCode() {
} }
} }
if vform.Method == MfaSetupVerification {
c.SetSession(object.MfaSmsCountryCodeSession, vform.CountryCode)
c.SetSession(object.MfaSmsDestSession, vform.Dest)
}
if sendResp != nil { if sendResp != nil {
c.ResponseError(sendResp.Error()) c.ResponseError(sendResp.Error())
} else { } else {

View File

@ -66,7 +66,7 @@ func (c *ApiController) WebAuthnSignupBegin() {
// @Tag User API // @Tag User API
// @Description WebAuthn Registration Flow 2nd stage // @Description WebAuthn Registration Flow 2nd stage
// @Param body body protocol.CredentialCreationResponse true "authenticator attestation Response" // @Param body body protocol.CredentialCreationResponse true "authenticator attestation Response"
// @Success 200 {object} Response "The Response object" // @Success 200 {object} controllers.Response "The Response object"
// @router /webauthn/signup/finish [post] // @router /webauthn/signup/finish [post]
func (c *ApiController) WebAuthnSignupFinish() { func (c *ApiController) WebAuthnSignupFinish() {
webauthnObj, err := object.GetWebAuthnObject(c.Ctx.Request.Host) webauthnObj, err := object.GetWebAuthnObject(c.Ctx.Request.Host)
@ -150,7 +150,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
// @Tag Login API // @Tag Login API
// @Description WebAuthn Login Flow 2nd stage // @Description WebAuthn Login Flow 2nd stage
// @Param body body protocol.CredentialAssertionResponse true "authenticator assertion Response" // @Param body body protocol.CredentialAssertionResponse true "authenticator assertion Response"
// @Success 200 {object} Response "The Response object" // @Success 200 {object} controllers.Response "The Response object"
// @router /webauthn/signin/finish [post] // @router /webauthn/signin/finish [post]
func (c *ApiController) WebAuthnSigninFinish() { func (c *ApiController) WebAuthnSigninFinish() {
responseType := c.Input().Get("responseType") responseType := c.Input().Get("responseType")

View File

@ -26,9 +26,10 @@ import (
// @Title GetWebhooks // @Title GetWebhooks
// @Tag Webhook API // @Tag Webhook API
// @Description get webhooks // @Description get webhooks
// @Param owner query string true "The owner of webhooks" // @Param owner query string built-in/admin true "The owner of webhooks"
// @Success 200 {array} object.Webhook The Response object // @Success 200 {array} object.Webhook The Response object
// @router /get-webhooks [get] // @router /get-webhooks [get]
// @Security test_apiKey
func (c *ApiController) GetWebhooks() { func (c *ApiController) GetWebhooks() {
owner := c.Input().Get("owner") owner := c.Input().Get("owner")
limit := c.Input().Get("pageSize") limit := c.Input().Get("pageSize")
@ -42,11 +43,11 @@ func (c *ApiController) GetWebhooks() {
if limit == "" || page == "" { if limit == "" || page == "" {
webhooks, err := object.GetWebhooks(owner, organization) webhooks, err := object.GetWebhooks(owner, organization)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = webhooks c.ResponseOk(webhooks)
c.ServeJSON()
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetWebhookCount(owner, organization, field, value) count, err := object.GetWebhookCount(owner, organization, field, value)
@ -71,7 +72,7 @@ func (c *ApiController) GetWebhooks() {
// @Title GetWebhook // @Title GetWebhook
// @Tag Webhook API // @Tag Webhook API
// @Description get webhook // @Description get webhook
// @Param id query string true "The id ( owner/name ) of the webhook" // @Param id query string built-in/admin true "The id ( owner/name ) of the webhook"
// @Success 200 {object} object.Webhook The Response object // @Success 200 {object} object.Webhook The Response object
// @router /get-webhook [get] // @router /get-webhook [get]
func (c *ApiController) GetWebhook() { func (c *ApiController) GetWebhook() {
@ -79,18 +80,18 @@ func (c *ApiController) GetWebhook() {
webhook, err := object.GetWebhook(id) webhook, err := object.GetWebhook(id)
if err != nil { if err != nil {
panic(err) c.ResponseError(err.Error())
return
} }
c.Data["json"] = webhook c.ResponseOk(webhook)
c.ServeJSON()
} }
// UpdateWebhook // UpdateWebhook
// @Title UpdateWebhook // @Title UpdateWebhook
// @Tag Webhook API // @Tag Webhook API
// @Description update webhook // @Description update webhook
// @Param id query string true "The id ( owner/name ) of the webhook" // @Param id query string built-in/admin true "The id ( owner/name ) of the webhook"
// @Param body body object.Webhook true "The details of the webhook" // @Param body body object.Webhook true "The details of the webhook"
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /update-webhook [post] // @router /update-webhook [post]

View File

@ -17,6 +17,7 @@ package deployment
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"strings" "strings"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
@ -45,7 +46,7 @@ func uploadFolder(storageProvider oss.StorageInterface, folder string) {
continue continue
} }
file, err := os.Open(path + filename) file, err := os.Open(filepath.Clean(path + filename))
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -25,6 +25,12 @@ import (
) )
func TestDeployStaticFiles(t *testing.T) { func TestDeployStaticFiles(t *testing.T) {
provider := object.GetProvider(util.GetId("admin", "provider_storage_aliyun_oss")) object.InitConfig()
provider, err := object.GetProvider(util.GetId("admin", "provider_storage_aliyun_oss"))
if err != nil {
panic(err)
}
deployStaticFiles(provider) deployStaticFiles(provider)
} }

227
email/azure_acs.go Normal file
View File

@ -0,0 +1,227 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package email
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/google/uuid"
)
const (
importanceNormal = "normal"
sendEmailEndpoint = "/emails:send"
apiVersion = "2023-03-31"
)
type Email struct {
Recipients Recipients `json:"recipients"`
SenderAddress string `json:"senderAddress"`
Content Content `json:"content"`
Headers []CustomHeader `json:"headers"`
Tracking bool `json:"disableUserEngagementTracking"`
Importance string `json:"importance"`
ReplyTo []EmailAddress `json:"replyTo"`
Attachments []Attachment `json:"attachments"`
}
type Recipients struct {
To []EmailAddress `json:"to"`
CC []EmailAddress `json:"cc"`
BCC []EmailAddress `json:"bcc"`
}
type EmailAddress struct {
DisplayName string `json:"displayName"`
Address string `json:"address"`
}
type Content struct {
Subject string `json:"subject"`
HTML string `json:"html"`
PlainText string `json:"plainText"`
}
type CustomHeader struct {
Name string `json:"name"`
Value string `json:"value"`
}
type Attachment struct {
Content string `json:"contentBytesBase64"`
AttachmentType string `json:"attachmentType"`
Name string `json:"name"`
}
type ErrorResponse struct {
Error CommunicationError `json:"error"`
}
// CommunicationError contains the error code and message
type CommunicationError struct {
Code string `json:"code"`
Message string `json:"message"`
}
type AzureACSEmailProvider struct {
AccessKey string
Endpoint string
}
func NewAzureACSEmailProvider(accessKey string, endpoint string) *AzureACSEmailProvider {
return &AzureACSEmailProvider{
AccessKey: accessKey,
Endpoint: endpoint,
}
}
func newEmail(fromAddress string, toAddress string, subject string, content string) *Email {
return &Email{
Recipients: Recipients{
To: []EmailAddress{
{
DisplayName: toAddress,
Address: toAddress,
},
},
},
SenderAddress: fromAddress,
Content: Content{
Subject: subject,
HTML: content,
},
Importance: importanceNormal,
}
}
func (a *AzureACSEmailProvider) sendEmail(e *Email) error {
postBody, err := json.Marshal(e)
if err != nil {
return fmt.Errorf("email JSON marshall failed: %s", err)
}
bodyBuffer := bytes.NewBuffer(postBody)
req, err := http.NewRequest("POST", a.Endpoint+sendEmailEndpoint+"?api-version="+apiVersion, bodyBuffer)
if err != nil {
return fmt.Errorf("error creating AzureACS API request: %s", err)
}
// Sign the request using the AzureACS access key and HMAC-SHA256
err = signRequestHMAC(a.AccessKey, req)
if err != nil {
return fmt.Errorf("error signing AzureACS API request: %s", err)
}
req.Header.Set("Content-Type", "application/json")
// Some important header
req.Header.Set("repeatability-request-id", uuid.New().String())
req.Header.Set("repeatability-first-sent", time.Now().UTC().Format(http.TimeFormat))
// Send request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("error sending AzureACS API request: %s", err)
}
defer resp.Body.Close()
// Response error Handling
if resp.StatusCode == http.StatusBadRequest {
commError := ErrorResponse{}
err = json.NewDecoder(resp.Body).Decode(&commError)
if err != nil {
return err
}
return fmt.Errorf("error sending email: %s", commError.Error.Message)
}
if resp.StatusCode != http.StatusAccepted {
return fmt.Errorf("error sending email: status: %d", resp.StatusCode)
}
return nil
}
func signRequestHMAC(secret string, req *http.Request) error {
method := req.Method
host := req.URL.Host
pathAndQuery := req.URL.Path
if req.URL.RawQuery != "" {
pathAndQuery = pathAndQuery + "?" + req.URL.RawQuery
}
var content []byte
var err error
if req.Body != nil {
content, err = io.ReadAll(req.Body)
if err != nil {
// return err
content = []byte{}
}
}
req.Body = io.NopCloser(bytes.NewBuffer(content))
key, err := base64.StdEncoding.DecodeString(secret)
if err != nil {
return fmt.Errorf("error decoding secret: %s", err)
}
timestamp := time.Now().UTC().Format(http.TimeFormat)
contentHash := GetContentHashBase64(content)
stringToSign := fmt.Sprintf("%s\n%s\n%s;%s;%s", strings.ToUpper(method), pathAndQuery, timestamp, host, contentHash)
signature := GetHmac(stringToSign, key)
req.Header.Set("x-ms-content-sha256", contentHash)
req.Header.Set("x-ms-date", timestamp)
req.Header.Set("Authorization", "HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature="+signature)
return nil
}
func GetContentHashBase64(content []byte) string {
hasher := sha256.New()
hasher.Write(content)
return base64.StdEncoding.EncodeToString(hasher.Sum(nil))
}
func GetHmac(content string, key []byte) string {
hmac := hmac.New(sha256.New, key)
hmac.Write([]byte(content))
return base64.StdEncoding.EncodeToString(hmac.Sum(nil))
}
func (a *AzureACSEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
e := newEmail(fromAddress, toAddress, subject, content)
return a.sendEmail(e)
}

27
email/provider.go Normal file
View File

@ -0,0 +1,27 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package email
type EmailProvider interface {
Send(fromAddress string, fromName, toAddress string, subject string, content string) error
}
func GetEmailProvider(typ string, clientId string, clientSecret string, appId string, host string, port int, disableSsl bool) EmailProvider {
if typ == "Azure ACS" {
return NewAzureACSEmailProvider(appId, host)
} else {
return NewSmtpEmailProvider(clientId, clientSecret, host, port, typ, disableSsl)
}
}

49
email/smtp.go Normal file
View File

@ -0,0 +1,49 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package email
import (
"crypto/tls"
"github.com/casdoor/gomail/v2"
)
type SmtpEmailProvider struct {
Dialer *gomail.Dialer
}
func NewSmtpEmailProvider(userName string, password string, host string, port int, typ string, disableSsl bool) *SmtpEmailProvider {
dialer := &gomail.Dialer{}
dialer = gomail.NewDialer(host, port, userName, password)
if typ == "SUBMAIL" {
dialer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
}
dialer.SSL = !disableSsl
return &SmtpEmailProvider{Dialer: dialer}
}
func (s *SmtpEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
message := gomail.NewMessage()
message.SetAddressHeader("From", fromAddress, fromName)
message.SetHeader("To", toAddress)
message.SetHeader("Subject", subject)
message.SetBody("text/html", content)
message.SkipUsernameCheck = true
return s.Dialer.DialAndSend(message)
}

View File

@ -17,17 +17,18 @@ package form
type AuthForm struct { type AuthForm struct {
Type string `json:"type"` Type string `json:"type"`
Organization string `json:"organization"` Organization string `json:"organization"`
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
Name string `json:"name"` Name string `json:"name"`
FirstName string `json:"firstName"` FirstName string `json:"firstName"`
LastName string `json:"lastName"` LastName string `json:"lastName"`
Email string `json:"email"` Email string `json:"email"`
Phone string `json:"phone"` Phone string `json:"phone"`
Affiliation string `json:"affiliation"` Affiliation string `json:"affiliation"`
IdCard string `json:"idCard"` IdCard string `json:"idCard"`
Region string `json:"region"` Region string `json:"region"`
InvitationCode string `json:"invitationCode"`
Application string `json:"application"` Application string `json:"application"`
ClientId string `json:"clientId"` ClientId string `json:"clientId"`

46
go.mod
View File

@ -6,19 +6,19 @@ require (
github.com/Masterminds/squirrel v1.5.3 github.com/Masterminds/squirrel v1.5.3
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387 github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
github.com/aliyun/alibaba-cloud-sdk-go v1.62.188 // indirect github.com/aws/aws-sdk-go v1.45.5
github.com/aws/aws-sdk-go v1.44.4
github.com/beego/beego v1.12.12 github.com/beego/beego v1.12.12
github.com/beevik/etree v1.1.0 github.com/beevik/etree v1.1.0
github.com/casbin/casbin v1.9.1 // indirect github.com/casbin/casbin v1.9.1 // indirect
github.com/casbin/casbin/v2 v2.30.1 github.com/casbin/casbin/v2 v2.37.0
github.com/casdoor/go-sms-sender v0.6.1 github.com/casdoor/go-sms-sender v0.14.0
github.com/casdoor/gomail/v2 v2.0.1 github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/oss v1.2.0 github.com/casdoor/notify v0.43.0
github.com/casdoor/oss v1.3.0
github.com/casdoor/xorm-adapter/v3 v3.0.4 github.com/casdoor/xorm-adapter/v3 v3.0.4
github.com/casvisor/casvisor-go-sdk v1.0.3
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
github.com/denisenkom/go-mssqldb v0.9.0 github.com/denisenkom/go-mssqldb v0.9.0
github.com/dlclark/regexp2 v1.9.0 // indirect
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
github.com/fogleman/gg v1.3.0 github.com/fogleman/gg v1.3.0
github.com/forestmgy/ldapserver v1.1.0 github.com/forestmgy/ldapserver v1.1.0
@ -27,48 +27,44 @@ require (
github.com/go-mysql-org/go-mysql v1.7.0 github.com/go-mysql-org/go-mysql v1.7.0
github.com/go-pay/gopay v1.5.72 github.com/go-pay/gopay v1.5.72
github.com/go-sql-driver/mysql v1.6.0 github.com/go-sql-driver/mysql v1.6.0
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/go-webauthn/webauthn v0.6.0 github.com/go-webauthn/webauthn v0.6.0
github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/google/uuid v1.3.1
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/uuid v1.3.0
github.com/gorilla/mux v1.7.3 // indirect
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
github.com/lestrrat-go/jwx v1.2.21 github.com/lestrrat-go/jwx v1.2.21
github.com/lib/pq v1.10.2 github.com/lib/pq v1.10.9
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
github.com/markbates/goth v1.75.2 github.com/markbates/goth v1.75.2
github.com/mitchellh/mapstructure v1.5.0
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/nyaruka/phonenumbers v1.1.5 github.com/nyaruka/phonenumbers v1.1.5
github.com/pkoukk/tiktoken-go v0.1.1 github.com/pquerna/otp v1.4.0
github.com/plutov/paypal/v4 v4.7.0
github.com/prometheus/client_golang v1.11.1 github.com/prometheus/client_golang v1.11.1
github.com/prometheus/client_model v0.2.0 github.com/prometheus/client_model v0.3.0
github.com/qiangmzsx/string-adapter/v2 v2.1.0 github.com/qiangmzsx/string-adapter/v2 v2.1.0
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/russellhaering/gosaml2 v0.9.0 github.com/russellhaering/gosaml2 v0.9.0
github.com/russellhaering/goxmldsig v1.2.0 github.com/russellhaering/goxmldsig v1.2.0
github.com/sashabaranov/go-openai v1.9.1
github.com/satori/go.uuid v1.2.0
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible github.com/shirou/gopsutil v3.21.11+incompatible
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.4
github.com/stripe/stripe-go/v74 v74.29.0
github.com/tealeg/xlsx v1.0.5 github.com/tealeg/xlsx v1.0.5
github.com/thanhpk/randstr v1.0.4 github.com/thanhpk/randstr v1.0.4
github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/go-sysconf v0.3.10 // indirect
github.com/xorm-io/builder v0.3.13 // indirect github.com/xorm-io/builder v0.3.13
github.com/xorm-io/core v0.7.4 github.com/xorm-io/core v0.7.4
github.com/xorm-io/xorm v1.1.6 github.com/xorm-io/xorm v1.1.6
github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.6.0 golang.org/x/crypto v0.12.0
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/net v0.14.0
golang.org/x/net v0.7.0 golang.org/x/oauth2 v0.11.0
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 google.golang.org/api v0.138.0
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v2 v2.4.0 // indirect maunium.net/go/mautrix v0.16.0
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84 modernc.org/sqlite v1.18.2
) )

1896
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,18 @@ func TestGenerateI18nFrontend(t *testing.T) {
applyToOtherLanguage("frontend", "ru", data) applyToOtherLanguage("frontend", "ru", data)
applyToOtherLanguage("frontend", "vi", data) applyToOtherLanguage("frontend", "vi", data)
applyToOtherLanguage("frontend", "pt", data) applyToOtherLanguage("frontend", "pt", data)
applyToOtherLanguage("frontend", "it", data)
applyToOtherLanguage("frontend", "ms", data)
applyToOtherLanguage("frontend", "tr", data)
applyToOtherLanguage("frontend", "ar", data)
applyToOtherLanguage("frontend", "he", data)
applyToOtherLanguage("frontend", "nl", data)
applyToOtherLanguage("frontend", "pl", data)
applyToOtherLanguage("frontend", "fi", data)
applyToOtherLanguage("frontend", "sv", data)
applyToOtherLanguage("frontend", "uk", data)
applyToOtherLanguage("frontend", "kk", data)
applyToOtherLanguage("frontend", "fa", data)
} }
func TestGenerateI18nBackend(t *testing.T) { func TestGenerateI18nBackend(t *testing.T) {
@ -49,4 +61,16 @@ func TestGenerateI18nBackend(t *testing.T) {
applyToOtherLanguage("backend", "ru", data) applyToOtherLanguage("backend", "ru", data)
applyToOtherLanguage("backend", "vi", data) applyToOtherLanguage("backend", "vi", data)
applyToOtherLanguage("backend", "pt", data) applyToOtherLanguage("backend", "pt", data)
applyToOtherLanguage("backend", "it", data)
applyToOtherLanguage("backend", "ms", data)
applyToOtherLanguage("backend", "tr", data)
applyToOtherLanguage("backend", "ar", data)
applyToOtherLanguage("backend", "he", data)
applyToOtherLanguage("backend", "nl", data)
applyToOtherLanguage("backend", "pl", data)
applyToOtherLanguage("backend", "fi", data)
applyToOtherLanguage("backend", "sv", data)
applyToOtherLanguage("backend", "uk", data)
applyToOtherLanguage("backend", "kk", data)
applyToOtherLanguage("backend", "fa", data)
} }

142
i18n/locales/ar/data.json Normal file
View File

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

View File

@ -18,19 +18,12 @@
"The login method: login with password is not enabled for the application": "Die Anmeldeart \"Anmeldung mit Passwort\" ist für die Anwendung nicht aktiviert", "The login method: login with password is not enabled for the application": "Die Anmeldeart \"Anmeldung mit Passwort\" ist für die Anwendung nicht aktiviert",
"The provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert", "The provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert",
"Unauthorized operation": "Nicht autorisierte Operation", "Unauthorized operation": "Nicht autorisierte Operation",
"Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s" "Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "Service %s und %s stimmen nicht überein" "Service %s and %s do not match": "Service %s und %s stimmen nicht überein"
}, },
"chat": {
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
"The chat: %s is not found": "The chat: %s is not found",
"The message is invalid": "The message is invalid",
"The message: %s is not found": "The message: %s is not found",
"The provider: %s is invalid": "The provider: %s is invalid",
"The provider: %s is not found": "The provider: %s is not found"
},
"check": { "check": {
"Affiliation cannot be blank": "Zugehörigkeit darf nicht leer sein", "Affiliation cannot be blank": "Zugehörigkeit darf nicht leer sein",
"DisplayName cannot be blank": "Anzeigename kann nicht leer sein", "DisplayName cannot be blank": "Anzeigename kann nicht leer sein",
@ -68,7 +61,8 @@
"Missing parameter": "Fehlender Parameter", "Missing parameter": "Fehlender Parameter",
"Please login first": "Bitte zuerst einloggen", "Please login first": "Bitte zuerst einloggen",
"The user: %s doesn't exist": "Der Benutzer %s existiert nicht", "The user: %s doesn't exist": "Der Benutzer %s existiert nicht",
"don't support captchaProvider: ": "Unterstütze captchaProvider nicht:" "don't support captchaProvider: ": "Unterstütze captchaProvider nicht:",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Es gibt einen LDAP-Server" "Ldap server exist": "Es gibt einen LDAP-Server"
@ -119,8 +113,7 @@
}, },
"user": { "user": {
"Display name cannot be empty": "Anzeigename darf nicht leer sein", "Display name cannot be empty": "Anzeigename darf nicht leer sein",
"New password cannot contain blank space.": "Das neue Passwort darf keine Leerzeichen enthalten.", "New password cannot contain blank space.": "Das neue Passwort darf keine Leerzeichen enthalten."
"New password must have at least 6 characters": "Das neue Passwort muss mindestens 6 Zeichen haben"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Fehler beim Importieren von Benutzern" "Failed to import users": "Fehler beim Importieren von Benutzern"

View File

@ -18,19 +18,12 @@
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s" "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "Service %s and %s do not match" "Service %s and %s do not match": "Service %s and %s do not match"
}, },
"chat": {
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
"The chat: %s is not found": "The chat: %s is not found",
"The message is invalid": "The message is invalid",
"The message: %s is not found": "The message: %s is not found",
"The provider: %s is invalid": "The provider: %s is invalid",
"The provider: %s is not found": "The provider: %s is not found"
},
"check": { "check": {
"Affiliation cannot be blank": "Affiliation cannot be blank", "Affiliation cannot be blank": "Affiliation cannot be blank",
"DisplayName cannot be blank": "DisplayName cannot be blank", "DisplayName cannot be blank": "DisplayName cannot be blank",
@ -68,7 +61,8 @@
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: " "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Ldap server exist" "Ldap server exist": "Ldap server exist"
@ -119,8 +113,7 @@
}, },
"user": { "user": {
"Display name cannot be empty": "Display name cannot be empty", "Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "New password cannot contain blank space.", "New password cannot contain blank space.": "New password cannot contain blank space."
"New password must have at least 6 characters": "New password must have at least 6 characters"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Failed to import users" "Failed to import users": "Failed to import users"

View File

@ -18,19 +18,12 @@
"The login method: login with password is not enabled for the application": "El método de inicio de sesión: inicio de sesión con contraseña no está habilitado para la aplicación", "The login method: login with password is not enabled for the application": "El método de inicio de sesión: inicio de sesión con contraseña no está habilitado para la aplicación",
"The provider: %s is not enabled for the application": "El proveedor: %s no está habilitado para la aplicación", "The provider: %s is not enabled for the application": "El proveedor: %s no está habilitado para la aplicación",
"Unauthorized operation": "Operación no autorizada", "Unauthorized operation": "Operación no autorizada",
"Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s" "Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "Los servicios %s y %s no coinciden" "Service %s and %s do not match": "Los servicios %s y %s no coinciden"
}, },
"chat": {
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
"The chat: %s is not found": "The chat: %s is not found",
"The message is invalid": "The message is invalid",
"The message: %s is not found": "The message: %s is not found",
"The provider: %s is invalid": "The provider: %s is invalid",
"The provider: %s is not found": "The provider: %s is not found"
},
"check": { "check": {
"Affiliation cannot be blank": "Afiliación no puede estar en blanco", "Affiliation cannot be blank": "Afiliación no puede estar en blanco",
"DisplayName cannot be blank": "El nombre de visualización no puede estar en blanco", "DisplayName cannot be blank": "El nombre de visualización no puede estar en blanco",
@ -68,7 +61,8 @@
"Missing parameter": "Parámetro faltante", "Missing parameter": "Parámetro faltante",
"Please login first": "Por favor, inicia sesión primero", "Please login first": "Por favor, inicia sesión primero",
"The user: %s doesn't exist": "El usuario: %s no existe", "The user: %s doesn't exist": "El usuario: %s no existe",
"don't support captchaProvider: ": "No apoyo a captchaProvider" "don't support captchaProvider: ": "No apoyo a captchaProvider",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
}, },
"ldap": { "ldap": {
"Ldap server exist": "El servidor LDAP existe" "Ldap server exist": "El servidor LDAP existe"
@ -119,8 +113,7 @@
}, },
"user": { "user": {
"Display name cannot be empty": "El nombre de pantalla no puede estar vacío", "Display name cannot be empty": "El nombre de pantalla no puede estar vacío",
"New password cannot contain blank space.": "La nueva contraseña no puede contener espacios en blanco.", "New password cannot contain blank space.": "La nueva contraseña no puede contener espacios en blanco."
"New password must have at least 6 characters": "La nueva contraseña debe tener al menos 6 caracteres"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Error al importar usuarios" "Failed to import users": "Error al importar usuarios"

142
i18n/locales/fa/data.json Normal file
View File

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

142
i18n/locales/fi/data.json Normal file
View File

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

View File

@ -18,19 +18,12 @@
"The login method: login with password is not enabled for the application": "La méthode de connexion : connexion avec mot de passe n'est pas activée pour l'application", "The login method: login with password is not enabled for the application": "La méthode de connexion : connexion avec mot de passe n'est pas activée pour l'application",
"The provider: %s is not enabled for the application": "Le fournisseur :%s n'est pas activé pour l'application", "The provider: %s is not enabled for the application": "Le fournisseur :%s n'est pas activé pour l'application",
"Unauthorized operation": "Opération non autorisée", "Unauthorized operation": "Opération non autorisée",
"Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s" "Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "Les services %s et %s ne correspondent pas" "Service %s and %s do not match": "Les services %s et %s ne correspondent pas"
}, },
"chat": {
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
"The chat: %s is not found": "The chat: %s is not found",
"The message is invalid": "The message is invalid",
"The message: %s is not found": "The message: %s is not found",
"The provider: %s is invalid": "The provider: %s is invalid",
"The provider: %s is not found": "The provider: %s is not found"
},
"check": { "check": {
"Affiliation cannot be blank": "Affiliation ne peut pas être vide", "Affiliation cannot be blank": "Affiliation ne peut pas être vide",
"DisplayName cannot be blank": "Le nom d'affichage ne peut pas être vide", "DisplayName cannot be blank": "Le nom d'affichage ne peut pas être vide",
@ -68,7 +61,8 @@
"Missing parameter": "Paramètre manquant", "Missing parameter": "Paramètre manquant",
"Please login first": "Veuillez d'abord vous connecter", "Please login first": "Veuillez d'abord vous connecter",
"The user: %s doesn't exist": "L'utilisateur : %s n'existe pas", "The user: %s doesn't exist": "L'utilisateur : %s n'existe pas",
"don't support captchaProvider: ": "Ne pas prendre en charge la captchaProvider" "don't support captchaProvider: ": "Ne pas prendre en charge la captchaProvider",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Le serveur LDAP existe" "Ldap server exist": "Le serveur LDAP existe"
@ -119,8 +113,7 @@
}, },
"user": { "user": {
"Display name cannot be empty": "Le nom d'affichage ne peut pas être vide", "Display name cannot be empty": "Le nom d'affichage ne peut pas être vide",
"New password cannot contain blank space.": "Le nouveau mot de passe ne peut pas contenir d'espace.", "New password cannot contain blank space.": "Le nouveau mot de passe ne peut pas contenir d'espace."
"New password must have at least 6 characters": "Le nouveau mot de passe doit comporter au moins 6 caractères"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Échec de l'importation des utilisateurs" "Failed to import users": "Échec de l'importation des utilisateurs"

142
i18n/locales/he/data.json Normal file
View File

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

View File

@ -18,19 +18,12 @@
"The login method: login with password is not enabled for the application": "Metode login: login dengan kata sandi tidak diaktifkan untuk aplikasi tersebut", "The login method: login with password is not enabled for the application": "Metode login: login dengan kata sandi tidak diaktifkan untuk aplikasi tersebut",
"The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini", "The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini",
"Unauthorized operation": "Operasi tidak sah", "Unauthorized operation": "Operasi tidak sah",
"Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s" "Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "Layanan %s dan %s tidak cocok" "Service %s and %s do not match": "Layanan %s dan %s tidak cocok"
}, },
"chat": {
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
"The chat: %s is not found": "The chat: %s is not found",
"The message is invalid": "The message is invalid",
"The message: %s is not found": "The message: %s is not found",
"The provider: %s is invalid": "The provider: %s is invalid",
"The provider: %s is not found": "The provider: %s is not found"
},
"check": { "check": {
"Affiliation cannot be blank": "Keterkaitan tidak boleh kosong", "Affiliation cannot be blank": "Keterkaitan tidak boleh kosong",
"DisplayName cannot be blank": "Nama Pengguna tidak boleh kosong", "DisplayName cannot be blank": "Nama Pengguna tidak boleh kosong",
@ -68,7 +61,8 @@
"Missing parameter": "Parameter hilang", "Missing parameter": "Parameter hilang",
"Please login first": "Silahkan login terlebih dahulu", "Please login first": "Silahkan login terlebih dahulu",
"The user: %s doesn't exist": "Pengguna: %s tidak ada", "The user: %s doesn't exist": "Pengguna: %s tidak ada",
"don't support captchaProvider: ": "Jangan mendukung captchaProvider:" "don't support captchaProvider: ": "Jangan mendukung captchaProvider:",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Server ldap ada" "Ldap server exist": "Server ldap ada"
@ -119,8 +113,7 @@
}, },
"user": { "user": {
"Display name cannot be empty": "Nama tampilan tidak boleh kosong", "Display name cannot be empty": "Nama tampilan tidak boleh kosong",
"New password cannot contain blank space.": "Kata sandi baru tidak boleh mengandung spasi kosong.", "New password cannot contain blank space.": "Kata sandi baru tidak boleh mengandung spasi kosong."
"New password must have at least 6 characters": "Kata sandi baru harus memiliki setidaknya 6 karakter"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Gagal mengimpor pengguna" "Failed to import users": "Gagal mengimpor pengguna"

150
i18n/locales/it/data.json Normal file
View File

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

View File

@ -18,19 +18,12 @@
"The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません", "The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません",
"The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません", "The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません",
"Unauthorized operation": "不正操作", "Unauthorized operation": "不正操作",
"Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s" "Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "サービス%sと%sは一致しません" "Service %s and %s do not match": "サービス%sと%sは一致しません"
}, },
"chat": {
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
"The chat: %s is not found": "The chat: %s is not found",
"The message is invalid": "The message is invalid",
"The message: %s is not found": "The message: %s is not found",
"The provider: %s is invalid": "The provider: %s is invalid",
"The provider: %s is not found": "The provider: %s is not found"
},
"check": { "check": {
"Affiliation cannot be blank": "所属は空白にできません", "Affiliation cannot be blank": "所属は空白にできません",
"DisplayName cannot be blank": "表示名は空白にできません", "DisplayName cannot be blank": "表示名は空白にできません",
@ -68,7 +61,8 @@
"Missing parameter": "不足しているパラメーター", "Missing parameter": "不足しているパラメーター",
"Please login first": "最初にログインしてください", "Please login first": "最初にログインしてください",
"The user: %s doesn't exist": "そのユーザー:%sは存在しません", "The user: %s doesn't exist": "そのユーザー:%sは存在しません",
"don't support captchaProvider: ": "captchaProviderをサポートしないでください" "don't support captchaProvider: ": "captchaProviderをサポートしないでください",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
}, },
"ldap": { "ldap": {
"Ldap server exist": "LDAPサーバーは存在します" "Ldap server exist": "LDAPサーバーは存在します"
@ -119,8 +113,7 @@
}, },
"user": { "user": {
"Display name cannot be empty": "表示名は空にできません", "Display name cannot be empty": "表示名は空にできません",
"New password cannot contain blank space.": "新しいパスワードにはスペースを含めることはできません。", "New password cannot contain blank space.": "新しいパスワードにはスペースを含めることはできません。"
"New password must have at least 6 characters": "新しいパスワードは少なくとも6文字必要です"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "ユーザーのインポートに失敗しました" "Failed to import users": "ユーザーのインポートに失敗しました"

142
i18n/locales/kk/data.json Normal file
View File

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

View File

@ -18,19 +18,12 @@
"The login method: login with password is not enabled for the application": "어플리케이션에서는 암호를 사용한 로그인 방법이 활성화되어 있지 않습니다", "The login method: login with password is not enabled for the application": "어플리케이션에서는 암호를 사용한 로그인 방법이 활성화되어 있지 않습니다",
"The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다", "The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
"Unauthorized operation": "무단 조작", "Unauthorized operation": "무단 조작",
"Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s" "Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다" "Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다"
}, },
"chat": {
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
"The chat: %s is not found": "The chat: %s is not found",
"The message is invalid": "The message is invalid",
"The message: %s is not found": "The message: %s is not found",
"The provider: %s is invalid": "The provider: %s is invalid",
"The provider: %s is not found": "The provider: %s is not found"
},
"check": { "check": {
"Affiliation cannot be blank": "소속은 비워 둘 수 없습니다", "Affiliation cannot be blank": "소속은 비워 둘 수 없습니다",
"DisplayName cannot be blank": "DisplayName는 비어 있을 수 없습니다", "DisplayName cannot be blank": "DisplayName는 비어 있을 수 없습니다",
@ -68,7 +61,8 @@
"Missing parameter": "누락된 매개변수", "Missing parameter": "누락된 매개변수",
"Please login first": "먼저 로그인 하십시오", "Please login first": "먼저 로그인 하십시오",
"The user: %s doesn't exist": "사용자 %s는 존재하지 않습니다", "The user: %s doesn't exist": "사용자 %s는 존재하지 않습니다",
"don't support captchaProvider: ": "CaptchaProvider를 지원하지 마세요" "don't support captchaProvider: ": "CaptchaProvider를 지원하지 마세요",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
}, },
"ldap": { "ldap": {
"Ldap server exist": "LDAP 서버가 존재합니다" "Ldap server exist": "LDAP 서버가 존재합니다"
@ -119,8 +113,7 @@
}, },
"user": { "user": {
"Display name cannot be empty": "디스플레이 이름은 비어 있을 수 없습니다", "Display name cannot be empty": "디스플레이 이름은 비어 있을 수 없습니다",
"New password cannot contain blank space.": "새 비밀번호에는 공백이 포함될 수 없습니다.", "New password cannot contain blank space.": "새 비밀번호에는 공백이 포함될 수 없습니다."
"New password must have at least 6 characters": "새로운 비밀번호는 최소 6자 이상이어야 합니다"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "사용자 가져오기를 실패했습니다" "Failed to import users": "사용자 가져오기를 실패했습니다"

150
i18n/locales/ms/data.json Normal file
View File

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

142
i18n/locales/nl/data.json Normal file
View File

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

142
i18n/locales/pl/data.json Normal file
View File

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

142
i18n/locales/pt/data.json Normal file
View File

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

View File

@ -18,19 +18,12 @@
"The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения", "The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения",
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения", "The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
"Unauthorized operation": "Несанкционированная операция", "Unauthorized operation": "Несанкционированная операция",
"Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s" "Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "Сервисы %s и %s не совпадают" "Service %s and %s do not match": "Сервисы %s и %s не совпадают"
}, },
"chat": {
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
"The chat: %s is not found": "The chat: %s is not found",
"The message is invalid": "The message is invalid",
"The message: %s is not found": "The message: %s is not found",
"The provider: %s is invalid": "The provider: %s is invalid",
"The provider: %s is not found": "The provider: %s is not found"
},
"check": { "check": {
"Affiliation cannot be blank": "Принадлежность не может быть пустым значением", "Affiliation cannot be blank": "Принадлежность не может быть пустым значением",
"DisplayName cannot be blank": "Имя отображения не может быть пустым", "DisplayName cannot be blank": "Имя отображения не может быть пустым",
@ -68,7 +61,8 @@
"Missing parameter": "Отсутствующий параметр", "Missing parameter": "Отсутствующий параметр",
"Please login first": "Пожалуйста, сначала войдите в систему", "Please login first": "Пожалуйста, сначала войдите в систему",
"The user: %s doesn't exist": "Пользователь %s не существует", "The user: %s doesn't exist": "Пользователь %s не существует",
"don't support captchaProvider: ": "не поддерживайте captchaProvider:" "don't support captchaProvider: ": "не поддерживайте captchaProvider:",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
}, },
"ldap": { "ldap": {
"Ldap server exist": "LDAP-сервер существует" "Ldap server exist": "LDAP-сервер существует"
@ -119,8 +113,7 @@
}, },
"user": { "user": {
"Display name cannot be empty": "Отображаемое имя не может быть пустым", "Display name cannot be empty": "Отображаемое имя не может быть пустым",
"New password cannot contain blank space.": "Новый пароль не может содержать пробелы.", "New password cannot contain blank space.": "Новый пароль не может содержать пробелы."
"New password must have at least 6 characters": "Новый пароль должен содержать не менее 6 символов"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Не удалось импортировать пользователей" "Failed to import users": "Не удалось импортировать пользователей"

142
i18n/locales/sv/data.json Normal file
View File

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

150
i18n/locales/tr/data.json Normal file
View File

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

142
i18n/locales/uk/data.json Normal file
View File

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

View File

@ -18,19 +18,12 @@
"The login method: login with password is not enabled for the application": "Phương thức đăng nhập: đăng nhập bằng mật khẩu không được kích hoạt cho ứng dụng", "The login method: login with password is not enabled for the application": "Phương thức đăng nhập: đăng nhập bằng mật khẩu không được kích hoạt cho ứng dụng",
"The provider: %s is not enabled for the application": "Nhà cung cấp: %s không được kích hoạt cho ứng dụng", "The provider: %s is not enabled for the application": "Nhà cung cấp: %s không được kích hoạt cho ứng dụng",
"Unauthorized operation": "Hoạt động không được ủy quyền", "Unauthorized operation": "Hoạt động không được ủy quyền",
"Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s" "Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s",
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "Dịch sang tiếng Việt: Dịch vụ %s và %s không khớp" "Service %s and %s do not match": "Dịch sang tiếng Việt: Dịch vụ %s và %s không khớp"
}, },
"chat": {
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
"The chat: %s is not found": "The chat: %s is not found",
"The message is invalid": "The message is invalid",
"The message: %s is not found": "The message: %s is not found",
"The provider: %s is invalid": "The provider: %s is invalid",
"The provider: %s is not found": "The provider: %s is not found"
},
"check": { "check": {
"Affiliation cannot be blank": "Tình trạng liên kết không thể để trống", "Affiliation cannot be blank": "Tình trạng liên kết không thể để trống",
"DisplayName cannot be blank": "Tên hiển thị không thể để trống", "DisplayName cannot be blank": "Tên hiển thị không thể để trống",
@ -68,7 +61,8 @@
"Missing parameter": "Thiếu tham số", "Missing parameter": "Thiếu tham số",
"Please login first": "Vui lòng đăng nhập trước", "Please login first": "Vui lòng đăng nhập trước",
"The user: %s doesn't exist": "Người dùng: %s không tồn tại", "The user: %s doesn't exist": "Người dùng: %s không tồn tại",
"don't support captchaProvider: ": "không hỗ trợ captchaProvider: " "don't support captchaProvider: ": "không hỗ trợ captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
}, },
"ldap": { "ldap": {
"Ldap server exist": "Máy chủ LDAP tồn tại" "Ldap server exist": "Máy chủ LDAP tồn tại"
@ -119,8 +113,7 @@
}, },
"user": { "user": {
"Display name cannot be empty": "Tên hiển thị không thể trống", "Display name cannot be empty": "Tên hiển thị không thể trống",
"New password cannot contain blank space.": "Mật khẩu mới không thể chứa dấu trắng.", "New password cannot contain blank space.": "Mật khẩu mới không thể chứa dấu trắng."
"New password must have at least 6 characters": "Mật khẩu mới phải có ít nhất 6 ký tự"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "Không thể nhập người dùng" "Failed to import users": "Không thể nhập người dùng"

View File

@ -18,19 +18,12 @@
"The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式", "The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式",
"The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用", "The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用",
"Unauthorized operation": "未授权的操作", "Unauthorized operation": "未授权的操作",
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s" "Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s",
"User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "服务%s与%s不匹配" "Service %s and %s do not match": "服务%s与%s不匹配"
}, },
"chat": {
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
"The chat: %s is not found": "The chat: %s is not found",
"The message is invalid": "The message is invalid",
"The message: %s is not found": "The message: %s is not found",
"The provider: %s is invalid": "The provider: %s is invalid",
"The provider: %s is not found": "The provider: %s is not found"
},
"check": { "check": {
"Affiliation cannot be blank": "工作单位不可为空", "Affiliation cannot be blank": "工作单位不可为空",
"DisplayName cannot be blank": "显示名称不可为空", "DisplayName cannot be blank": "显示名称不可为空",
@ -68,7 +61,8 @@
"Missing parameter": "缺少参数", "Missing parameter": "缺少参数",
"Please login first": "请先登录", "Please login first": "请先登录",
"The user: %s doesn't exist": "用户: %s不存在", "The user: %s doesn't exist": "用户: %s不存在",
"don't support captchaProvider: ": "不支持验证码提供商: " "don't support captchaProvider: ": "不支持验证码提供商: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
}, },
"ldap": { "ldap": {
"Ldap server exist": "LDAP服务器已存在" "Ldap server exist": "LDAP服务器已存在"
@ -119,8 +113,7 @@
}, },
"user": { "user": {
"Display name cannot be empty": "显示名称不可为空", "Display name cannot be empty": "显示名称不可为空",
"New password cannot contain blank space.": "新密码不可以包含空格", "New password cannot contain blank space.": "新密码不可以包含空格"
"New password must have at least 6 characters": "新密码至少需要6位字符"
}, },
"user_upload": { "user_upload": {
"Failed to import users": "导入用户失败" "Failed to import users": "导入用户失败"

View File

@ -20,32 +20,37 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
_ "net/url"
_ "time"
"github.com/casdoor/casdoor/util"
"github.com/mitchellh/mapstructure"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
type CustomIdProvider struct { type CustomIdProvider struct {
Client *http.Client Client *http.Client
Config *oauth2.Config Config *oauth2.Config
UserInfoUrl string
UserInfoURL string
TokenURL string
AuthURL string
UserMapping map[string]string
Scopes []string
} }
func NewCustomIdProvider(clientId string, clientSecret string, redirectUrl string, authUrl string, tokenUrl string, userInfoUrl string) *CustomIdProvider { func NewCustomIdProvider(idpInfo *ProviderInfo, redirectUrl string) *CustomIdProvider {
idp := &CustomIdProvider{} idp := &CustomIdProvider{}
idp.UserInfoUrl = userInfoUrl
config := &oauth2.Config{ idp.Config = &oauth2.Config{
ClientID: clientId, ClientID: idpInfo.ClientId,
ClientSecret: clientSecret, ClientSecret: idpInfo.ClientSecret,
RedirectURL: redirectUrl, RedirectURL: redirectUrl,
Endpoint: oauth2.Endpoint{ Endpoint: oauth2.Endpoint{
AuthURL: authUrl, AuthURL: idpInfo.AuthURL,
TokenURL: tokenUrl, TokenURL: idpInfo.TokenURL,
}, },
} }
idp.Config = config idp.UserInfoURL = idpInfo.UserInfoURL
idp.UserMapping = idpInfo.UserMapping
return idp return idp
} }
@ -60,22 +65,20 @@ func (idp *CustomIdProvider) GetToken(code string) (*oauth2.Token, error) {
} }
type CustomUserInfo struct { type CustomUserInfo struct {
Id string `json:"sub"` Id string `mapstructure:"id"`
Name string `json:"preferred_username,omitempty"` Username string `mapstructure:"username"`
DisplayName string `json:"name"` DisplayName string `mapstructure:"displayName"`
Email string `json:"email"` Email string `mapstructure:"email"`
AvatarUrl string `json:"picture"` AvatarUrl string `mapstructure:"avatarUrl"`
Status string `json:"status"`
Msg string `json:"msg"`
} }
func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) { func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
ctUserinfo := &CustomUserInfo{}
accessToken := token.AccessToken accessToken := token.AccessToken
request, err := http.NewRequest("GET", idp.UserInfoUrl, nil) request, err := http.NewRequest("GET", idp.UserInfoURL, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// add accessToken to request header // add accessToken to request header
request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken)) request.Header.Add("Authorization", fmt.Sprintf("Bearer %s", accessToken))
resp, err := idp.Client.Do(request) resp, err := idp.Client.Do(request)
@ -89,21 +92,40 @@ func (idp *CustomIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
return nil, err return nil, err
} }
err = json.Unmarshal(data, ctUserinfo) var dataMap map[string]interface{}
err = json.Unmarshal(data, &dataMap)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if ctUserinfo.Status != "" { // map user info
return nil, fmt.Errorf("err: %s", ctUserinfo.Msg) for k, v := range idp.UserMapping {
_, ok := dataMap[v]
if !ok {
return nil, fmt.Errorf("cannot find %s in user from castom provider", v)
}
dataMap[k] = dataMap[v]
}
// try to parse id to string
id, err := util.ParseIdToString(dataMap["id"])
if err != nil {
return nil, err
}
dataMap["id"] = id
customUserinfo := &CustomUserInfo{}
err = mapstructure.Decode(dataMap, customUserinfo)
if err != nil {
return nil, err
} }
userInfo := &UserInfo{ userInfo := &UserInfo{
Id: ctUserinfo.Id, Id: customUserinfo.Id,
Username: ctUserinfo.Name, Username: customUserinfo.Username,
DisplayName: ctUserinfo.DisplayName, DisplayName: customUserinfo.DisplayName,
Email: ctUserinfo.Email, Email: customUserinfo.Email,
AvatarUrl: ctUserinfo.AvatarUrl, AvatarUrl: customUserinfo.AvatarUrl,
} }
return userInfo, nil return userInfo, nil
} }

View File

@ -72,13 +72,13 @@ type FacebookCheckToken struct {
} }
// FacebookCheckTokenData // FacebookCheckTokenData
// Get more detail via: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken // Get more detail via: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow#checktoken
type FacebookCheckTokenData struct { type FacebookCheckTokenData struct {
UserId string `json:"user_id"` UserId string `json:"user_id"`
} }
// GetToken use code get access_token (*operation of getting code ought to be done in front) // GetToken use code get access_token (*operation of getting code ought to be done in front)
// get more detail via: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#confirm // get more detail via: https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow#confirm
func (idp *FacebookIdProvider) GetToken(code string) (*oauth2.Token, error) { func (idp *FacebookIdProvider) GetToken(code string) (*oauth2.Token, error) {
params := url.Values{} params := url.Values{}
params.Add("client_id", idp.Config.ClientID) params.Add("client_id", idp.Config.ClientID)

View File

@ -132,7 +132,7 @@ func (idp *GiteeIdProvider) GetToken(code string) (*oauth2.Token, error) {
"type": "User", "type": "User",
"blog": null, "blog": null,
"weibo": null, "weibo": null,
"bio": "个人博客https://gitee.com/xxx/xxx/pages", "bio": "bio",
"public_repos": 2, "public_repos": 2,
"public_gists": 0, "public_gists": 0,
"followers": 0, "followers": 0,

View File

@ -21,15 +21,39 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"strings"
"time"
"github.com/casdoor/casdoor/util"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
const GoogleIdTokenKey = "GoogleIdToken"
type GoogleIdProvider struct { type GoogleIdProvider struct {
Client *http.Client Client *http.Client
Config *oauth2.Config Config *oauth2.Config
} }
// https://developers.google.com/identity/sign-in/web/backend-auth#calling-the-tokeninfo-endpoint
type GoogleIdToken struct {
// These six fields are included in all Google ID Tokens.
Iss string `json:"iss"` // The issuer, or signer, of the token. For Google-signed ID tokens, this value is https://accounts.google.com.
Sub string `json:"sub"` // The subject: the ID that represents the principal making the request.
Azp string `json:"azp"` // Optional. Who the token was issued to. Here is the ClientID
Aud string `json:"aud"` // The audience of the token. Here is the ClientID
Iat string `json:"iat"` // Unix epoch time when the token was issued.
Exp string `json:"exp"` // Unix epoch time when the token expires.
// These seven fields are only included when the user has granted the "profile" and "email" OAuth scopes to the application.
Email string `json:"email"`
EmailVerified string `json:"email_verified"`
Name string `json:"name"`
Picture string `json:"picture"`
GivenName string `json:"given_name"`
FamilyName string `json:"family_name"`
Locale string `json:"locale"`
}
func NewGoogleIdProvider(clientId string, clientSecret string, redirectUrl string) *GoogleIdProvider { func NewGoogleIdProvider(clientId string, clientSecret string, redirectUrl string) *GoogleIdProvider {
idp := &GoogleIdProvider{} idp := &GoogleIdProvider{}
@ -61,6 +85,25 @@ func (idp *GoogleIdProvider) getConfig() *oauth2.Config {
} }
func (idp *GoogleIdProvider) GetToken(code string) (*oauth2.Token, error) { func (idp *GoogleIdProvider) GetToken(code string) (*oauth2.Token, error) {
// Obtained the GoogleIdToken through Google OneTap authorization.
if strings.HasPrefix(code, GoogleIdTokenKey) {
code = strings.TrimPrefix(code, GoogleIdTokenKey+"-")
var googleIdToken GoogleIdToken
if err := json.Unmarshal([]byte(code), &googleIdToken); err != nil {
return nil, err
}
expiry := int64(util.ParseInt(googleIdToken.Exp))
token := &oauth2.Token{
AccessToken: fmt.Sprintf("%v-%v", GoogleIdTokenKey, googleIdToken.Sub),
TokenType: "Bearer",
Expiry: time.Unix(expiry, 0),
}
token = token.WithExtra(map[string]interface{}{
GoogleIdTokenKey: googleIdToken,
})
return token, nil
}
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, idp.Client) ctx := context.WithValue(context.Background(), oauth2.HTTPClient, idp.Client)
return idp.Config.Exchange(ctx, code) return idp.Config.Exchange(ctx, code)
} }
@ -88,6 +131,20 @@ type GoogleUserInfo struct {
} }
func (idp *GoogleIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) { func (idp *GoogleIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
if strings.HasPrefix(token.AccessToken, GoogleIdTokenKey) {
googleIdToken, ok := token.Extra(GoogleIdTokenKey).(GoogleIdToken)
if !ok {
return nil, errors.New("invalid googleIdToken")
}
userInfo := UserInfo{
Id: googleIdToken.Sub,
Username: googleIdToken.Email,
DisplayName: googleIdToken.Name,
Email: googleIdToken.Email,
AvatarUrl: googleIdToken.Picture,
}
return &userInfo, nil
}
url := fmt.Sprintf("https://www.googleapis.com/oauth2/v2/userinfo?alt=json&access_token=%s", token.AccessToken) url := fmt.Sprintf("https://www.googleapis.com/oauth2/v2/userinfo?alt=json&access_token=%s", token.AccessToken)
resp, err := idp.Client.Get(url) resp, err := idp.Client.Get(url)
if err != nil { if err != nil {

View File

@ -19,6 +19,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"reflect" "reflect"
"strings"
"time" "time"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
@ -97,6 +98,9 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Session: &amazon.Session{}, Session: &amazon.Session{},
} }
case "Apple": case "Apple":
if !strings.Contains(redirectUrl, "/api/callback") {
redirectUrl = strings.Replace(redirectUrl, "/callback", "/api/callback", 1)
}
idp = GothIdProvider{ idp = GothIdProvider{
Provider: apple.New(clientId, clientSecret, redirectUrl, nil), Provider: apple.New(clientId, clientSecret, redirectUrl, nil),
Session: &apple.Session{}, Session: &apple.Session{},
@ -392,7 +396,9 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
// Goth's idp all implement the Client method, but since the goth.Provider interface does not provide to modify idp's client method, reflection is required // Goth's idp all implement the Client method, but since the goth.Provider interface does not provide to modify idp's client method, reflection is required
func (idp *GothIdProvider) SetHttpClient(client *http.Client) { func (idp *GothIdProvider) SetHttpClient(client *http.Client) {
idpClient := reflect.ValueOf(idp.Provider).Elem().FieldByName("HTTPClient") idpClient := reflect.ValueOf(idp.Provider).Elem().FieldByName("HTTPClient")
idpClient.Set(reflect.ValueOf(client)) if idpClient.IsValid() {
idpClient.Set(reflect.ValueOf(client))
}
} }
func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) { func (idp *GothIdProvider) GetToken(code string) (*oauth2.Token, error) {
@ -468,6 +474,8 @@ func getUser(gothUser goth.User, provider string) *UserInfo {
if provider == "steam" { if provider == "steam" {
user.Username = user.Id user.Username = user.Id
user.Email = "" user.Email = ""
} else if provider == "apple" {
user.Username = util.GetUsernameFromEmail(user.Email)
} }
return &user return &user
} }

70
idp/metamask.go Normal file
View File

@ -0,0 +1,70 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package idp
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
"golang.org/x/oauth2"
)
type MetaMaskIdProvider struct {
Client *http.Client
}
func NewMetaMaskIdProvider() *MetaMaskIdProvider {
idp := &MetaMaskIdProvider{}
return idp
}
func (idp *MetaMaskIdProvider) SetHttpClient(client *http.Client) {
idp.Client = client
}
func (idp *MetaMaskIdProvider) GetToken(code string) (*oauth2.Token, error) {
web3AuthToken := Web3AuthToken{}
if err := json.Unmarshal([]byte(code), &web3AuthToken); err != nil {
return nil, err
}
token := &oauth2.Token{
AccessToken: web3AuthToken.Signature,
TokenType: "Bearer",
Expiry: time.Now().AddDate(0, 1, 0),
}
token = token.WithExtra(map[string]interface{}{
Web3AuthTokenKey: web3AuthToken,
})
return token, nil
}
func (idp *MetaMaskIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
// TODO use "github.com/ethereum/go-ethereum" to check address's eth balance or transaction
web3AuthToken, ok := token.Extra(Web3AuthTokenKey).(Web3AuthToken)
if !ok {
return nil, errors.New("invalid web3AuthToken")
}
userInfo := &UserInfo{
Id: web3AuthToken.Address,
Username: web3AuthToken.Address,
DisplayName: web3AuthToken.Address,
AvatarUrl: fmt.Sprintf("metamask:%v", web3AuthToken.Address),
}
return userInfo, nil
}

View File

@ -32,72 +32,93 @@ type UserInfo struct {
AvatarUrl string AvatarUrl string
} }
type ProviderInfo struct {
Type string
SubType string
ClientId string
ClientSecret string
AppId string
HostUrl string
RedirectUrl string
TokenURL string
AuthURL string
UserInfoURL string
UserMapping map[string]string
}
type IdProvider interface { type IdProvider interface {
SetHttpClient(client *http.Client) SetHttpClient(client *http.Client)
GetToken(code string) (*oauth2.Token, error) GetToken(code string) (*oauth2.Token, error)
GetUserInfo(token *oauth2.Token) (*UserInfo, error) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
} }
func GetIdProvider(typ string, subType string, clientId string, clientSecret string, appId string, redirectUrl string, hostUrl string, authUrl string, tokenUrl string, userInfoUrl string) IdProvider { func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) IdProvider {
if typ == "GitHub" { switch idpInfo.Type {
return NewGithubIdProvider(clientId, clientSecret, redirectUrl) case "GitHub":
} else if typ == "Google" { return NewGithubIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
return NewGoogleIdProvider(clientId, clientSecret, redirectUrl) case "Google":
} else if typ == "QQ" { return NewGoogleIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
return NewQqIdProvider(clientId, clientSecret, redirectUrl) case "QQ":
} else if typ == "WeChat" { return NewQqIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
return NewWeChatIdProvider(clientId, clientSecret, redirectUrl) case "WeChat":
} else if typ == "Facebook" { return NewWeChatIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
return NewFacebookIdProvider(clientId, clientSecret, redirectUrl) case "Facebook":
} else if typ == "DingTalk" { return NewFacebookIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
return NewDingTalkIdProvider(clientId, clientSecret, redirectUrl) case "DingTalk":
} else if typ == "Weibo" { return NewDingTalkIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
return NewWeiBoIdProvider(clientId, clientSecret, redirectUrl) case "Weibo":
} else if typ == "Gitee" { return NewWeiBoIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
return NewGiteeIdProvider(clientId, clientSecret, redirectUrl) case "Gitee":
} else if typ == "LinkedIn" { return NewGiteeIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
return NewLinkedInIdProvider(clientId, clientSecret, redirectUrl) case "LinkedIn":
} else if typ == "WeCom" { return NewLinkedInIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
if subType == "Internal" { case "WeCom":
return NewWeComInternalIdProvider(clientId, clientSecret, redirectUrl) if idpInfo.SubType == "Internal" {
} else if subType == "Third-party" { return NewWeComInternalIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
return NewWeComIdProvider(clientId, clientSecret, redirectUrl) } else if idpInfo.SubType == "Third-party" {
return NewWeComIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
} else { } else {
return nil return nil
} }
} else if typ == "Lark" { case "Lark":
return NewLarkIdProvider(clientId, clientSecret, redirectUrl) return NewLarkIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
} else if typ == "GitLab" { case "GitLab":
return NewGitlabIdProvider(clientId, clientSecret, redirectUrl) return NewGitlabIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
} else if typ == "Adfs" { case "Adfs":
return NewAdfsIdProvider(clientId, clientSecret, redirectUrl, hostUrl) return NewAdfsIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
} else if typ == "Baidu" { case "Baidu":
return NewBaiduIdProvider(clientId, clientSecret, redirectUrl) return NewBaiduIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
} else if typ == "Alipay" { case "Alipay":
return NewAlipayIdProvider(clientId, clientSecret, redirectUrl) return NewAlipayIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
} else if typ == "Custom" { case "Custom":
return NewCustomIdProvider(clientId, clientSecret, redirectUrl, authUrl, tokenUrl, userInfoUrl) return NewCustomIdProvider(idpInfo, redirectUrl)
} else if typ == "Infoflow" { case "Infoflow":
if subType == "Internal" { if idpInfo.SubType == "Internal" {
return NewInfoflowInternalIdProvider(clientId, clientSecret, appId, redirectUrl) return NewInfoflowInternalIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, idpInfo.AppId, redirectUrl)
} else if subType == "Third-party" { } else if idpInfo.SubType == "Third-party" {
return NewInfoflowIdProvider(clientId, clientSecret, appId, redirectUrl) return NewInfoflowIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, idpInfo.AppId, redirectUrl)
} else { } else {
return nil return nil
} }
} else if typ == "Casdoor" { case "Casdoor":
return NewCasdoorIdProvider(clientId, clientSecret, redirectUrl, hostUrl) return NewCasdoorIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
} else if typ == "Okta" { case "Okta":
return NewOktaIdProvider(clientId, clientSecret, redirectUrl, hostUrl) return NewOktaIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
} else if typ == "Douyin" { case "Douyin":
return NewDouyinIdProvider(clientId, clientSecret, redirectUrl) return NewDouyinIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
} else if isGothSupport(typ) { case "Bilibili":
return NewGothIdProvider(typ, clientId, clientSecret, redirectUrl, hostUrl) return NewBilibiliIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl)
} else if typ == "Bilibili" { case "MetaMask":
return NewBilibiliIdProvider(clientId, clientSecret, redirectUrl) return NewMetaMaskIdProvider()
case "Web3Onboard":
return NewWeb3OnboardIdProvider()
default:
if isGothSupport(idpInfo.Type) {
return NewGothIdProvider(idpInfo.Type, idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.HostUrl)
}
return nil
} }
return nil
} }
var gothList = []string{ var gothList = []string{

103
idp/web3onboard.go Normal file
View File

@ -0,0 +1,103 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package idp
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"time"
"golang.org/x/oauth2"
)
const Web3AuthTokenKey = "web3AuthToken"
type Web3AuthToken struct {
Address string `json:"address"`
Nonce string `json:"nonce"`
CreateAt uint64 `json:"createAt"`
TypedData string `json:"typedData"` // typed data use for application
Signature string `json:"signature"` // signature for typed data
WalletType string `json:"walletType"` // e.g."MetaMask", "Coinbase"
}
type Web3OnboardIdProvider struct {
Client *http.Client
}
func NewWeb3OnboardIdProvider() *Web3OnboardIdProvider {
idp := &Web3OnboardIdProvider{}
return idp
}
func (idp *Web3OnboardIdProvider) SetHttpClient(client *http.Client) {
idp.Client = client
}
func (idp *Web3OnboardIdProvider) GetToken(code string) (*oauth2.Token, error) {
web3AuthToken := Web3AuthToken{}
if err := json.Unmarshal([]byte(code), &web3AuthToken); err != nil {
return nil, err
}
token := &oauth2.Token{
AccessToken: fmt.Sprintf("%v:%v", Web3AuthTokenKey, web3AuthToken.Address),
TokenType: "Bearer",
Expiry: time.Now().AddDate(0, 1, 0),
}
token = token.WithExtra(map[string]interface{}{
Web3AuthTokenKey: web3AuthToken,
})
return token, nil
}
func (idp *Web3OnboardIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
web3AuthToken, ok := token.Extra(Web3AuthTokenKey).(Web3AuthToken)
if !ok {
return nil, errors.New("invalid web3AuthToken")
}
fmtAddress := fmt.Sprintf("%v_%v",
strings.ReplaceAll(strings.TrimSpace(web3AuthToken.WalletType), " ", "_"),
web3AuthToken.Address,
)
userInfo := &UserInfo{
Id: fmtAddress,
Username: fmtAddress,
DisplayName: fmtAddress,
AvatarUrl: fmt.Sprintf("metamask:%v", forceEthereumAddress(web3AuthToken.Address)),
}
return userInfo, nil
}
func forceEthereumAddress(address string) string {
// The required address to general MetaMask avatar is a string of length 42 that represents an Ethereum address.
// This function is used to force any address as an Ethereum address
address = strings.TrimSpace(address)
var builder strings.Builder
for _, ch := range address {
builder.WriteRune(ch)
}
for len(builder.String()) < 42 {
builder.WriteString("0")
}
if len(builder.String()) > 42 {
return builder.String()[:42]
}
return builder.String()
}

View File

@ -198,12 +198,22 @@ func (idp *WeChatIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (string, error) { 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) 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) request, err := http.NewRequest("GET", accessTokenUrl, nil)
if err != nil {
return "", err
}
client := new(http.Client) client := new(http.Client)
resp, err := client.Do(request) resp, err := client.Do(request)
if err != nil {
return "", err
}
defer resp.Body.Close()
respBytes, err := ioutil.ReadAll(resp.Body) respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return "", err return "", err
} }
var data struct { var data struct {
ExpireIn int `json:"expires_in"` ExpireIn int `json:"expires_in"`
AccessToken string `json:"access_token"` AccessToken string `json:"access_token"`
@ -212,20 +222,30 @@ func GetWechatOfficialAccountAccessToken(clientId string, clientSecret string) (
if err != nil { if err != nil {
return "", err return "", err
} }
return data.AccessToken, nil return data.AccessToken, nil
} }
func GetWechatOfficialAccountQRCode(clientId string, clientSecret string) (string, error) { func GetWechatOfficialAccountQRCode(clientId string, clientSecret string) (string, error) {
accessToken, err := GetWechatOfficialAccountAccessToken(clientId, clientSecret) accessToken, err := GetWechatOfficialAccountAccessToken(clientId, clientSecret)
client := new(http.Client) client := new(http.Client)
params := "{\"action_name\": \"QR_LIMIT_STR_SCENE\", \"action_info\": {\"scene\": {\"scene_str\": \"test\"}}}"
weChatEndpoint := "https://api.weixin.qq.com/cgi-bin/qrcode/create"
qrCodeUrl := fmt.Sprintf("%s?access_token=%s", weChatEndpoint, accessToken)
params := `{"action_name": "QR_LIMIT_STR_SCENE", "action_info": {"scene": {"scene_str": "test"}}}`
bodyData := bytes.NewReader([]byte(params)) 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) requeset, err := http.NewRequest("POST", qrCodeUrl, bodyData)
if err != nil {
return "", err
}
resp, err := client.Do(requeset) resp, err := client.Do(requeset)
if err != nil { if err != nil {
return "", err return "", err
} }
defer resp.Body.Close()
respBytes, err := ioutil.ReadAll(resp.Body) respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return "", err return "", err

View File

@ -8,11 +8,12 @@
"favicon": "", "favicon": "",
"passwordType": "plain", "passwordType": "plain",
"passwordSalt": "", "passwordSalt": "",
"countryCodes": ["US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"], "passwordOptions": ["AtLeast6"],
"countryCodes": ["US", "GB", "ES", "FR", "DE", "CN", "JP", "KR", "VN", "ID", "SG", "IN", "IT", "MY", "TR", "DZ", "IL", "PH", "NL", "PL", "FI", "SE", "UA", "KZ"],
"defaultAvatar": "", "defaultAvatar": "",
"defaultApplication": "", "defaultApplication": "",
"tags": [], "tags": [],
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi"], "languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "it", "ms", "tr","ar", "he", "nl", "pl", "fi", "sv", "uk", "kk", "fa"],
"masterPassword": "", "masterPassword": "",
"initScore": 2000, "initScore": 2000,
"enableSoftDeletion": false, "enableSoftDeletion": false,
@ -122,7 +123,6 @@
"score": 2000, "score": 2000,
"ranking": 1, "ranking": 1,
"isAdmin": true, "isAdmin": true,
"isGlobalAdmin": true,
"isForbidden": false, "isForbidden": false,
"isDeleted": false, "isDeleted": false,
"signupApplication": "", "signupApplication": "",

View File

@ -34,7 +34,7 @@ func StartLdapServer() {
server.Handle(routes) server.Handle(routes)
err := server.ListenAndServe("0.0.0.0:" + conf.GetConfigString("ldapServerPort")) err := server.ListenAndServe("0.0.0.0:" + conf.GetConfigString("ldapServerPort"))
if err != nil { if err != nil {
return log.Printf("StartLdapServer() failed, ErrMsg = %s", err.Error())
} }
} }
@ -62,7 +62,7 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
return return
} }
if bindOrg == "built-in" || bindUser.IsGlobalAdmin { if bindOrg == "built-in" || bindUser.IsGlobalAdmin() {
m.Client.IsGlobalAdmin, m.Client.IsOrgAdmin = true, true m.Client.IsGlobalAdmin, m.Client.IsOrgAdmin = true, true
} else if bindUser.IsAdmin { } else if bindUser.IsAdmin {
m.Client.IsOrgAdmin = true m.Client.IsOrgAdmin = true

18
main.go
View File

@ -15,7 +15,6 @@
package main package main
import ( import (
"flag"
"fmt" "fmt"
"github.com/beego/beego" "github.com/beego/beego"
@ -30,17 +29,10 @@ import (
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
func getCreateDatabaseFlag() bool {
res := flag.Bool("createDatabase", false, "true if you need Casdoor to create database")
flag.Parse()
return *res
}
func main() { func main() {
createDatabase := getCreateDatabaseFlag() object.InitFlag()
object.InitAdapter() object.InitAdapter()
object.CreateTables(createDatabase) object.CreateTables()
object.DoMigration() object.DoMigration()
object.InitDb() object.InitDb()
@ -48,7 +40,9 @@ func main() {
object.InitDefaultStorageProvider() object.InitDefaultStorageProvider()
object.InitLdapAutoSynchronizer() object.InitLdapAutoSynchronizer()
proxy.InitHttpClient() proxy.InitHttpClient()
authz.InitAuthz() authz.InitApi()
object.InitUserManager()
object.InitCasvisorConfig()
util.SafeGoroutine(func() { object.RunSyncUsersJob() }) util.SafeGoroutine(func() { object.RunSyncUsersJob() })
@ -62,7 +56,7 @@ func main() {
beego.InsertFilter("*", beego.BeforeRouter, routers.StaticFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.StaticFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.CorsFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.CorsFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.AuthzFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.ApiFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage) beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)

View File

@ -12,17 +12,18 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package ai package notification
import ( import (
"github.com/casdoor/casdoor/proxy" "github.com/casdoor/notify"
"github.com/sashabaranov/go-openai" "github.com/casdoor/notify/service/bark"
) )
func getProxyClientFromToken(authToken string) *openai.Client { func NewBarkProvider(deviceKey string) (notify.Notifier, error) {
config := openai.DefaultConfig(authToken) barkSrv := bark.New(deviceKey)
config.HTTPClient = proxy.ProxyHttpClient
c := openai.NewClientWithConfig(config) notifier := notify.New()
return c notifier.UseServices(barkSrv)
return notifier, nil
} }

View File

@ -0,0 +1,73 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notification
import (
"bytes"
"context"
"fmt"
"net/http"
"github.com/casdoor/casdoor/proxy"
)
type HttpNotificationClient struct {
endpoint string
method string
paramName string
}
func NewCustomHttpProvider(endpoint string, method string, paramName string) (*HttpNotificationClient, error) {
client := &HttpNotificationClient{
endpoint: endpoint,
method: method,
paramName: paramName,
}
return client, nil
}
func (c *HttpNotificationClient) Send(ctx context.Context, subject string, content string) error {
var err error
httpClient := proxy.DefaultHttpClient
req, err := http.NewRequest(c.method, c.endpoint, bytes.NewBufferString(content))
if err != nil {
return err
}
if c.method == "POST" {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.PostForm = map[string][]string{
c.paramName: {content},
}
} else if c.method == "GET" {
q := req.URL.Query()
q.Add(c.paramName, content)
req.URL.RawQuery = q.Encode()
}
resp, err := httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("SendMessage() error, custom HTTP Notification request failed with status: %s", resp.Status)
}
return err
}

33
notification/dingtalk.go Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notification
import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/dingding"
)
func NewDingTalkProvider(token string, secret string) (notify.Notifier, error) {
cfg := dingding.Config{
Token: token,
Secret: secret,
}
dingtalkSrv := dingding.New(&cfg)
notifier := notify.New()
notifier.UseServices(dingtalkSrv)
return notifier, nil
}

37
notification/discord.go Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notification
import (
"github.com/casdoor/casdoor/proxy"
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/discord"
)
func NewDiscordProvider(token string, channelId string) (*notify.Notify, error) {
discordSrv := discord.New()
err := discordSrv.AuthenticateWithBotToken(token)
if err != nil {
return nil, err
}
discordSrv.SetHttpClient(proxy.ProxyHttpClient)
discordSrv.AddReceivers(channelId)
notifier := notify.NewWithServices(discordSrv)
return notifier, nil
}

View File

@ -0,0 +1,53 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package notification
import (
"context"
"strings"
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/googlechat"
"google.golang.org/api/chat/v1"
"google.golang.org/api/option"
)
func NewGoogleChatProvider(credentials string) (*notify.Notify, error) {
withCred := option.WithCredentialsJSON([]byte(credentials))
withSpacesScope := option.WithScopes("https://www.googleapis.com/auth/chat.spaces")
listSvc, err := chat.NewService(context.Background(), withCred, withSpacesScope)
spaces, err := listSvc.Spaces.List().Do()
if err != nil {
return nil, err
}
receivers := make([]string, 0)
for _, space := range spaces.Spaces {
name := strings.Replace(space.Name, "spaces/", "", 1)
receivers = append(receivers, name)
}
googleChatSrv, err := googlechat.New(withCred)
if err != nil {
return nil, err
}
googleChatSrv.AddReceivers(receivers...)
notifier := notify.NewWithServices(googleChatSrv)
return notifier, nil
}

View File

@ -12,17 +12,18 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package ai package notification
import "github.com/pkoukk/tiktoken-go" import (
"github.com/casdoor/notify"
"github.com/casdoor/notify/service/lark"
)
func getTokenSize(model string, prompt string) (int, error) { func NewLarkProvider(webhookURL string) (notify.Notifier, error) {
tkm, err := tiktoken.EncodingForModel(model) larkSrv := lark.NewWebhookService(webhookURL)
if err != nil {
return 0, err
}
token := tkm.Encode(prompt, nil, nil) notifier := notify.New()
res := len(token) notifier.UseServices(larkSrv)
return res, nil
return notifier, nil
} }

View File

@ -12,31 +12,21 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
//go:build !skipCi package notification
// +build !skipCi
package ai
import ( import (
"testing"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/proxy" "github.com/casdoor/casdoor/proxy"
"github.com/sashabaranov/go-openai" "github.com/casdoor/notify"
"github.com/casdoor/notify/service/line"
) )
func TestRun(t *testing.T) { func NewLineProvider(channelSecret string, accessToken string, receiver string) (*notify.Notify, error) {
object.InitConfig() lineSrv, _ := line.NewWithHttpClient(channelSecret, accessToken, proxy.ProxyHttpClient)
proxy.InitHttpClient()
text, err := queryAnswer("", "hi", 5) lineSrv.AddReceivers(receiver)
if err != nil {
panic(err)
}
println(text) notifier := notify.New()
} notifier.UseServices(lineSrv)
func TestToken(t *testing.T) { return notifier, nil
println(getTokenSize(openai.GPT3TextDavinci003, ""))
} }

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