Compare commits

...

62 Commits

Author SHA1 Message Date
UsherFall
bc8e9cfd64 feat: storage provider's domain initial value bug (#2303) 2023-09-05 14:53:32 +08:00
Yang Luo
c1eae9fcd8 Fix TotpMfa's Verify() 2023-09-04 19:21:26 +08:00
YunShu
6dae6e4954 docs: fix all dead links (#2297)
https://github.com/Selflocking/linkchecker/actions/runs/6058177987
2023-09-03 21:19:23 +08:00
YunShu
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
Yang Luo
b0aaf09ef1 Add 7 new i18n languages 2023-09-02 18:49:43 +08:00
Yang Luo
7e2f67c49a Fix i18n error 2023-09-02 18:33:19 +08:00
Yang Luo
e584a6a111 Support using "?allowEmpty=1" to bypass empty displayName check in update-user API 2023-09-02 11:59:07 +08:00
YunShu
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
Cattī Crūdēlēs
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
Yang Luo
0b859197da Fix CAS "/proxyValidate" API 2023-09-01 21:47:26 +08:00
Yang Luo
3078409343 Add CertPublicKey to Application 2023-09-01 21:16:51 +08:00
Tower He
bbf2db2e00 feat: support to use a different db schema for pg (#2281) 2023-09-01 18:02:13 +08:00
Yang Luo
0c7b911ce7 Fix enforcer edit page logic 2023-09-01 01:30:50 +08:00
Yang Luo
2cc55715ac Add app.conf existence check 2023-09-01 01:25:45 +08:00
Yang Luo
c829bf1769 Fix DummyPaymentProvider's return URL 2023-09-01 01:25:15 +08:00
Yang Luo
ec956c12ca Fix Email duplicated issue in update-user 2023-08-31 23:44:40 +08:00
Tower He
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
Yang Luo
669ac7c618 Don't encrypt user pass when user.PasswordType is non-empty when adding users 2023-08-31 17:49:36 +08:00
Yang Luo
6715efd781 Fix enforcer edit page 2023-08-31 17:32:36 +08:00
haiwu
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
Yang Luo
943cc43427 Fix payment list and product edit actions 2023-08-28 21:01:23 +08:00
Yang Luo
1e5ce7a045 Fix crash in syncUsersNoError() 2023-08-28 01:51:06 +08:00
Baihhh
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
Yang Luo
7e349c1768 feat: fix crash bug in getSteps() 2023-08-27 21:58:58 +08:00
Baihhh
b19be2df88 fix: change the id to key in syncer (#2263) 2023-08-27 20:57:27 +08:00
Yang Luo
fc3866db1c Use XORM grammar in syncer 2023-08-27 18:15:23 +08:00
Yang Luo
bf2bb31e41 Add sslMode for syncer 2023-08-27 17:07:19 +08:00
Baihhh
ec8bd6f01d feat: add tour for list pages (#2243) 2023-08-27 16:40:31 +08:00
Yang Luo
98722fd681 Fix crash in app list page for normal user 2023-08-27 11:31:48 +08:00
Yang Luo
221c55aa93 Fix yarn build cmd 2023-08-27 11:17:18 +08:00
Yang Luo
988b26b3c2 Return error for RunSyncer() 2023-08-27 02:22:37 +08:00
Yang Luo
7e3c361ce7 Add all webhook events 2023-08-26 23:50:24 +08:00
Yang Luo
a637707e77 Fix null bug in IsAdminOrSelf() 2023-08-26 10:39:46 +08:00
Yaodong Yu
7970edeaa7 feat: password and invitation code verification rules (#2258) 2023-08-25 21:16:21 +08:00
haiwu
9da2f0775f fix: fix bug in Pricing (#2255) 2023-08-25 19:27:46 +08:00
Yang Luo
739a9bcd0d feat: add CasvisorUrl 2023-08-25 11:56:12 +08:00
Yang Luo
fb0949b9ed Fix docker cannot get version bug 2023-08-25 11:49:47 +08:00
Yang Luo
27ed901167 Restrict sysinfo page to global admin 2023-08-25 11:20:11 +08:00
Yang Luo
ceab662b88 Remove dup swagger page 2023-08-25 11:09:59 +08:00
haiwu
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
Yang Luo
8073dfa88c Remove tmpFiles folder usage 2023-08-24 22:03:36 +08:00
Yang Luo
1eeeb64a0c Add checkModel() for UserGroupEnforcer 2023-08-24 18:22:23 +08:00
Yaodong Yu
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
Andrey
a0c5eb241f feat: add fields to syncer (PreferredMfaType, TotpSecret, SignupApplication) #2239 (#2245) 2023-08-23 21:40:00 +08:00
Lars Lehtonen
4d8edcc446 fix: dropped controllers err (#2244)
Signed-off-by: Lars Lehtonen <lars.lehtonen@gmail.com>
2023-08-23 21:37:51 +08:00
Yaodong Yu
2b23c04f49 fix: add SignupApplication and type for user synced from LDAP (#2240) 2023-08-21 22:52:35 +08:00
Cattī Crūdēlēs
e60ee52d91 feat: replace satori/go.uuid with google/uuid (#2238) 2023-08-21 13:58:15 +08:00
UsherFall
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
Yaodong Yu
f0e097e138 feat: fix home page (#2236)
* fix: home page

* fix: home page
2023-08-20 00:58:39 +08:00
Yang Luo
25ec1bdfa8 Fix bug in getUserOrganization() 2023-08-20 00:53:51 +08:00
Yang Luo
ea7718d7b7 Use Casvisor for records 2023-08-20 00:44:01 +08:00
Yang Luo
463fa8b636 Add ormer_session.go 2023-08-19 18:41:08 +08:00
Yang Luo
11895902f4 Move getCreateDatabaseFlag() to ormer 2023-08-19 16:44:34 +08:00
Yang Luo
15269d3315 Refactor out conf_quota.go 2023-08-19 16:39:21 +08:00
Yang Luo
4468859795 Improve sendTest msg 2023-08-19 12:47:51 +08:00
UsherFall
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
Yaodong Yu
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
Yang Luo
a07216d0e1 Improve contentType parsing in downloadImage() 2023-08-19 02:35:45 +08:00
haiwu
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
hsluoyz
a2db61cc1a chore: Revert "feat: restrict redirectUrls for CAS login" (#2234)
This reverts commit b7a37126ad.
2023-08-19 00:30:35 +08:00
Yaodong Yu
134541acde chore: put some dev dependency package to right place (#2232) 2023-08-18 22:17:16 +08:00
Yaodong Yu
59fca0342e chore: fix yarn build warning (#2231) 2023-08-18 21:25:57 +08:00
176 changed files with 10870 additions and 2738 deletions

View File

@@ -87,6 +87,7 @@ p, *, *, GET, /api/get-prometheus-info, *, *
p, *, *, *, /api/metrics, *, *
p, *, *, GET, /api/get-pricing, *, *
p, *, *, GET, /api/get-plan, *, *
p, *, *, GET, /api/get-subscription, *, *
p, *, *, GET, /api/get-organization-names, *, *
`

View File

@@ -15,7 +15,7 @@
package conf
import (
"encoding/json"
"fmt"
"os"
"runtime"
"strconv"
@@ -24,15 +24,6 @@ import (
"github.com/beego/beego"
)
type Quota struct {
Organization int `json:"organization"`
User int `json:"user"`
Application int `json:"application"`
Provider int `json:"provider"`
}
var quota = &Quota{-1, -1, -1, -1}
func init() {
// this array contains the beego configuration items that may be modified via env
presetConfigItems := []string{"httpport", "appname"}
@@ -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 {
@@ -67,7 +47,7 @@ func GetConfigString(key string) string {
if key == "staticBaseUrl" {
res = "https://cdn.casbin.org"
} 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"))
}
}
@@ -129,10 +109,6 @@ func GetConfigBatchSize() int {
return res
}
func GetConfigQuota() *Quota {
return quota
}
func GetConfigRealDataSourceName(driverName string) string {
var dataSourceName string
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

@@ -140,25 +140,28 @@ func (c *ApiController) Signup() {
username = id
}
password := authForm.Password
msg = object.CheckPasswordComplexityByOrg(organization, password)
if msg != "" {
c.ResponseError(msg)
return
}
initScore, err := organization.GetInitScore()
if err != nil {
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
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{
Owner: authForm.Organization,
Name: username,
CreatedTime: util.GetCurrentTime(),
Id: id,
Type: "normal-user",
Type: userType,
Password: authForm.Password,
DisplayName: authForm.Name,
Avatar: organization.DefaultAvatar,
@@ -171,7 +174,6 @@ func (c *ApiController) Signup() {
Region: authForm.Region,
Score: initScore,
IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false,
IsDeleted: false,
SignupApplication: application.Name,
@@ -211,7 +213,7 @@ func (c *ApiController) Signup() {
return
}
if application.HasPromptPage() {
if application.HasPromptPage() && user.Type == "normal-user" {
// The prompt page needs the user to be signed in
c.SetSessionUsername(user.GetId())
}
@@ -228,15 +230,6 @@ func (c *ApiController) Signup() {
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.Organization = application.Organization
record.User = user.Name

View File

@@ -62,13 +62,13 @@ func (c *ApiController) GetApplications() {
}
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 {
c.ResponseError(err.Error())
return
}
applications := object.GetMaskedApplications(app, userId)
applications := object.GetMaskedApplications(application, userId)
c.ResponseOk(applications, paginator.Nums())
}
}
@@ -84,13 +84,23 @@ func (c *ApiController) GetApplication() {
userId := c.GetSessionUsername()
id := c.Input().Get("id")
app, err := object.GetApplication(id)
application, err := object.GetApplication(id)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(object.GetMaskedApplication(app, userId))
if c.Input().Get("withKey") != "" && application.Cert != "" {
cert, err := object.GetCert(util.GetId(application.Owner, application.Cert))
if err != nil {
c.ResponseError(err.Error())
return
}
application.CertPublicKey = cert.Certificate
}
c.ResponseOk(object.GetMaskedApplication(application, userId))
}
// GetUserApplication
@@ -164,13 +174,13 @@ func (c *ApiController) GetOrganizationApplications() {
}
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 {
c.ResponseError(err.Error())
return
}
applications := object.GetMaskedApplications(app, userId)
applications := object.GetMaskedApplications(application, userId)
c.ResponseOk(applications, paginator.Nums())
}
}

View File

@@ -70,7 +70,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
}
// check user's tag
if !user.IsGlobalAdmin && !user.IsAdmin && len(application.Tags) > 0 {
if !user.IsGlobalAdmin() && !user.IsAdmin && len(application.Tags) > 0 {
// only users with the tag that is listed in the application tags can login
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))
@@ -78,6 +78,46 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
}
}
// 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 {
c.SetSessionUsername(userId)
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
@@ -187,11 +227,34 @@ func (c *ApiController) GetApplicationLogin() {
redirectUri := c.Input().Get("redirectUri")
scope := c.Input().Get("scope")
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())
if err != nil {
c.ResponseError(err.Error())
return
var application *object.Application
var msg string
var err error
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, "")
@@ -566,7 +629,6 @@ func (c *ApiController) Login() {
Region: userInfo.CountryCode,
Score: initScore,
IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false,
IsDeleted: false,
SignupApplication: application.Name,

View File

@@ -61,6 +61,10 @@ func (c *ApiController) IsAdminOrSelf(user2 *object.User) bool {
return true
}
if user == nil || user2 == nil {
return false
}
if user.Owner == user2.Owner && user.Name == user2.Name {
return true
}
@@ -79,7 +83,7 @@ func (c *ApiController) isGlobalAdmin() (bool, *object.User) {
return false, nil
}
return user.Owner == "built-in" || user.IsGlobalAdmin, user
return user.IsGlobalAdmin(), user
}
func (c *ApiController) getCurrentUser() *object.User {

View File

@@ -35,6 +35,11 @@ const (
UnauthorizedService string = "UNAUTHORIZED_SERVICE"
)
func queryUnescape(service string) string {
s, _ := url.QueryUnescape(service)
return s
}
func (c *RootController) CasValidate() {
ticket := c.Input().Get("ticket")
service := c.Input().Get("service")
@@ -60,24 +65,25 @@ func (c *RootController) CasServiceValidate() {
if !strings.HasPrefix(ticket, "ST") {
c.sendCasAuthenticationResponseErr(InvalidTicket, fmt.Sprintf("Ticket %s not recognized", ticket), format)
}
c.CasP3ServiceAndProxyValidate()
c.CasP3ProxyValidate()
}
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")
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.CasP3ServiceAndProxyValidate()
c.CasP3ProxyValidate()
}
func queryUnescape(service string) string {
s, _ := url.QueryUnescape(service)
return s
}
func (c *RootController) CasP3ServiceAndProxyValidate() {
func (c *RootController) CasP3ProxyValidate() {
ticket := c.Input().Get("ticket")
format := c.Input().Get("format")
service := c.Input().Get("service")
@@ -115,15 +121,17 @@ func (c *RootController) CasP3ServiceAndProxyValidate() {
pgtiou := serviceResponse.Success.ProxyGrantingTicket
// todo: check whether it is https
pgtUrlObj, err := url.Parse(pgtUrl)
if err != nil {
c.sendCasAuthenticationResponseErr(InvalidProxyCallback, err.Error(), format)
return
}
if pgtUrlObj.Scheme != "https" {
c.sendCasAuthenticationResponseErr(InvalidProxyCallback, "callback is not https", format)
return
}
// make a request to pgturl passing pgt and pgtiou
if err != nil {
c.sendCasAuthenticationResponseErr(InternalError, err.Error(), format)
return
}
param := pgtUrlObj.Query()
param.Add("pgtId", pgt)
param.Add("pgtIou", pgtiou)
@@ -263,7 +271,6 @@ func (c *RootController) sendCasAuthenticationResponseErr(code, msg, format stri
Message: msg,
},
}
if format == "json" {
c.Data["json"] = serviceResponse
c.ServeJSON()

View File

@@ -83,7 +83,7 @@ func (c *ApiController) GetEnforcer() {
return
}
if loadModelCfg == "true" {
if loadModelCfg == "true" && enforcer.Model != "" {
err := enforcer.LoadModelCfg()
if err != nil {
return

View File

@@ -1,4 +1,4 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
// 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.
@@ -23,7 +23,9 @@ import "github.com/casdoor/casdoor/object"
// @Success 200 {object} controllers.Response The Response object
// @router /get-dashboard [get]
func (c *ApiController) GetDashboard() {
data, err := object.GetDashboard()
owner := c.Input().Get("owner")
data, err := object.GetDashboard(owner)
if err != nil {
c.ResponseError(err.Error())
return

View File

@@ -45,13 +45,13 @@ func (c *ApiController) Unlink() {
// the user will be unlinked from the provider
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.
c.ResponseError(c.T("link:You are not the global admin, you can't unlink other users"))
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.
application, err := object.GetApplicationByUser(user)
if err != nil {

View File

@@ -183,8 +183,6 @@ func (c *ApiController) DeleteOrganization() {
func (c *ApiController) GetDefaultApplication() {
userId := c.GetSessionUsername()
id := c.Input().Get("id")
redirectUri := c.Input().Get("redirectUri")
typ := c.Input().Get("type")
application, err := object.GetDefaultApplication(id)
if err != nil {
@@ -192,14 +190,6 @@ func (c *ApiController) GetDefaultApplication() {
return
}
if typ == "cas" {
err = object.CheckCasRestrict(application, c.GetAcceptLanguage(), redirectUri)
if err != nil {
c.ResponseError(err.Error())
return
}
}
maskedApplication := object.GetMaskedApplication(application, userId)
c.ResponseOk(maskedApplication)
}

View File

@@ -16,6 +16,7 @@ package controllers
import (
"fmt"
"os"
"github.com/casdoor/casdoor/object"
"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))
path := util.GetUploadXlsxPath(fileId)
util.EnsureFileFolderExists(path)
defer os.Remove(path)
err = saveFile(path, &file)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.UploadPermissions(owner, fileId)
affected, err := object.UploadPermissions(owner, path)
if err != nil {
c.ResponseError(err.Error())
}

View File

@@ -16,6 +16,7 @@ package controllers
import (
"encoding/json"
"fmt"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
@@ -82,7 +83,10 @@ func (c *ApiController) GetPlan() {
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 {
options, err := object.GetPermissionsByRole(plan.Role)
if err != nil {
@@ -110,14 +114,29 @@ func (c *ApiController) GetPlan() {
// @router /update-plan [post]
func (c *ApiController) UpdatePlan() {
id := c.Input().Get("id")
owner := util.GetOwnerFromId(id)
var plan object.Plan
err := json.Unmarshal(c.Ctx.Input.RequestBody, &plan)
if err != nil {
c.ResponseError(err.Error())
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.ServeJSON()
}
@@ -136,7 +155,14 @@ func (c *ApiController) AddPlan() {
c.ResponseError(err.Error())
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.ServeJSON()
}
@@ -155,7 +181,13 @@ func (c *ApiController) DeletePlan() {
c.ResponseError(err.Error())
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.ServeJSON()
}

View File

@@ -16,6 +16,7 @@ package controllers
import (
"encoding/json"
"fmt"
"github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/object"
@@ -80,7 +81,10 @@ func (c *ApiController) GetPricing() {
c.ResponseError(err.Error())
return
}
if pricing == nil {
c.ResponseError(fmt.Sprintf(c.T("pricing:The pricing: %s does not exist"), id))
return
}
c.ResponseOk(pricing)
}

View File

@@ -161,10 +161,17 @@ func (c *ApiController) DeleteProduct() {
// @router /buy-product [post]
func (c *ApiController) BuyProduct() {
id := c.Input().Get("id")
providerName := c.Input().Get("providerName")
host := c.Ctx.Request.Host
userId := c.GetSessionUsername()
providerName := c.Input().Get("providerName")
// 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 == "" {
c.ResponseError(c.T("general:Please login first"))
return
@@ -175,13 +182,12 @@ func (c *ApiController) BuyProduct() {
c.ResponseError(err.Error())
return
}
if user == nil {
c.ResponseError(fmt.Sprintf(c.T("general:The user: %s doesn't exist"), userId))
return
}
payUrl, orderId, err := object.BuyProduct(id, providerName, user, host)
payUrl, orderId, err := object.BuyProduct(id, user, providerName, pricingName, planName, host)
if err != nil {
c.ResponseError(err.Error())
return

View File

@@ -1,121 +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")
organizationName := c.Input().Get("organizationName")
if limit == "" || page == "" {
records, err := object.GetRecords()
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(records)
} else {
limit := util.ParseInt(limit)
if c.IsGlobalAdmin() && organizationName != "" {
organization = organizationName
}
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 {
c.ResponseError(err.Error())
return
}
records, err := object.GetRecordsByField(record)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(records)
}
// 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

@@ -16,6 +16,7 @@ package controllers
import (
"fmt"
"os"
"github.com/casdoor/casdoor/object"
"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))
path := util.GetUploadXlsxPath(fileId)
util.EnsureFileFolderExists(path)
defer os.Remove(path)
err = saveFile(path, &file)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.UploadRoles(owner, fileId)
affected, err := object.UploadRoles(owner, path)
if err != nil {
c.ResponseError(err.Error())
}

View File

@@ -40,6 +40,10 @@ type SmsForm struct {
OrgId string `json:"organizationId"` // e.g. "admin/built-in"
}
type NotificationForm struct {
Content string `json:"content"`
}
// SendEmail
// @Title SendEmail
// @Tag Service API
@@ -156,3 +160,33 @@ func (c *ApiController) SendSms() {
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

@@ -160,7 +160,11 @@ func (c *ApiController) RunSyncer() {
return
}
object.RunSyncer(syncer)
err = object.RunSyncer(syncer)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk()
}

View File

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

View File

@@ -258,6 +258,13 @@ func (c *ApiController) UpdateUser() {
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 != "" {
c.ResponseError(msg)
return
@@ -441,6 +448,10 @@ func (c *ApiController) SetPassword() {
}
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 {
c.ResponseError(err.Error())
return

View File

@@ -48,17 +48,17 @@ func (c *ApiController) UploadUsers() {
c.ResponseError(err.Error())
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)
util.EnsureFileFolderExists(path)
defer os.Remove(path)
err = saveFile(path, &file)
if err != nil {
c.ResponseError(err.Error())
return
}
affected, err := object.UploadUsers(owner, fileId)
affected, err := object.UploadUsers(owner, path)
if err != nil {
c.ResponseError(err.Error())
return

View File

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

12
go.mod
View File

@@ -16,6 +16,7 @@ require (
github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/oss v1.3.0
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/denisenkom/go-mssqldb v0.9.0
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
@@ -26,16 +27,17 @@ require (
github.com/go-mysql-org/go-mysql v1.7.0
github.com/go-pay/gopay v1.5.72
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/golang-jwt/jwt/v4 v4.5.0
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/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/markbates/goth v1.75.2
github.com/mitchellh/mapstructure v1.5.0
github.com/nikoksr/notify v0.41.0
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
github.com/nyaruka/phonenumbers v1.1.5
github.com/pquerna/otp v1.4.0
@@ -45,13 +47,12 @@ require (
github.com/robfig/cron/v3 v3.0.1
github.com/russellhaering/gosaml2 v0.9.0
github.com/russellhaering/goxmldsig v1.2.0
github.com/satori/go.uuid v1.2.0
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stretchr/testify v1.8.3
github.com/stripe/stripe-go/v74 v74.29.0 // indirect
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/thanhpk/randstr v1.0.4
github.com/tklauser/go-sysconf v0.3.10 // indirect
@@ -64,6 +65,5 @@ require (
golang.org/x/oauth2 v0.10.0
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v2 v2.4.0 // indirect
modernc.org/sqlite v1.18.2
)

173
go.sum
View File

@@ -783,6 +783,8 @@ github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzS
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
@@ -796,6 +798,8 @@ github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0A
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b h1:EgJ6N2S0h1WfFIjU5/VVHWbMSVYXAluop97Qxpr/lfQ=
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b/go.mod h1:3SAoF0F5EbcOuBD5WT9nYkbIJieBS84cUQXADbXeBsU=
github.com/RocketChat/Rocket.Chat.Go.SDK v0.0.0-20221121042443-a3fd332d56d9/go.mod h1:rjP7sIipbZcagro/6TCk6X0ZeFT2eyudH5+fve/cbBA=
github.com/SherClockHolmes/webpush-go v1.2.0/go.mod h1:w6X47YApe/B9wUz2Wh8xukxlyupaxSSEbu6yKJcHN2w=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
@@ -810,7 +814,9 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387 h1:loy0fjI90vF44BPW4ZYOkE3tDkGTy7yHURusOJimt+I=
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387/go.mod h1:GuR5j/NW7AU7tDAQUDGCtpiPxWIOy/c3kiRDnlwiCHc=
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
github.com/alicebob/miniredis/v2 v2.30.0/go.mod h1:84TWKZlxYkfgMucPBf5SOQBYJceZeQRFIaQgNMiCX6Q=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1075/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
github.com/aliyun/alibaba-cloud-sdk-go v1.62.188 h1:8lIVcOlHW+fKCGMEf6nuGTTEFOt/EZJPZ8oq0kTlhGk=
github.com/aliyun/alibaba-cloud-sdk-go v1.62.188/go.mod h1:Api2AkmMgGaSUAhmk76oaFObkoeCPc/bKAqcyplPODs=
@@ -824,13 +830,29 @@ github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0I
github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI=
github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg=
github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
github.com/appleboy/go-fcm v0.1.5/go.mod h1:MSxZ4LqGRsnywOjnlXJXMqbjZrG4vf+0oHitfC9HRH0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atc0005/go-teams-notify/v2 v2.6.1/go.mod h1:xo6GejLDHn3tWBA181F8LrllIL0xC1uRsRxq7YNXaaY=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.37.1/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.44.4 h1:ePN0CVJMdiz2vYUcJH96eyxRrtKGSDMgyhP6rah2OgE=
github.com/aws/aws-sdk-go v1.44.4/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw=
github.com/aws/aws-sdk-go-v2/config v1.18.27/go.mod h1:0My+YgmkGxeqjXZb5BYme5pc4drjTnM+x1GJ3zv42Nw=
github.com/aws/aws-sdk-go-v2/credentials v1.13.26/go.mod h1:GoXt2YC8jHUBbA4jr+W3JiemnIbkXOfxSXcisUsZ3os=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4/go.mod h1:E1hLXN/BL2e6YizK1zFlYd8vsfi2GTjbjBazinMmeaM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34/go.mod h1:wZpTEecJe0Btj3IYnDx/VlUzor9wm3fJHyvLpQF0VwY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkfIEXb4k52I7swUnZP0wohVajJMRn3vsUw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35/go.mod h1:0Eg1YjxE0Bhn56lx+SHJwCzhW+2JGtizsrx+lCqrfm0=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj7znCIg05jXlaGBlFMGP8+7UN3VtCkRBG2spnmRQkU=
github.com/aws/aws-sdk-go-v2/service/ses v1.15.11/go.mod h1:iI3tHe16kyXcYA6fcoct3hW1uB/XSoe46w29wupWwho=
github.com/aws/aws-sdk-go-v2/service/sns v1.20.13/go.mod h1:rWrvp9i8y/lX94lS7Kn/0iu9RY6vXzeKRqS/knVX8/c=
github.com/aws/aws-sdk-go-v2/service/sso v1.12.12/go.mod h1:HuCOxYsF21eKrerARYO6HapNeh9GBNq7fius2AcwodY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12/go.mod h1:E4VrHCPzmVB/KFXtqBGKb3c8zpbNBgKe3fisDNLAW5w=
github.com/aws/aws-sdk-go-v2/service/sts v1.19.2/go.mod h1:dp0yLPsLBOi++WTxzCjA/oZqi6NPIhoR+uF7GeMU9eg=
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
github.com/baidubce/bce-sdk-go v0.9.153 h1:h5l2EXehe4C4/bdlAPBaULrbnEDgIu5HOYgniN7bjGM=
github.com/baidubce/bce-sdk-go v0.9.153/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg=
github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
@@ -847,12 +869,15 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blinkbean/dingtalk v0.0.0-20210905093040-7d935c0f7e19/go.mod h1:9BaLuGSBqY3vT5hstValh48DbsKO7vaHaJnG9pXwbto=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
github.com/casbin/casbin v1.9.1 h1:ucjbS5zTrmSLtH4XogqOG920Poe6QatdXtz1FEbApeM=
github.com/casbin/casbin v1.9.1/go.mod h1:z8uPsfBJGUsnkagrt3G8QvjgTKFMBJ32UP8HpZllfog=
@@ -868,12 +893,16 @@ github.com/casdoor/oss v1.3.0 h1:D5pcz65tJRqJrWY11Ks7D9LUsmlhqqMHugjDhSxWTvk=
github.com/casdoor/oss v1.3.0/go.mod h1:YOi6KpG1pZHTkiy9AYaqI0UaPfE7YkaA07d89f1idqY=
github.com/casdoor/xorm-adapter/v3 v3.0.4 h1:vB04Ao8n2jA7aFBI9F+gGXo9+Aa1IQP6mTdo50913DM=
github.com/casdoor/xorm-adapter/v3 v3.0.4/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
github.com/casvisor/casvisor-go-sdk v1.0.3 h1:TKJQWKnhtznEBhzLPEdNsp7nJK2GgdD8JsB0lFPMW7U=
github.com/casvisor/casvisor-go-sdk v1.0.3/go.mod h1:frnNtH5GA0wxzAQLyZxxfL0RSsSub9GQPi2Ybe86ocE=
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -902,6 +931,8 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/couchbase/go-couchbase v0.0.0-20201216133707-c04035124b17/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A=
github.com/couchbase/gomemcached v0.1.2-0.20201224031647-c432ccf49f32/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo=
@@ -909,6 +940,7 @@ github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401/go.mod h1:BQwMFl
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cschomburg/go-pushbullet v0.0.0-20171206132031-67759df45fbb/go.mod h1:RfQ9wji3fjcSEsQ+uFCtIh3+BXgcZum8Kt3JxvzYzlk=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ=
@@ -918,14 +950,19 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f h1:q/DpyjJjZs94bziQ7YkBmIlpqbVP7yw179rnzoNVX1M=
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f/go.mod h1:QGrK8vMWWHQYQ3QU9bw9Y9OPNfxccGzfb41qjvVeXtY=
github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
github.com/denisenkom/go-mssqldb v0.9.0 h1:RSohk2RsiZqLZ0zCjtfn3S4Gp4exhpBWHyQ7D0yGjAk=
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dghubble/oauth1 v0.7.2/go.mod h1:9erQdIhqhOHG/7K9s/tgh9Ks/AfoyrO5mW/43Lu2+kE=
github.com/dghubble/sling v1.4.0/go.mod h1:0r40aNsU9EdDUVBNhfCstAtFgutjgJGYbO1oNzkMoM8=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/drswork/go-twitter v0.0.0-20221107160839-dea1b6ed53d7/go.mod h1:ncTaGuXc5v7AuiVekeJ0Nwh8Bf4cudukoj0qM/15UZE=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
@@ -953,6 +990,10 @@ github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
@@ -960,7 +1001,10 @@ github.com/forestmgy/ldapserver v1.1.0 h1:gvil4nuLhqPEL8SugCkFhRyA0/lIvRdwZSqlrw
github.com/forestmgy/ldapserver v1.1.0/go.mod h1:1RZ8lox1QSY7rmbjdmy+sYQXY4Lp7SpGzpdE3+j3IyM=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88=
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -989,6 +1033,7 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-lark/lark v1.7.4/go.mod h1:6ltbSztPZRT6IaO9ZIQyVaY5pVp/KeMizDYtfZkU+vM=
github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ=
@@ -1011,11 +1056,18 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiRJrJ3iKAgqqH0Bl/Ocjdk=
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc/go.mod h1:25mL1NKxbJhB63ihiK8MnNeTRd+xAizd6bOdydrTLUQ=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible h1:2cauKuaELYAEARXRkq2LrJ0yDDv1rW7+wrTEdVL3uaU=
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-webauthn/revoke v0.1.6 h1:3tv+itza9WpX5tryRQx4GwxCCBrCIiJ8GIkOhxiAmmU=
github.com/go-webauthn/revoke v0.1.6/go.mod h1:TB4wuW4tPlwgF3znujA96F70/YSQXHPPWl7vgY09Iy8=
github.com/go-webauthn/webauthn v0.6.0 h1:uLInMApSvBfP+vEFasNE0rnVPG++fjp7lmAIvNhe+UU=
@@ -1023,6 +1075,8 @@ github.com/go-webauthn/webauthn v0.6.0/go.mod h1:7edMRZXwuM6JIVjN68G24Bzt+bPCvTm
github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
@@ -1032,6 +1086,7 @@ github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0-rc.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
@@ -1101,6 +1156,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI=
github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo=
@@ -1128,6 +1185,7 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
@@ -1136,6 +1194,7 @@ github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JV
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/s2a-go v0.1.4 h1:1kZ/sQM3srePvKs3tXAvQzo66XfcReoqFpIpIccE7Oc=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/uuid v0.0.0-20171113160352-8c31c18f31ed/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
@@ -1166,35 +1225,40 @@ github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56
github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1 h1:LqbZZ9sNMWVjeXS4NN5oVvhMjDyLhmA1LG86oSo+IqY=
github.com/gorilla/pat v0.0.0-20180118222023-199c85a7f6d1/go.mod h1:YeAe0gNeiNT5hoiZRI4yiOky6jVdNvfO2N6Kav/HmxY=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gregdel/pushover v1.2.0/go.mod h1:EcaO66Nn1StkpEm1iKtBTV3d2A16SoMsVER1PthX7to=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk=
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
@@ -1209,15 +1273,19 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@@ -1229,8 +1297,12 @@ github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uia
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kevinburke/go-types v0.0.0-20210723172823-2deba1f80ba7/go.mod h1:/Pk5i/SqYdYv1cie5wGwoZ4P6TpgMi+Yf58mtJSHdOw=
github.com/kevinburke/handlers v0.0.0-20220416175136-cbf86af60bb5/go.mod h1:wOuHsUtSfRb2irqcjH8V3/hrPZq9wKy+NxX+hbfY0uI=
github.com/kevinburke/rest v0.0.0-20210506044642-5611499aa33c/go.mod h1:pD+iEcdAGVXld5foVN4e24zb/6fnb60tgZPZ3P/3T/I=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/twilio-go v0.0.0-20221122012537-65f3dd7539e2/go.mod h1:PDdDH7RSKjjy9iFyoMzfeChOSmXpXuMEUqmAJSihxx4=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
@@ -1271,14 +1343,16 @@ github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/line/line-bot-sdk-go v7.8.0+incompatible/go.mod h1:0RjLjJEAU/3GIcHkC3av6O4jInAbt25nnZVmOFUgDBg=
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 h1:wIONC+HMNRqmWBjuMxhatuSzHaljStc4gjDeKycxy0A=
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3/go.mod h1:37YR9jabpiIxsb8X9VCIx8qFOjTDIIrIHHODa8C4gz0=
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailgun/mailgun-go/v4 v4.9.1/go.mod h1:FJlF9rI5cQT+mrwujtJjPMbIVy3Ebor9bKTVsJ0QU40=
github.com/markbates/going v1.0.0 h1:DQw0ZP7NbNlFGcKbcE/IVSOAFzScxRtLpd0rLMzLhq0=
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
github.com/markbates/goth v1.75.2 h1:C7KloBMMk50JyXaHhzfqWYLW6+bDcSVIvUGHXneLWro=
@@ -1287,9 +1361,11 @@ github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@@ -1297,10 +1373,12 @@ github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsO
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mileusna/viber v1.0.1/go.mod h1:Pxu/iPMnYjnHgu+bEp3SiKWHWmlf/kDp/yOX8XUdYrQ=
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY=
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -1312,25 +1390,43 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA=
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nikoksr/notify v0.41.0 h1:4LGE41GpWdHX5M3Xo6DlWRwS2WLDbOq1Rk7IzY4vjmQ=
github.com/nikoksr/notify v0.41.0/go.mod h1:FoE0UVPeopz1Vy5nm9vQZ+JVmYjEIjQgbFstbkw+cRE=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/nyaruka/phonenumbers v1.1.5 h1:vYy2DI+z5hdaemqVzXYJ4CVyK92IG484CirEY+40GTo=
github.com/nyaruka/phonenumbers v1.1.5/go.mod h1:yShPJHDSH3aTKzCbXyVxNpbl2kA+F+Ne5Pun/MvFRos=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@@ -1353,11 +1449,13 @@ github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20161029093637-248dadf4e906/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/plivo/plivo-go/v7 v7.31.0/go.mod h1:BohwA5tHmqajNPtohh55VIIF0AZA88vapm4jv00MFOE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
@@ -1404,8 +1502,12 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU=
github.com/russellhaering/gosaml2 v0.9.0 h1:CNMnH42z/GirrKjdmNrSS6bAAs47F9bPdl4PfRmVOIk=
github.com/russellhaering/gosaml2 v0.9.0/go.mod h1:byViER/1YPUa0Puj9ROZblpoq2jsE7h/CJmitzX0geU=
github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg=
@@ -1416,6 +1518,8 @@ github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfF
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
github.com/sendgrid/sendgrid-go v3.12.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
@@ -1434,25 +1538,31 @@ github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed h1:KMgQoLJGCq1Io
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed/go.mod h1:yFdBgwXP24JziuRl2NMUahT7nGLNOKi1SIiFxMttVD4=
github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
github.com/silenceper/wechat/v2 v2.1.5/go.mod h1:7Iu3EhQYVtDUJAj+ZVRy8yom75ga7aDWv8RurLkVm0s=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=
github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
@@ -1474,8 +1584,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stripe/stripe-go/v74 v74.29.0 h1:ffJ+1Ta1Ccg7yDDz+SfjixX0KizEEJ/wNVRoFYkdwFY=
github.com/stripe/stripe-go/v74 v74.29.0/go.mod h1:f9L6LvaXa35ja7eyvP6GQswoaIPaBRvGAimAO+udbBw=
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
@@ -1483,17 +1594,28 @@ github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFd
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.639 h1:Ubmf093flFkCvCUuVwFo+jgBdDh7RXZQ4pGcenLFP6M=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.639/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.639 h1:98cUzYW9tzSvY4weN/Ke2ZJfX5e/5/Tb6Z259cSgHZw=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/sms v1.0.639/go.mod h1:JX7a56foe9SsC6mCYOPGFx4YdRLBP/PJWQFuZQuhCt4=
github.com/textmagic/textmagic-rest-go-v2/v2 v2.0.4420/go.mod h1:PbP69y7uRiNdwtPE3/bVGDaPYA1sr4vuHaGLQAGzeW8=
github.com/thanhpk/randstr v1.0.4 h1:IN78qu/bR+My+gHCvMEXhR/i5oriVHcTB/BJJIRTsNo=
github.com/thanhpk/randstr v1.0.4/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U=
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2/go.mod h1:4kyMkleCiLkgY6z8gK5BkI01ChBtxR0ro3I1ZDcGM3w=
github.com/ttacon/libphonenumber v1.2.1/go.mod h1:E0TpmdVMq5dyVlQ7oenAkhsLu86OkUl+yR4OAxyEg/M=
github.com/twilio/twilio-go v0.26.0 h1:wFW4oTe3/LKt6bvByP7eio8JsjtaLHjMQKOUEzQry7U=
github.com/twilio/twilio-go v0.26.0/go.mod h1:lz62Hopu4vicpQ056H5TJ0JE4AP0rS3sQ35/ejmgOwE=
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
@@ -1505,6 +1627,8 @@ github.com/ucloud/ucloud-sdk-go v0.22.4/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzF
github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/utahta/go-linenotify v0.5.0/go.mod h1:KsvBXil2wx+ByaCR0e+IZKTbp4pDesc7yjzRigLf6pE=
github.com/vartanbeno/go-reddit/v2 v2.0.1/go.mod h1:758/S10hwZSLm43NPtwoNQdZFSg3sjB5745Mwjb0ANI=
github.com/volcengine/volc-sdk-golang v1.0.19 h1:jJp+aJgK0e//rZ9I0K2Y7ufJwvuZRo/AQsYDynXMNgA=
github.com/volcengine/volc-sdk-golang v1.0.19/go.mod h1:+GGi447k4p1I5PNdbpG2GLaF0Ui9vIInTojMM0IfSS4=
github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
@@ -1527,13 +1651,16 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.mau.fi/zeroconfig v0.1.2/go.mod h1:NcSJkf180JT+1IId76PcMuLTNa1CzsFFZ0nBygIQM70=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -1564,6 +1691,7 @@ go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -1611,6 +1739,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -1658,6 +1787,7 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20171115151908-9dfe39835686/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1686,10 +1816,12 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200927032502-5d4f70055728/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@@ -1700,6 +1832,7 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -1712,6 +1845,7 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220614195744-fb05da6f9022/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
@@ -1729,6 +1863,7 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY=
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
@@ -1764,6 +1899,7 @@ golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
golang.org/x/sync v0.0.0-20171101214715-fd80eb99c8f6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1789,6 +1925,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1798,8 +1935,10 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1818,6 +1957,7 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1828,6 +1968,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1851,6 +1992,7 @@ golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1981,6 +2123,7 @@ golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
@@ -2330,15 +2473,16 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
@@ -2372,6 +2516,9 @@ honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
maunium.net/go/mautrix v0.15.3/go.mod h1:zLrQqdxJlLkurRCozTc9CL6FySkgZlO/kpCYxBILSLE=
modernc.org/cc/v3 v3.31.5-0.20210308123301-7a3e9dab9009/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=

View File

@@ -38,7 +38,13 @@ func TestGenerateI18nFrontend(t *testing.T) {
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) {
@@ -60,5 +66,11 @@ func TestGenerateI18nBackend(t *testing.T) {
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/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/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"
}
}

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

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

@@ -72,13 +72,13 @@ type FacebookCheckToken struct {
}
// 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 {
UserId string `json:"user_id"`
}
// 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) {
params := url.Values{}
params.Add("client_id", idp.Config.ClientID)

View File

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

View File

@@ -62,7 +62,7 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
return
}
if bindOrg == "built-in" || bindUser.IsGlobalAdmin {
if bindOrg == "built-in" || bindUser.IsGlobalAdmin() {
m.Client.IsGlobalAdmin, m.Client.IsOrgAdmin = true, true
} else if bindUser.IsAdmin {
m.Client.IsOrgAdmin = true

15
main.go
View File

@@ -15,7 +15,6 @@
package main
import (
"flag"
"fmt"
"github.com/beego/beego"
@@ -30,17 +29,10 @@ import (
"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() {
createDatabase := getCreateDatabaseFlag()
object.InitAdapter(createDatabase)
object.CreateTables(createDatabase)
object.InitFlag()
object.InitAdapter()
object.CreateTables()
object.DoMigration()
object.InitDb()
@@ -50,6 +42,7 @@ func main() {
proxy.InitHttpClient()
authz.InitApi()
object.InitUserManager()
object.InitCasvisorConfig()
util.SafeGoroutine(func() { object.RunSyncUsersJob() })

View File

@@ -12,37 +12,35 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package object
package notification
import (
"bytes"
"context"
"fmt"
"net/http"
"github.com/casdoor/casdoor/proxy"
)
type HttpSmsClient struct {
type HttpNotificationClient struct {
endpoint string
method string
paramName string
text string
}
func newHttpSmsClient(endpoint string, method string, paramName string, text string) (*HttpSmsClient, error) {
client := &HttpSmsClient{
func NewCustomHttpProvider(endpoint string, method string, paramName string) (*HttpNotificationClient, error) {
client := &HttpNotificationClient{
endpoint: endpoint,
method: method,
paramName: paramName,
text: text,
}
return client, nil
}
func (c *HttpSmsClient) SendMessage(param map[string]string, targetPhoneNumber ...string) error {
func (c *HttpNotificationClient) Send(ctx context.Context, subject string, content string) error {
var err error
content := param["code"]
httpClient := proxy.DefaultHttpClient
req, err := http.NewRequest(c.method, c.endpoint, bytes.NewBufferString(content))
@@ -68,7 +66,7 @@ func (c *HttpSmsClient) SendMessage(param map[string]string, targetPhoneNumber .
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("SendMessage() error, custom HTTP SMS request failed with status: %s", resp.Status)
return fmt.Errorf("SendMessage() error, custom HTTP Notification request failed with status: %s", resp.Status)
}
return err

27
notification/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 notification
import "github.com/nikoksr/notify"
func GetNotificationProvider(typ string, appId string, receiver string, method string, title string) (notify.Notifier, error) {
if typ == "Telegram" {
return NewTelegramProvider(appId, receiver)
} else if typ == "Custom HTTP" {
return NewCustomHttpProvider(receiver, method, title)
}
return nil, nil
}

42
notification/telegram.go Normal file
View File

@@ -0,0 +1,42 @@
// 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 (
"strconv"
"github.com/casdoor/casdoor/proxy"
api "github.com/go-telegram-bot-api/telegram-bot-api"
"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/telegram"
)
func NewTelegramProvider(apiToken string, chatIdStr string) (notify.Notifier, error) {
client, err := api.NewBotAPIWithClient(apiToken, proxy.ProxyHttpClient)
if err != nil {
return nil, err
}
t := &telegram.Telegram{}
t.SetClient(client)
chatId, err := strconv.ParseInt(chatIdStr, 10, 64)
if err != nil {
return nil, err
}
t.AddReceivers(chatId)
return t, nil
}

View File

@@ -151,7 +151,7 @@ func (adapter *Adapter) InitAdapter() error {
if adapter.Adapter == nil {
var dataSourceName string
if adapter.builtInAdapter() {
if adapter.isBuiltIn() {
dataSourceName = conf.GetConfigString("dataSourceName")
if adapter.DatabaseType == "mysql" {
dataSourceName = dataSourceName + adapter.Database
@@ -183,6 +183,14 @@ func (adapter *Adapter) InitAdapter() error {
var err error
engine, err := xorm.NewEngine(adapter.DatabaseType, dataSourceName)
if adapter.isBuiltIn() && adapter.DatabaseType == "postgres" {
schema := util.GetValueFromDataSourceName("search_path", dataSourceName)
if schema != "" {
engine.SetSchema(schema)
}
}
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(engine, adapter.getTable(), adapter.TableNamePrefix)
if err != nil {
return err
@@ -211,7 +219,7 @@ func adapterChangeTrigger(oldName string, newName string) error {
return session.Commit()
}
func (adapter *Adapter) builtInAdapter() bool {
func (adapter *Adapter) isBuiltIn() bool {
if adapter.Owner != "built-in" {
return false
}

View File

@@ -57,7 +57,9 @@ type Application struct {
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
CertPublicKey string `xorm:"-" json:"certPublicKey"`
Tags []string `xorm:"mediumtext" json:"tags"`
InvitationCodes []string `xorm:"varchar(200)" json:"invitationCodes"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
@@ -311,6 +313,11 @@ func GetMaskedApplication(application *Application, userId string) *Application
application.OrganizationObj.PasswordSalt = "***"
}
}
if application.InvitationCodes != nil {
application.InvitationCodes = []string{"***"}
}
return application
}

View File

@@ -66,8 +66,11 @@ func CheckUserSignup(application *Application, organization *Organization, form
}
}
if len(form.Password) <= 5 {
return i18n.Translate(lang, "check:Password must have at least 6 characters")
if application.IsSignupItemVisible("Password") {
msg := CheckPasswordComplexityByOrg(organization, form.Password)
if msg != "" {
return msg
}
}
if application.IsSignupItemVisible("Email") {
@@ -124,6 +127,18 @@ func CheckUserSignup(application *Application, organization *Organization, form
}
}
if len(application.InvitationCodes) > 0 {
if form.InvitationCode == "" {
if application.IsSignupItemRequired("Invitation code") {
return i18n.Translate(lang, "check:Invitation code cannot be blank")
}
} else {
if !util.InSlice(application.InvitationCodes, form.InvitationCode) {
return i18n.Translate(lang, "check:Invitation code is invalid")
}
}
}
return ""
}
@@ -141,7 +156,7 @@ func checkSigninErrorTimes(user *User, lang string) string {
// reset the error times
user.SigninWrongTimes = 0
UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, user.IsGlobalAdmin)
UpdateUser(user.GetId(), user, []string{"signin_wrong_times"}, false)
}
return ""
@@ -319,7 +334,7 @@ func CheckUserPermission(requestUserId, userId string, strict bool, lang string)
if requestUser == nil {
return false, fmt.Errorf(i18n.Translate(lang, "check:Session outdated, please login again"))
}
if requestUser.IsGlobalAdmin {
if requestUser.IsGlobalAdmin() {
hasPermission = true
} else if requestUserId == userId {
hasPermission = true
@@ -391,10 +406,6 @@ func CheckUsername(username string, lang string) string {
}
func CheckUpdateUser(oldUser, user *User, lang string) string {
if user.DisplayName == "" {
return i18n.Translate(lang, "user:Display name cannot be empty")
}
if oldUser.Name != user.Name {
if msg := CheckUsername(user.Name, lang); msg != "" {
return msg
@@ -404,7 +415,7 @@ func CheckUpdateUser(oldUser, user *User, lang string) string {
}
}
if oldUser.Email != user.Email {
if HasUserByField(user.Name, "email", user.Email) {
if HasUserByField(user.Owner, "email", user.Email) {
return i18n.Translate(lang, "check:Email already exists")
}
}

View File

@@ -42,7 +42,7 @@ func resetUserSigninErrorTimes(user *User) {
return
}
user.SigninWrongTimes = 0
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin)
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
}
func recordSigninErrorInfo(user *User, lang string, options ...bool) string {
@@ -61,7 +61,7 @@ func recordSigninErrorInfo(user *User, lang string, options ...bool) string {
}
// update user
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin)
UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, false)
leftChances := SigninWrongTimesLimit - user.SigninWrongTimes
if leftChances == 0 && enableCaptcha {
return fmt.Sprint(i18n.Translate(lang, "check:password or code is incorrect"))

View File

@@ -27,7 +27,7 @@ type Dashboard struct {
SubscriptionCounts []int `json:"subscriptionCounts"`
}
func GetDashboard() (*Dashboard, error) {
func GetDashboard(owner string) (*Dashboard, error) {
dashboard := &Dashboard{
OrganizationCounts: make([]int, 31),
UserCounts: make([]int, 31),
@@ -47,7 +47,7 @@ func GetDashboard() (*Dashboard, error) {
wg.Add(5)
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&organizations); err != nil {
if err := ormer.Engine.Find(&organizations, &Organization{Owner: owner}); err != nil {
panic(err)
}
}()
@@ -55,7 +55,7 @@ func GetDashboard() (*Dashboard, error) {
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&users); err != nil {
if err := ormer.Engine.Find(&users, &User{Owner: owner}); err != nil {
panic(err)
}
}()
@@ -63,7 +63,7 @@ func GetDashboard() (*Dashboard, error) {
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&providers); err != nil {
if err := ormer.Engine.Find(&providers, &Provider{Owner: owner}); err != nil {
panic(err)
}
}()
@@ -71,7 +71,7 @@ func GetDashboard() (*Dashboard, error) {
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&applications); err != nil {
if err := ormer.Engine.Find(&applications, &Application{Owner: owner}); err != nil {
panic(err)
}
}()
@@ -79,7 +79,7 @@ func GetDashboard() (*Dashboard, error) {
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&subscriptions); err != nil {
if err := ormer.Engine.Find(&subscriptions, &Subscription{Owner: owner}); err != nil {
panic(err)
}
}()

View File

@@ -73,7 +73,6 @@ func getBuiltInAccountItems() []*AccountItem {
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is global admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is forbidden", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is deleted", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
@@ -145,7 +144,6 @@ func initBuiltInUser() {
Score: 2000,
Ranking: 1,
IsAdmin: true,
IsGlobalAdmin: true,
IsForbidden: false,
IsDeleted: false,
SignupApplication: "app-built-in",

View File

@@ -311,18 +311,20 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
}
newUser := &User{
Owner: owner,
Name: name,
CreatedTime: util.GetCurrentTime(),
DisplayName: syncUser.buildLdapDisplayName(),
Avatar: organization.DefaultAvatar,
Email: syncUser.Email,
Phone: syncUser.Mobile,
Address: []string{syncUser.Address},
Affiliation: affiliation,
Tag: tag,
Score: score,
Ldap: syncUser.Uuid,
Owner: owner,
Name: name,
CreatedTime: util.GetCurrentTime(),
DisplayName: syncUser.buildLdapDisplayName(),
SignupApplication: organization.DefaultApplication,
Type: "normal-user",
Avatar: organization.DefaultAvatar,
Email: syncUser.Email,
Phone: syncUser.Mobile,
Address: []string{syncUser.Address},
Affiliation: affiliation,
Tag: tag,
Score: score,
Ldap: syncUser.Uuid,
}
affected, err := AddUser(newUser)

View File

@@ -84,7 +84,7 @@ func MfaRecover(user *User, recoveryCode string) error {
return fmt.Errorf("recovery code not found")
}
_, err := UpdateUser(user.GetId(), user, []string{"recovery_codes"}, user.IsAdminUser())
_, err := UpdateUser(user.GetId(), user, []string{"recovery_codes"}, false)
if err != nil {
return err
}
@@ -181,7 +181,7 @@ func DisabledMultiFactorAuth(user *User) error {
func SetPreferredMultiFactorAuth(user *User, mfaType string) error {
user.PreferredMfaType = mfaType
_, err := UpdateUser(user.GetId(), user, []string{"preferred_mfa_type"}, user.IsAdminUser())
_, err := UpdateUser(user.GetId(), user, []string{"preferred_mfa_type"}, false)
if err != nil {
return err
}

View File

@@ -81,12 +81,15 @@ func (mfa *TotpMfa) SetupVerify(ctx *context.Context, passcode string) error {
return errors.New("totp secret is missing")
}
result, _ := totp.ValidateCustom(passcode, secret.(string), time.Now().UTC(), totp.ValidateOpts{
result, err := totp.ValidateCustom(passcode, secret.(string), time.Now().UTC(), totp.ValidateOpts{
Period: MfaTotpPeriodInSeconds,
Skew: 1,
Digits: otp.DigitsSix,
Algorithm: otp.AlgorithmSHA1,
})
if err != nil {
return err
}
if result {
return nil
@@ -125,7 +128,15 @@ func (mfa *TotpMfa) Enable(ctx *context.Context, user *User) error {
}
func (mfa *TotpMfa) Verify(passcode string) error {
result := totp.Validate(passcode, mfa.Config.Secret)
result, err := totp.ValidateCustom(passcode, mfa.Config.Secret, time.Now().UTC(), totp.ValidateOpts{
Period: MfaTotpPeriodInSeconds,
Skew: 1,
Digits: otp.DigitsSix,
Algorithm: otp.AlgorithmSHA1,
})
if err != nil {
return err
}
if result {
return nil

42
object/notification.go Normal file
View File

@@ -0,0 +1,42 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"context"
"github.com/casdoor/casdoor/notification"
"github.com/nikoksr/notify"
)
func getNotificationClient(provider *Provider) (notify.Notifier, error) {
var client notify.Notifier
client, err := notification.GetNotificationProvider(provider.Type, provider.AppId, provider.Receiver, provider.Method, provider.Title)
if err != nil {
return nil, err
}
return client, nil
}
func SendNotification(provider *Provider, content string) error {
client, err := getNotificationClient(provider)
if err != nil {
return err
}
err = client.Send(context.Background(), "", content)
return err
}

View File

@@ -103,7 +103,7 @@ func GetOidcDiscovery(host string) OidcDiscovery {
SubjectTypesSupported: []string{"public"},
IdTokenSigningAlgValuesSupported: []string{"RS256"},
ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"},
ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isGlobalAdmin", "isForbidden", "signupApplication", "ldap"},
ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isForbidden", "signupApplication", "ldap"},
RequestParameterSupported: true,
RequestObjectSigningAlgValuesSupported: []string{"HS256", "HS384", "HS512"},
EndSessionEndpoint: fmt.Sprintf("%s/api/logout", originBackend),

View File

@@ -437,14 +437,6 @@ func organizationChangeTrigger(oldName string, newName string) error {
return err
}
record := new(Record)
record.Owner = newName
record.Organization = newName
_, err = session.Where("organization=?", oldName).Update(record)
if err != nil {
return err
}
resource := new(Resource)
resource.Owner = newName
_, err = session.Where("owner=?", oldName).Update(resource)

View File

@@ -16,7 +16,10 @@ package object
import (
"database/sql"
"flag"
"fmt"
"os"
"regexp"
"runtime"
"strings"
@@ -32,7 +35,24 @@ import (
_ "modernc.org/sqlite" // db = sqlite
)
var ormer *Ormer
var (
ormer *Ormer = nil
isCreateDatabaseDefined = false
createDatabase = true
)
func InitFlag() {
if !isCreateDatabaseDefined {
isCreateDatabaseDefined = true
createDatabase = getCreateDatabaseFlag()
}
}
func getCreateDatabaseFlag() bool {
res := flag.Bool("createDatabase", false, "true if you need to create database")
flag.Parse()
return *res
}
func InitConfig() {
err := beego.LoadAppConfig("ini", "../conf/app.conf")
@@ -42,12 +62,23 @@ func InitConfig() {
beego.BConfig.WebConfig.Session.SessionOn = true
InitAdapter(true)
CreateTables(true)
InitAdapter()
CreateTables()
DoMigration()
}
func InitAdapter(createDatabase bool) {
func InitAdapter() {
if conf.GetConfigString("driverName") == "" {
if !util.FileExist("conf/app.conf") {
dir, err := os.Getwd()
if err != nil {
panic(err)
}
dir = strings.ReplaceAll(dir, "\\", "/")
panic(fmt.Sprintf("The Casdoor config file: \"app.conf\" was not found, it should be placed at: \"%s/conf/app.conf\"", dir))
}
}
if createDatabase {
err := createDatabaseForPostgres(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName"))
if err != nil {
@@ -62,7 +93,7 @@ func InitAdapter(createDatabase bool) {
ormer.Engine.SetTableMapper(tbMapper)
}
func CreateTables(createDatabase bool) {
func CreateTables() {
if createDatabase {
err := ormer.CreateDatabase()
if err != nil {
@@ -105,9 +136,14 @@ func NewAdapter(driverName string, dataSourceName string, dbName string) *Ormer
return a
}
func refineDataSourceNameForPostgres(dataSourceName string) string {
reg := regexp.MustCompile(`dbname=[^ ]+\s*`)
return reg.ReplaceAllString(dataSourceName, "")
}
func createDatabaseForPostgres(driverName string, dataSourceName string, dbName string) error {
if driverName == "postgres" {
db, err := sql.Open(driverName, dataSourceName)
db, err := sql.Open(driverName, refineDataSourceNameForPostgres(dataSourceName))
if err != nil {
return err
}
@@ -119,6 +155,21 @@ func createDatabaseForPostgres(driverName string, dataSourceName string, dbName
return err
}
}
schema := util.GetValueFromDataSourceName("search_path", dataSourceName)
if schema != "" {
db, err = sql.Open(driverName, dataSourceName)
if err != nil {
return err
}
defer db.Close()
_, err = db.Exec(fmt.Sprintf("CREATE SCHEMA %s;", schema))
if err != nil {
if !strings.Contains(err.Error(), "already exists") {
return err
}
}
}
return nil
} else {
@@ -151,6 +202,12 @@ func (a *Ormer) open() {
if err != nil {
panic(err)
}
if a.driverName == "postgres" {
schema := util.GetValueFromDataSourceName("search_path", dataSourceName)
if schema != "" {
engine.SetSchema(schema)
}
}
a.Engine = engine
}
@@ -229,11 +286,6 @@ func (a *Ormer) createTable() {
panic(err)
}
err = a.Engine.Sync2(new(Record))
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(Webhook))
if err != nil {
panic(err)
@@ -294,76 +346,3 @@ func (a *Ormer) createTable() {
panic(err)
}
}
func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
session := ormer.Engine.Prepare()
if offset != -1 && limit != -1 {
session.Limit(limit, offset)
}
if owner != "" {
session = session.And("owner=?", owner)
}
if field != "" && value != "" {
if util.FilterField(field) {
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
}
}
if sortField == "" || sortOrder == "" {
sortField = "created_time"
}
if sortOrder == "ascend" {
session = session.Asc(util.SnakeString(sortField))
} else {
session = session.Desc(util.SnakeString(sortField))
}
return session
}
func GetSessionForUser(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
session := ormer.Engine.Prepare()
if offset != -1 && limit != -1 {
session.Limit(limit, offset)
}
if owner != "" {
if offset == -1 {
session = session.And("owner=?", owner)
} else {
session = session.And("a.owner=?", owner)
}
}
if field != "" && value != "" {
if util.FilterField(field) {
if offset != -1 {
field = fmt.Sprintf("a.%s", field)
}
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
}
}
if sortField == "" || sortOrder == "" {
sortField = "created_time"
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
tableName := tableNamePrefix + "user"
if offset == -1 {
if sortOrder == "ascend" {
session = session.Asc(util.SnakeString(sortField))
} else {
session = session.Desc(util.SnakeString(sortField))
}
} else {
if sortOrder == "ascend" {
session = session.Alias("a").
Join("INNER", []string{tableName, "b"}, "a.owner = b.owner and a.name = b.name").
Select("b.*").
Asc("a." + util.SnakeString(sortField))
} else {
session = session.Alias("a").
Join("INNER", []string{tableName, "b"}, "a.owner = b.owner and a.name = b.name").
Select("b.*").
Desc("a." + util.SnakeString(sortField))
}
}
return session
}

96
object/ormer_session.go Normal file
View File

@@ -0,0 +1,96 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"fmt"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/xorm"
)
func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
session := ormer.Engine.Prepare()
if offset != -1 && limit != -1 {
session.Limit(limit, offset)
}
if owner != "" {
session = session.And("owner=?", owner)
}
if field != "" && value != "" {
if util.FilterField(field) {
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
}
}
if sortField == "" || sortOrder == "" {
sortField = "created_time"
}
if sortOrder == "ascend" {
session = session.Asc(util.SnakeString(sortField))
} else {
session = session.Desc(util.SnakeString(sortField))
}
return session
}
func GetSessionForUser(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
session := ormer.Engine.Prepare()
if offset != -1 && limit != -1 {
session.Limit(limit, offset)
}
if owner != "" {
if offset == -1 {
session = session.And("owner=?", owner)
} else {
session = session.And("a.owner=?", owner)
}
}
if field != "" && value != "" {
if util.FilterField(field) {
if offset != -1 {
field = fmt.Sprintf("a.%s", field)
}
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
}
}
if sortField == "" || sortOrder == "" {
sortField = "created_time"
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
tableName := tableNamePrefix + "user"
if offset == -1 {
if sortOrder == "ascend" {
session = session.Asc(util.SnakeString(sortField))
} else {
session = session.Desc(util.SnakeString(sortField))
}
} else {
if sortOrder == "ascend" {
session = session.Alias("a").
Join("INNER", []string{tableName, "b"}, "a.owner = b.owner and a.name = b.name").
Select("b.*").
Asc("a." + util.SnakeString(sortField))
} else {
session = session.Alias("a").
Join("INNER", []string{tableName, "b"}, "a.owner = b.owner and a.name = b.name").
Select("b.*").
Desc("a." + util.SnakeString(sortField))
}
}
return session
}

View File

@@ -79,9 +79,7 @@ func (p *Permission) setEnforcerAdapter(enforcer *casbin.Enforcer) error {
}
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
driverName := conf.GetConfigString("driverName")
dataSourceName := conf.GetConfigRealDataSourceName(driverName)
adapter, err := xormadapter.NewAdapterWithTableName(driverName, dataSourceName, tableName, tableNamePrefix, true)
adapter, err := xormadapter.NewAdapterByEngineWithTableName(ormer.Engine, tableName, tableNamePrefix)
if err != nil {
return err
}

View File

@@ -33,8 +33,8 @@ func getPermissionMap(owner string) (map[string]*Permission, error) {
return m, err
}
func UploadPermissions(owner string, fileId string) (bool, error) {
table := xlsx.ReadXlsxFile(fileId)
func UploadPermissions(owner string, path string) (bool, error) {
table := xlsx.ReadXlsxFile(path)
oldUserMap, err := getPermissionMap(owner)
if err != nil {

View File

@@ -16,6 +16,7 @@ package object
import (
"fmt"
"time"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/core"
@@ -28,15 +29,39 @@ type Plan struct {
DisplayName string `xorm:"varchar(100)" json:"displayName"`
Description string `xorm:"varchar(100)" json:"description"`
PricePerMonth float64 `json:"pricePerMonth"`
PricePerYear float64 `json:"pricePerYear"`
Currency string `xorm:"varchar(100)" json:"currency"`
IsEnabled bool `json:"isEnabled"`
Price float64 `json:"price"`
Currency string `xorm:"varchar(100)" json:"currency"`
Period string `xorm:"varchar(100)" json:"period"`
Product string `xorm:"varchar(100)" json:"product"`
PaymentProviders []string `xorm:"varchar(100)" json:"paymentProviders"` // payment providers for related product
IsEnabled bool `json:"isEnabled"`
Role string `xorm:"varchar(100)" json:"role"`
Options []string `xorm:"-" json:"options"`
}
const (
PeriodMonthly = "Monthly"
PeriodYearly = "Yearly"
)
func (plan *Plan) GetId() string {
return fmt.Sprintf("%s/%s", plan.Owner, plan.Name)
}
func GetDuration(period string) (startTime time.Time, endTime time.Time) {
if period == PeriodYearly {
startTime = time.Now()
endTime = startTime.AddDate(1, 0, 0)
} else if period == PeriodMonthly {
startTime = time.Now()
endTime = startTime.AddDate(0, 1, 0)
} else {
panic(fmt.Sprintf("invalid period: %s", period))
}
return
}
func GetPlanCount(owner, field, value string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "")
return session.Count(&Plan{})
@@ -114,38 +139,3 @@ func DeletePlan(plan *Plan) (bool, error) {
}
return affected != 0, nil
}
func (plan *Plan) GetId() string {
return fmt.Sprintf("%s/%s", plan.Owner, plan.Name)
}
func Subscribe(owner string, user string, plan string, pricing string) (*Subscription, error) {
selectedPricing, err := GetPricing(fmt.Sprintf("%s/%s", owner, pricing))
if err != nil {
return nil, err
}
valid := selectedPricing != nil && selectedPricing.IsEnabled
if !valid {
return nil, nil
}
planBelongToPricing, err := selectedPricing.HasPlan(owner, plan)
if err != nil {
return nil, err
}
if planBelongToPricing {
newSubscription := NewSubscription(owner, user, plan, selectedPricing.TrialDuration)
affected, err := AddSubscription(newSubscription)
if err != nil {
return nil, err
}
if affected {
return newSubscription, nil
}
}
return nil, nil
}

View File

@@ -16,7 +16,6 @@ package object
import (
"fmt"
"strings"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/core"
@@ -33,12 +32,26 @@ type Pricing struct {
IsEnabled bool `json:"isEnabled"`
TrialDuration int `json:"trialDuration"`
Application string `xorm:"varchar(100)" json:"application"`
}
Submitter string `xorm:"varchar(100)" json:"submitter"`
Approver string `xorm:"varchar(100)" json:"approver"`
ApproveTime string `xorm:"varchar(100)" json:"approveTime"`
func (pricing *Pricing) GetId() string {
return fmt.Sprintf("%s/%s", pricing.Owner, pricing.Name)
}
State string `xorm:"varchar(100)" json:"state"`
func (pricing *Pricing) HasPlan(planName string) (bool, error) {
planId := util.GetId(pricing.Owner, planName)
plan, err := GetPlan(planId)
if err != nil {
return false, err
}
if plan == nil {
return false, fmt.Errorf("plan: %s does not exist", planId)
}
if util.InSlice(pricing.Plans, plan.Name) {
return true, nil
}
return false, nil
}
func GetPricingCount(owner, field, value string) (int64, error) {
@@ -74,7 +87,7 @@ func getPricing(owner, name string) (*Pricing, error) {
pricing := Pricing{Owner: owner, Name: name}
existed, err := ormer.Engine.Get(&pricing)
if err != nil {
return &pricing, err
return nil, err
}
if existed {
return &pricing, nil
@@ -88,6 +101,20 @@ func GetPricing(id string) (*Pricing, error) {
return getPricing(owner, name)
}
func GetApplicationDefaultPricing(owner, appName string) (*Pricing, error) {
pricings := make([]*Pricing, 0, 1)
err := ormer.Engine.Asc("created_time").Find(&pricings, &Pricing{Owner: owner, Application: appName})
if err != nil {
return nil, err
}
for _, pricing := range pricings {
if pricing.IsEnabled {
return pricing, nil
}
}
return nil, nil
}
func UpdatePricing(id string, pricing *Pricing) (bool, error) {
owner, name := util.GetOwnerAndNameFromId(id)
if p, err := getPricing(owner, name); err != nil {
@@ -120,28 +147,21 @@ func DeletePricing(pricing *Pricing) (bool, error) {
return affected != 0, nil
}
func (pricing *Pricing) GetId() string {
return fmt.Sprintf("%s/%s", pricing.Owner, pricing.Name)
}
func (pricing *Pricing) HasPlan(owner string, plan string) (bool, error) {
selectedPlan, err := GetPlan(fmt.Sprintf("%s/%s", owner, plan))
if err != nil {
return false, err
}
if selectedPlan == nil {
return false, nil
}
result := false
for _, pricingPlan := range pricing.Plans {
if strings.Contains(pricingPlan, selectedPlan.Name) {
result = true
break
func CheckPricingAndPlan(owner, pricingName, planName string) error {
pricingId := util.GetId(owner, pricingName)
pricing, err := GetPricing(pricingId)
if pricing == nil || err != nil {
if pricing == nil && err == nil {
err = fmt.Errorf("pricing: %s does not exist", pricingName)
}
return err
}
return result, nil
ok, err := pricing.HasPlan(planName)
if err != nil {
return err
}
if !ok {
return fmt.Errorf("pricing: %s does not have plan: %s", pricingName, planName)
}
return nil
}

View File

@@ -141,24 +141,24 @@ func (product *Product) isValidProvider(provider *Provider) bool {
return false
}
func (product *Product) getProvider(providerId string) (*Provider, error) {
provider, err := getProvider(product.Owner, providerId)
func (product *Product) getProvider(providerName string) (*Provider, error) {
provider, err := getProvider(product.Owner, providerName)
if err != nil {
return nil, err
}
if provider == nil {
return nil, fmt.Errorf("the payment provider: %s does not exist", providerId)
return nil, fmt.Errorf("the payment provider: %s does not exist", providerName)
}
if !product.isValidProvider(provider) {
return nil, fmt.Errorf("the payment provider: %s is not valid for the product: %s", providerId, product.Name)
return nil, fmt.Errorf("the payment provider: %s is not valid for the product: %s", providerName, product.Name)
}
return provider, nil
}
func BuyProduct(id string, providerName string, user *User, host string) (string, string, error) {
func BuyProduct(id string, user *User, providerName, pricingName, planName, host string) (string, string, error) {
product, err := GetProduct(id)
if err != nil {
return "", "", err
@@ -181,13 +181,31 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
owner := product.Owner
productName := product.Name
payerName := fmt.Sprintf("%s | %s", user.Name, user.DisplayName)
paymentName := util.GenerateTimeId()
paymentName := fmt.Sprintf("payment_%v", util.GenerateTimeId())
productDisplayName := product.DisplayName
originFrontend, originBackend := getOriginFromHost(host)
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
notifyUrl := fmt.Sprintf("%s/api/notify-payment/%s/%s", originBackend, owner, paymentName)
// Create an Order and get the payUrl
if user.Type == "paid-user" {
// Create a subscription for `paid-user`
if pricingName != "" && planName != "" {
plan, err := GetPlan(util.GetId(owner, planName))
if err != nil {
return "", "", err
}
if plan == nil {
return "", "", fmt.Errorf("the plan: %s does not exist", planName)
}
sub := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
_, err = AddSubscription(sub)
if err != nil {
return "", "", err
}
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
}
}
// Create an OrderId and get the payUrl
payUrl, orderId, err := pProvider.Pay(providerName, productName, payerName, paymentName, productDisplayName, product.Price, product.Currency, returnUrl, notifyUrl)
if err != nil {
return "", "", err
@@ -228,7 +246,6 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
if !affected {
return "", "", fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
}
return payUrl, orderId, err
}
@@ -252,3 +269,38 @@ func ExtendProductWithProviders(product *Product) error {
return nil
}
func CreateProductForPlan(plan *Plan) *Product {
product := &Product{
Owner: plan.Owner,
Name: fmt.Sprintf("product_%v", util.GetRandomName()),
DisplayName: fmt.Sprintf("Product for Plan %v/%v/%v", plan.Name, plan.DisplayName, plan.Period),
CreatedTime: plan.CreatedTime,
Image: "https://cdn.casbin.org/img/casdoor-logo_1185x256.png", // TODO
Detail: fmt.Sprintf("This product was auto created for plan %v(%v), subscription period is %v", plan.Name, plan.DisplayName, plan.Period),
Description: plan.Description,
Tag: "auto_created_product_for_plan",
Price: plan.Price,
Currency: plan.Currency,
Quantity: 999,
Sold: 0,
Providers: plan.PaymentProviders,
State: "Published",
}
if product.Providers == nil {
product.Providers = []string{}
}
return product
}
func UpdateProductForPlan(plan *Plan, product *Product) {
product.Owner = plan.Owner
product.DisplayName = fmt.Sprintf("Product for Plan %v/%v/%v", plan.Name, plan.DisplayName, plan.Period)
product.Detail = fmt.Sprintf("This product was auto created for plan %v(%v), subscription period is %v", plan.Name, plan.DisplayName, plan.Period)
product.Price = plan.Price
product.Currency = plan.Currency
product.Providers = plan.PaymentProviders
}

View File

@@ -21,6 +21,7 @@ import (
"github.com/beego/beego/context"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
)
var logPostOnly bool
@@ -30,26 +31,10 @@ func init() {
}
type Record struct {
Id int `xorm:"int notnull pk autoincr" json:"id"`
Owner string `xorm:"varchar(100) index" json:"owner"`
Name string `xorm:"varchar(100) index" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
Organization string `xorm:"varchar(100)" json:"organization"`
ClientIp string `xorm:"varchar(100)" json:"clientIp"`
User string `xorm:"varchar(100)" json:"user"`
Method string `xorm:"varchar(100)" json:"method"`
RequestUri string `xorm:"varchar(1000)" json:"requestUri"`
Action string `xorm:"varchar(1000)" json:"action"`
Object string `xorm:"-" json:"object"`
ExtendedUser *User `xorm:"-" json:"extendedUser"`
IsTriggered bool `json:"isTriggered"`
casvisorsdk.Record
}
func NewRecord(ctx *context.Context) *Record {
func NewRecord(ctx *context.Context) *casvisorsdk.Record {
ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1)
action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1)
requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"})
@@ -62,7 +47,7 @@ func NewRecord(ctx *context.Context) *Record {
object = string(ctx.Input.RequestBody)
}
record := Record{
record := casvisorsdk.Record{
Name: util.GenerateId(),
CreatedTime: util.GetCurrentTime(),
ClientIp: ip,
@@ -76,7 +61,7 @@ func NewRecord(ctx *context.Context) *Record {
return &record
}
func AddRecord(record *Record) bool {
func AddRecord(record *casvisorsdk.Record) bool {
if logPostOnly {
if record.Method == "GET" {
return false
@@ -96,51 +81,19 @@ func AddRecord(record *Record) bool {
fmt.Println(errWebhook)
}
affected, err := ormer.Engine.Insert(record)
if casvisorsdk.GetClient() == nil {
return false
}
affected, err := casvisorsdk.AddRecord(record)
if err != nil {
panic(err)
}
return affected != 0
return affected
}
func GetRecordCount(field, value string, filterRecord *Record) (int64, error) {
session := GetSession("", -1, -1, field, value, "", "")
return session.Count(filterRecord)
}
func GetRecords() ([]*Record, error) {
records := []*Record{}
err := ormer.Engine.Desc("id").Find(&records)
if err != nil {
return records, err
}
return records, nil
}
func GetPaginationRecords(offset, limit int, field, value, sortField, sortOrder string, filterRecord *Record) ([]*Record, error) {
records := []*Record{}
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
err := session.Find(&records, filterRecord)
if err != nil {
return records, err
}
return records, nil
}
func GetRecordsByField(record *Record) ([]*Record, error) {
records := []*Record{}
err := ormer.Engine.Find(&records, record)
if err != nil {
return records, err
}
return records, nil
}
func SendWebhooks(record *Record) error {
func SendWebhooks(record *casvisorsdk.Record) error {
webhooks, err := getWebhooksByOrganization(record.Organization)
if err != nil {
return err
@@ -160,17 +113,16 @@ func SendWebhooks(record *Record) error {
}
if matched {
var user *User
if webhook.IsUserExtended {
user, err := getUser(record.Organization, record.User)
user, err = getUser(record.Organization, record.User)
user, err = GetMaskedUser(user, false, err)
if err != nil {
return err
}
record.ExtendedUser = user
}
err := sendWebhook(webhook, record)
err = sendWebhook(webhook, record, user)
if err != nil {
return err
}

50
object/record_casvisor.go Normal file
View File

@@ -0,0 +1,50 @@
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"strings"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
)
func getCasvisorApplication() *Application {
applications, err := GetApplications("admin")
if err != nil {
panic(err)
}
for _, application := range applications {
if strings.Contains(strings.ToLower(application.Name), "casvisor") {
return application
}
}
return nil
}
func InitCasvisorConfig() {
application := getCasvisorApplication()
if application == nil {
return
}
casvisorEndpoint := application.HomepageUrl
clientId := application.ClientId
clientSecret := application.ClientSecret
casdoorOrganization := application.Organization
casdoorApplication := application.Name
casvisorsdk.InitConfig(casvisorEndpoint, clientId, clientSecret, casdoorOrganization, casdoorApplication)
}

View File

@@ -33,8 +33,8 @@ func getRoleMap(owner string) (map[string]*Role, error) {
return m, nil
}
func UploadRoles(owner string, fileId string) (bool, error) {
table := xlsx.ReadXlsxFile(fileId)
func UploadRoles(owner string, path string) (bool, error) {
table := xlsx.ReadXlsxFile(path)
oldUserMap, err := getRoleMap(owner)
if err != nil {

View File

@@ -31,8 +31,8 @@ import (
"github.com/RobotsAndPencils/go-saml"
"github.com/beevik/etree"
"github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
dsig "github.com/russellhaering/goxmldsig"
uuid "github.com/satori/go.uuid"
)
// NewSamlResponse
@@ -46,7 +46,7 @@ func NewSamlResponse(user *User, host string, certificate string, destination st
expireTime := time.Now().UTC().Add(time.Hour * 24).Format(time.RFC3339)
samlResponse.CreateAttr("xmlns:samlp", "urn:oasis:names:tc:SAML:2.0:protocol")
samlResponse.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:2.0:assertion")
arId := uuid.NewV4()
arId := uuid.New()
samlResponse.CreateAttr("ID", fmt.Sprintf("_%s", arId))
samlResponse.CreateAttr("Version", "2.0")
@@ -60,7 +60,7 @@ func NewSamlResponse(user *User, host string, certificate string, destination st
assertion := samlResponse.CreateElement("saml:Assertion")
assertion.CreateAttr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
assertion.CreateAttr("xmlns:xs", "http://www.w3.org/2001/XMLSchema")
assertion.CreateAttr("ID", fmt.Sprintf("_%s", uuid.NewV4()))
assertion.CreateAttr("ID", fmt.Sprintf("_%s", uuid.New()))
assertion.CreateAttr("Version", "2.0")
assertion.CreateAttr("IssueInstant", now)
assertion.CreateElement("saml:Issuer").SetText(host)
@@ -82,7 +82,7 @@ func NewSamlResponse(user *User, host string, certificate string, destination st
}
authnStatement := assertion.CreateElement("saml:AuthnStatement")
authnStatement.CreateAttr("AuthnInstant", now)
authnStatement.CreateAttr("SessionIndex", fmt.Sprintf("_%s", uuid.NewV4()))
authnStatement.CreateAttr("SessionIndex", fmt.Sprintf("_%s", uuid.New()))
authnStatement.CreateAttr("SessionNotOnOrAfter", expireTime)
authnStatement.CreateElement("saml:AuthnContext").CreateElement("saml:AuthnContextClassRef").SetText("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport")
@@ -355,7 +355,7 @@ func NewSamlResponse11(user *User, requestID string, host string) *etree.Element
samlResponse.CreateAttr("MajorVersion", "1")
samlResponse.CreateAttr("MinorVersion", "1")
responseID := uuid.NewV4()
responseID := uuid.New()
samlResponse.CreateAttr("ResponseID", fmt.Sprintf("_%s", responseID))
samlResponse.CreateAttr("InResponseTo", requestID)
@@ -371,7 +371,7 @@ func NewSamlResponse11(user *User, requestID string, host string) *etree.Element
assertion.CreateAttr("xmlns:saml", "urn:oasis:names:tc:SAML:1.0:assertion")
assertion.CreateAttr("MajorVersion", "1")
assertion.CreateAttr("MinorVersion", "1")
assertion.CreateAttr("AssertionID", uuid.NewV4().String())
assertion.CreateAttr("AssertionID", uuid.New().String())
assertion.CreateAttr("Issuer", host)
assertion.CreateAttr("IssueInstant", now)

View File

@@ -26,8 +26,6 @@ func getSmsClient(provider *Provider) (sender.SmsClient, error) {
if provider.Type == sender.HuaweiCloud || provider.Type == sender.AzureACS {
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.ProviderUrl, provider.AppId)
} else if provider.Type == "Custom HTTP SMS" {
client, err = newHttpSmsClient(provider.Endpoint, provider.Method, provider.ClientId, provider.Title)
} else {
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId)
}

View File

@@ -18,47 +18,108 @@ import (
"fmt"
"time"
"github.com/casdoor/casdoor/pp"
"github.com/casdoor/casdoor/util"
"github.com/xorm-io/core"
)
const defaultStatus = "Pending"
type SubscriptionState string
const (
SubStatePending SubscriptionState = "Pending"
SubStateError SubscriptionState = "Error"
SubStateSuspended SubscriptionState = "Suspended" // suspended by the admin
SubStateActive SubscriptionState = "Active"
SubStateUpcoming SubscriptionState = "Upcoming"
SubStateExpired SubscriptionState = "Expired"
)
type Subscription struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
DisplayName string `xorm:"varchar(100)" json:"displayName"`
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
Description string `xorm:"varchar(100)" json:"description"`
StartDate time.Time `json:"startDate"`
EndDate time.Time `json:"endDate"`
Duration int `json:"duration"`
Description string `xorm:"varchar(100)" json:"description"`
User string `xorm:"varchar(100)" json:"user"`
Pricing string `xorm:"varchar(100)" json:"pricing"`
Plan string `xorm:"varchar(100)" json:"plan"`
Payment string `xorm:"varchar(100)" json:"payment"`
User string `xorm:"mediumtext" json:"user"`
Plan string `xorm:"varchar(100)" json:"plan"`
IsEnabled bool `json:"isEnabled"`
Submitter string `xorm:"varchar(100)" json:"submitter"`
Approver string `xorm:"varchar(100)" json:"approver"`
ApproveTime string `xorm:"varchar(100)" json:"approveTime"`
State string `xorm:"varchar(100)" json:"state"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
Period string `xorm:"varchar(100)" json:"period"`
State SubscriptionState `xorm:"varchar(100)" json:"state"`
}
func NewSubscription(owner string, user string, plan string, duration int) *Subscription {
func (sub *Subscription) GetId() string {
return fmt.Sprintf("%s/%s", sub.Owner, sub.Name)
}
func (sub *Subscription) UpdateState() error {
preState := sub.State
// update subscription state by payment state
if sub.State == SubStatePending {
if sub.Payment == "" {
return nil
}
payment, err := GetPayment(util.GetId(sub.Owner, sub.Payment))
if err != nil {
return err
}
if payment == nil {
sub.Description = fmt.Sprintf("payment: %s does not exist", sub.Payment)
sub.State = SubStateError
} else {
if payment.State == pp.PaymentStatePaid {
sub.State = SubStateActive
} else if payment.State != pp.PaymentStateCreated {
// other states: Canceled, Timeout, Error
sub.Description = fmt.Sprintf("payment: %s state is %v", sub.Payment, payment.State)
sub.State = SubStateError
}
}
}
if sub.State == SubStateActive || sub.State == SubStateUpcoming || sub.State == SubStateExpired {
if sub.EndTime.Before(time.Now()) {
sub.State = SubStateExpired
} else if sub.StartTime.After(time.Now()) {
sub.State = SubStateUpcoming
} else {
sub.State = SubStateActive
}
}
if preState != sub.State {
_, err := UpdateSubscription(sub.GetId(), sub)
if err != nil {
return err
}
}
return nil
}
func NewSubscription(owner, userName, planName, paymentName, period string) *Subscription {
startTime, endTime := GetDuration(period)
id := util.GenerateId()[:6]
return &Subscription{
Name: "Subscription_" + id,
DisplayName: "New Subscription - " + id,
Owner: owner,
User: owner + "/" + user,
Plan: owner + "/" + plan,
Name: "sub_" + id,
DisplayName: "New Subscription - " + id,
CreatedTime: util.GetCurrentTime(),
State: defaultStatus,
Duration: duration,
StartDate: time.Now(),
EndDate: time.Now().AddDate(0, 0, duration),
User: userName,
Plan: planName,
Payment: paymentName,
StartTime: startTime,
EndTime: endTime,
Period: period,
State: SubStatePending, // waiting for payment complete
}
}
@@ -73,7 +134,28 @@ func GetSubscriptions(owner string) ([]*Subscription, error) {
if err != nil {
return subscriptions, err
}
for _, sub := range subscriptions {
err = sub.UpdateState()
if err != nil {
return nil, err
}
}
return subscriptions, nil
}
func GetSubscriptionsByUser(owner, userName string) ([]*Subscription, error) {
subscriptions := []*Subscription{}
err := ormer.Engine.Desc("created_time").Find(&subscriptions, &Subscription{Owner: owner, User: userName})
if err != nil {
return subscriptions, err
}
// update subscription state
for _, sub := range subscriptions {
err = sub.UpdateState()
if err != nil {
return subscriptions, err
}
}
return subscriptions, nil
}
@@ -84,7 +166,12 @@ func GetPaginationSubscriptions(owner string, offset, limit int, field, value, s
if err != nil {
return subscriptions, err
}
for _, sub := range subscriptions {
err = sub.UpdateState()
if err != nil {
return nil, err
}
}
return subscriptions, nil
}
@@ -144,7 +231,3 @@ func DeleteSubscription(subscription *Subscription) (bool, error) {
return affected != 0, nil
}
func (subscription *Subscription) GetId() string {
return fmt.Sprintf("%s/%s", subscription.Owner, subscription.Name)
}

View File

@@ -37,12 +37,13 @@ type Syncer struct {
Organization string `xorm:"varchar(100)" json:"organization"`
Type string `xorm:"varchar(100)" json:"type"`
DatabaseType string `xorm:"varchar(100)" json:"databaseType"`
SslMode string `xorm:"varchar(100)" json:"sslMode"`
Host string `xorm:"varchar(100)" json:"host"`
Port int `json:"port"`
User string `xorm:"varchar(100)" json:"user"`
Password string `xorm:"varchar(100)" json:"password"`
DatabaseType string `xorm:"varchar(100)" json:"databaseType"`
Database string `xorm:"varchar(100)" json:"database"`
Table string `xorm:"varchar(100)" json:"table"`
TableColumns []*TableColumn `xorm:"mediumtext" json:"tableColumns"`
@@ -250,7 +251,7 @@ func (syncer *Syncer) getKey() string {
return key
}
func RunSyncer(syncer *Syncer) {
func RunSyncer(syncer *Syncer) error {
syncer.initAdapter()
syncer.syncUsers()
return syncer.syncUsers()
}

View File

@@ -52,11 +52,14 @@ func addSyncerJob(syncer *Syncer) error {
syncer.initAdapter()
syncer.syncUsers()
err := syncer.syncUsers()
if err != nil {
return err
}
schedule := fmt.Sprintf("@every %ds", syncer.SyncInterval)
cron := getCronMap(syncer.Name)
_, err := cron.AddFunc(schedule, syncer.syncUsers)
_, err = cron.AddFunc(schedule, syncer.syncUsersNoError)
if err != nil {
return err
}

View File

@@ -19,15 +19,15 @@ import (
"time"
)
func (syncer *Syncer) syncUsers() {
func (syncer *Syncer) syncUsers() error {
if len(syncer.TableColumns) == 0 {
return
return fmt.Errorf("The syncer table columns should not be empty")
}
fmt.Printf("Running syncUsers()..\n")
users, _, _ := syncer.getUserMap()
oUsers, oUserMap, err := syncer.getOriginalUserMap()
oUsers, _, err := syncer.getOriginalUserMap()
if err != nil {
fmt.Printf(err.Error())
@@ -35,10 +35,8 @@ func (syncer *Syncer) syncUsers() {
line := fmt.Sprintf("[%s] %s\n", timestamp, err.Error())
_, err = updateSyncerErrorText(syncer, line)
if err != nil {
panic(err)
return err
}
return
}
fmt.Printf("Users: %d, oUsers: %d\n", len(users), len(oUsers))
@@ -55,6 +53,11 @@ func (syncer *Syncer) syncUsers() {
myUsers[syncer.getUserValue(m, key)] = m
}
myOUsers := map[string]*User{}
for _, m := range oUsers {
myOUsers[syncer.getUserValue(m, key)] = m
}
newUsers := []*User{}
for _, oUser := range oUsers {
primary := syncer.getUserValue(oUser, key)
@@ -71,28 +74,30 @@ func (syncer *Syncer) syncUsers() {
updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
updatedUser.Hash = oHash
updatedUser.PreHash = oHash
fmt.Printf("Update from oUser to user: %v\n", updatedUser)
_, err = syncer.updateUserForOriginalByFields(updatedUser, key)
if err != nil {
panic(err)
return err
}
fmt.Printf("Update from oUser to user: %v\n", updatedUser)
}
} else {
if user.PreHash == oHash {
if !syncer.IsReadOnly {
updatedOUser := syncer.createOriginalUserFromUser(user)
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
_, err = syncer.updateUser(updatedOUser)
if err != nil {
panic(err)
return err
}
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
}
// update preHash
user.PreHash = user.Hash
_, err = SetUserField(user, "pre_hash", user.PreHash)
if err != nil {
panic(err)
return err
}
} else {
if user.Hash == oHash {
@@ -100,17 +105,18 @@ func (syncer *Syncer) syncUsers() {
user.PreHash = user.Hash
_, err = SetUserField(user, "pre_hash", user.PreHash)
if err != nil {
panic(err)
return err
}
} else {
updatedUser := syncer.createUserFromOriginalUser(oUser, affiliationMap)
updatedUser.Hash = oHash
updatedUser.PreHash = oHash
fmt.Printf("Update from oUser to user (2nd condition): %v\n", updatedUser)
_, err = syncer.updateUserForOriginalByFields(updatedUser, key)
if err != nil {
panic(err)
return err
}
fmt.Printf("Update from oUser to user (2nd condition): %v\n", updatedUser)
}
}
}
@@ -118,20 +124,30 @@ func (syncer *Syncer) syncUsers() {
}
_, err = AddUsersInBatch(newUsers)
if err != nil {
panic(err)
return err
}
if !syncer.IsReadOnly {
for _, user := range users {
id := user.Id
if _, ok := oUserMap[id]; !ok {
primary := syncer.getUserValue(user, key)
if _, ok := myOUsers[primary]; !ok {
newOUser := syncer.createOriginalUserFromUser(user)
fmt.Printf("New oUser: %v\n", newOUser)
_, err = syncer.addUser(newOUser)
if err != nil {
panic(err)
return err
}
fmt.Printf("New oUser: %v\n", newOUser)
}
}
}
return nil
}
func (syncer *Syncer) syncUsersNoError() {
err := syncer.syncUsers()
if err != nil {
fmt.Printf("syncUsersNoError() error: %s\n", err.Error())
}
}

View File

@@ -31,8 +31,8 @@ type Credential struct {
}
func (syncer *Syncer) getOriginalUsers() ([]*OriginalUser, error) {
sql := fmt.Sprintf("select * from %s", syncer.getTable())
results, err := syncer.Ormer.Engine.QueryString(sql)
var results []map[string]string
err := syncer.Ormer.Engine.Table(syncer.getTable()).Find(&results)
if err != nil {
return nil, err
}
@@ -64,19 +64,10 @@ func (syncer *Syncer) getOriginalUserMap() ([]*OriginalUser, map[string]*Origina
func (syncer *Syncer) addUser(user *OriginalUser) (bool, error) {
m := syncer.getMapFromOriginalUser(user)
keyString, valueString := syncer.getSqlKeyValueStringFromMap(m)
sql := fmt.Sprintf("insert into %s (%s) values (%s)", syncer.getTable(), keyString, valueString)
res, err := syncer.Ormer.Engine.Exec(sql)
affected, err := syncer.Ormer.Engine.Table(syncer.getTable()).Insert(m)
if err != nil {
return false, err
}
affected, err := res.RowsAffected()
if err != nil {
return false, err
}
return affected != 0, nil
}
@@ -93,23 +84,14 @@ func (syncer *Syncer) getCasdoorColumns() []string {
func (syncer *Syncer) updateUser(user *OriginalUser) (bool, error) {
key := syncer.getKey()
m := syncer.getMapFromOriginalUser(user)
pkValue := m[key]
delete(m, key)
setString := syncer.getSqlSetStringFromMap(m)
sql := fmt.Sprintf("update %s set %s where %s = %s", syncer.getTable(), setString, key, pkValue)
res, err := syncer.Ormer.Engine.Exec(sql)
affected, err := syncer.Ormer.Engine.Table(syncer.getTable()).ID(pkValue).Update(&m)
if err != nil {
return false, err
}
affected, err := res.RowsAffected()
if err != nil {
return false, err
}
return affected != 0, nil
}
@@ -185,7 +167,11 @@ func (syncer *Syncer) initAdapter() {
if syncer.DatabaseType == "mssql" {
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s", syncer.User, syncer.Password, syncer.Host, syncer.Port, syncer.Database)
} else if syncer.DatabaseType == "postgres" {
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=disable dbname=%s", syncer.User, syncer.Password, syncer.Host, syncer.Port, syncer.Database)
sslMode := "disable"
if syncer.SslMode != "" {
sslMode = syncer.SslMode
}
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=%s dbname=%s", syncer.User, syncer.Password, syncer.Host, syncer.Port, sslMode, syncer.Database)
} else {
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/", syncer.User, syncer.Password, syncer.Host, syncer.Port)
}

View File

@@ -154,14 +154,18 @@ func (syncer *Syncer) setUserByKeyValue(user *User, key string, value string) {
user.IsOnline = util.ParseBool(value)
case "IsAdmin":
user.IsAdmin = util.ParseBool(value)
case "IsGlobalAdmin":
user.IsGlobalAdmin = util.ParseBool(value)
case "IsForbidden":
user.IsForbidden = util.ParseBool(value)
case "IsDeleted":
user.IsDeleted = util.ParseBool(value)
case "CreatedIp":
user.CreatedIp = value
case "PreferredMfaType":
user.PreferredMfaType = value
case "TotpSecret":
user.TotpSecret = value
case "SignupApplication":
user.SignupApplication = value
}
}
@@ -289,10 +293,12 @@ func (syncer *Syncer) getMapFromOriginalUser(user *OriginalUser) map[string]stri
m["IsDefaultAvatar"] = util.BoolToString(user.IsDefaultAvatar)
m["IsOnline"] = util.BoolToString(user.IsOnline)
m["IsAdmin"] = util.BoolToString(user.IsAdmin)
m["IsGlobalAdmin"] = util.BoolToString(user.IsGlobalAdmin)
m["IsForbidden"] = util.BoolToString(user.IsForbidden)
m["IsDeleted"] = util.BoolToString(user.IsDeleted)
m["CreatedIp"] = user.CreatedIp
m["PreferredMfaType"] = user.PreferredMfaType
m["TotpSecret"] = user.TotpSecret
m["SignupApplication"] = user.SignupApplication
m2 := map[string]string{}
for _, tableColumn := range syncer.TableColumns {
@@ -316,19 +322,3 @@ func (syncer *Syncer) getSqlSetStringFromMap(m map[string]string) string {
}
return strings.Join(tokens, ", ")
}
func (syncer *Syncer) getSqlKeyValueStringFromMap(m map[string]string) (string, string) {
typeMap := syncer.getTableColumnsTypeMap()
keys := []string{}
values := []string{}
for k, v := range m {
if typeMap[k] == "string" {
v = fmt.Sprintf("'%s'", v)
}
keys = append(keys, k)
values = append(values, v)
}
return strings.Join(keys, ", "), strings.Join(values, ", ")
}

View File

@@ -824,7 +824,6 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
Type: "normal-user",
CreatedTime: util.GetCurrentTime(),
IsAdmin: false,
IsGlobalAdmin: false,
IsForbidden: false,
IsDeleted: false,
Properties: map[string]string{

View File

@@ -123,7 +123,7 @@ var stToServiceResponse sync.Map
// pgt is short for proxy granting ticket
var pgtToServiceResponse sync.Map
func CheckCasRestrict(application *Application, lang string, service string) error {
func CheckCasLogin(application *Application, lang string, service string) error {
if len(application.RedirectUris) > 0 && !application.IsRedirectUriValid(service) {
return fmt.Errorf(i18n.Translate(lang, "token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), service)
}

View File

@@ -73,7 +73,6 @@ type UserWithoutThirdIdp struct {
IsDefaultAvatar bool `json:"isDefaultAvatar"`
IsOnline bool `json:"isOnline"`
IsAdmin bool `json:"isAdmin"`
IsGlobalAdmin bool `json:"isGlobalAdmin"`
IsForbidden bool `json:"isForbidden"`
IsDeleted bool `json:"isDeleted"`
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
@@ -154,7 +153,6 @@ func getUserWithoutThirdIdp(user *User) *UserWithoutThirdIdp {
IsDefaultAvatar: user.IsDefaultAvatar,
IsOnline: user.IsOnline,
IsAdmin: user.IsAdmin,
IsGlobalAdmin: user.IsGlobalAdmin,
IsForbidden: user.IsForbidden,
IsDeleted: user.IsDeleted,
SignupApplication: user.SignupApplication,

View File

@@ -83,7 +83,6 @@ type User struct {
IsDefaultAvatar bool `json:"isDefaultAvatar"`
IsOnline bool `json:"isOnline"`
IsAdmin bool `json:"isAdmin"`
IsGlobalAdmin bool `json:"isGlobalAdmin"`
IsForbidden bool `json:"isForbidden"`
IsDeleted bool `json:"isDeleted"`
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
@@ -530,19 +529,19 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
columns = []string{
"owner", "display_name", "avatar",
"location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret",
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
"spotify", "strava", "stripe", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo",
"spotify", "strava", "stripe", "type", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo",
"yammer", "yandex", "zoom", "custom",
}
}
if isAdmin {
columns = append(columns, "name", "email", "phone", "country_code")
columns = append(columns, "name", "email", "phone", "country_code", "type")
}
if util.ContainsString(columns, "groups") {
@@ -628,12 +627,10 @@ func AddUser(user *User) (bool, error) {
return false, nil
}
if user.PasswordType == "" && organization.PasswordType != "" {
user.PasswordType = organization.PasswordType
if user.PasswordType == "" || user.PasswordType == "plain" {
user.UpdateUserPassword(organization)
}
user.UpdateUserPassword(organization)
err = user.UpdateUserHash()
if err != nil {
return false, err
@@ -891,5 +888,13 @@ func (user *User) IsApplicationAdmin(application *Application) bool {
return false
}
return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin
return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin()
}
func (user *User) IsGlobalAdmin() bool {
if user == nil {
return false
}
return user.Owner == "built-in"
}

View File

@@ -18,6 +18,7 @@ import (
"bytes"
"fmt"
"io"
"mime"
"net/http"
"strings"
@@ -58,22 +59,15 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
if strings.Contains(contentType, "text/html") {
fileExtension = ".html"
} else {
switch contentType {
case "image/jpeg":
fileExtension = ".jpg"
case "image/png":
fileExtension = ".png"
case "image/gif":
fileExtension = ".gif"
case "image/vnd.microsoft.icon":
fileExtension = ".ico"
case "image/x-icon":
fileExtension = ".ico"
case "image/svg+xml":
fileExtension = ".svg"
default:
return nil, "", fmt.Errorf("unsupported content type: %s", contentType)
fileExtensions, err := mime.ExtensionsByType(contentType)
if err != nil {
return nil, "", err
}
if fileExtensions == nil {
return nil, "", fmt.Errorf("fileExtensions is nil")
}
fileExtension = fileExtensions[0]
}
// Save the image to a bytes.Buffer

View File

@@ -1,6 +1,8 @@
package object
import (
"fmt"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/errors"
"github.com/casdoor/casdoor/util"
@@ -17,11 +19,28 @@ func NewUserGroupEnforcer(enforcer *casbin.Enforcer) *UserGroupEnforcer {
}
}
func (e *UserGroupEnforcer) checkModel() error {
if _, ok := e.enforcer.GetModel()["g"]; !ok {
return fmt.Errorf("The Casbin model used by enforcer doesn't support RBAC (\"[role_definition]\" section not found), please use a RBAC enabled Casbin model for the enforcer")
}
return nil
}
func (e *UserGroupEnforcer) AddGroupForUser(user string, group string) (bool, error) {
err := e.checkModel()
if err != nil {
return false, err
}
return e.enforcer.AddRoleForUser(user, GetGroupWithPrefix(group))
}
func (e *UserGroupEnforcer) AddGroupsForUser(user string, groups []string) (bool, error) {
err := e.checkModel()
if err != nil {
return false, err
}
g := make([]string, len(groups))
for i, group := range groups {
g[i] = GetGroupWithPrefix(group)
@@ -30,14 +49,29 @@ func (e *UserGroupEnforcer) AddGroupsForUser(user string, groups []string) (bool
}
func (e *UserGroupEnforcer) DeleteGroupForUser(user string, group string) (bool, error) {
err := e.checkModel()
if err != nil {
return false, err
}
return e.enforcer.DeleteRoleForUser(user, GetGroupWithPrefix(group))
}
func (e *UserGroupEnforcer) DeleteGroupsForUser(user string) (bool, error) {
err := e.checkModel()
if err != nil {
return false, err
}
return e.enforcer.DeleteRolesForUser(user)
}
func (e *UserGroupEnforcer) GetGroupsForUser(user string) ([]string, error) {
err := e.checkModel()
if err != nil {
return nil, err
}
groups, err := e.enforcer.GetRolesForUser(user)
for i, group := range groups {
groups[i] = GetGroupWithoutPrefix(group)
@@ -46,6 +80,11 @@ func (e *UserGroupEnforcer) GetGroupsForUser(user string) ([]string, error) {
}
func (e *UserGroupEnforcer) GetAllUsersByGroup(group string) ([]string, error) {
err := e.checkModel()
if err != nil {
return nil, err
}
users, err := e.enforcer.GetUsersForRole(GetGroupWithPrefix(group))
if err != nil {
if err == errors.ERR_NAME_NOT_FOUND {
@@ -65,13 +104,17 @@ func GetGroupWithoutPrefix(group string) string {
}
func (e *UserGroupEnforcer) GetUserNamesByGroupName(groupName string) ([]string, error) {
var names []string
err := e.checkModel()
if err != nil {
return nil, err
}
userIds, err := e.GetAllUsersByGroup(groupName)
if err != nil {
return nil, err
}
names := []string{}
for _, userId := range userIds {
_, name := util.GetOwnerAndNameFromIdNoCheck(userId)
names = append(names, name)
@@ -81,7 +124,12 @@ func (e *UserGroupEnforcer) GetUserNamesByGroupName(groupName string) ([]string,
}
func (e *UserGroupEnforcer) UpdateGroupsForUser(user string, groups []string) (bool, error) {
_, err := e.DeleteGroupsForUser(user)
err := e.checkModel()
if err != nil {
return false, err
}
_, err = e.DeleteGroupsForUser(user)
if err != nil {
return false, err
}

View File

@@ -73,8 +73,8 @@ func parseListItem(lines *[]string, i int) []string {
return trimmedItems
}
func UploadUsers(owner string, fileId string) (bool, error) {
table := xlsx.ReadXlsxFile(fileId)
func UploadUsers(owner string, path string) (bool, error) {
table := xlsx.ReadXlsxFile(path)
oldUserMap, err := getUserMap(owner)
if err != nil {
@@ -124,15 +124,14 @@ func UploadUsers(owner string, fileId string) (bool, error) {
IsDefaultAvatar: false,
IsOnline: parseLineItemBool(&line, 31),
IsAdmin: parseLineItemBool(&line, 32),
IsGlobalAdmin: parseLineItemBool(&line, 33),
IsForbidden: parseLineItemBool(&line, 34),
IsDeleted: parseLineItemBool(&line, 35),
SignupApplication: parseLineItem(&line, 36),
IsForbidden: parseLineItemBool(&line, 33),
IsDeleted: parseLineItemBool(&line, 34),
SignupApplication: parseLineItem(&line, 35),
Hash: "",
PreHash: "",
CreatedIp: parseLineItem(&line, 37),
LastSigninTime: parseLineItem(&line, 38),
LastSigninIp: parseLineItem(&line, 39),
CreatedIp: parseLineItem(&line, 36),
LastSigninTime: parseLineItem(&line, 37),
LastSigninIp: parseLineItem(&line, 38),
Ldap: "",
Properties: map[string]string{},
}

View File

@@ -310,10 +310,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
item := GetAccountItemByName("Is admin", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.IsGlobalAdmin != newUser.IsGlobalAdmin {
item := GetAccountItemByName("Is global admin", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.IsForbidden != newUser.IsForbidden {
item := GetAccountItemByName("Is forbidden", organization)
itemsChanged = append(itemsChanged, item)
@@ -351,5 +348,5 @@ func (user *User) IsAdminUser() bool {
return false
}
return user.IsAdmin || user.IsGlobalAdmin
return user.IsAdmin || user.IsGlobalAdmin()
}

View File

@@ -19,12 +19,22 @@ import (
"strings"
"github.com/casdoor/casdoor/util"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
)
func sendWebhook(webhook *Webhook, record *Record) error {
func sendWebhook(webhook *Webhook, record *casvisorsdk.Record, extendedUser *User) error {
client := &http.Client{}
body := strings.NewReader(util.StructToJson(record))
type RecordEx struct {
casvisorsdk.Record
ExtendedUser *User `xorm:"-" json:"extendedUser"`
}
recordEx := &RecordEx{
Record: *record,
ExtendedUser: extendedUser,
}
body := strings.NewReader(util.StructToJson(recordEx))
req, err := http.NewRequest(webhook.Method, webhook.Url, body)
if err != nil {

View File

@@ -14,10 +14,7 @@
package pp
import (
"fmt"
"net/http"
)
import "net/http"
type DummyPaymentProvider struct{}
@@ -27,8 +24,7 @@ func NewDummyPaymentProvider() (*DummyPaymentProvider, error) {
}
func (pp *DummyPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
payUrl := fmt.Sprintf("/payments/%s/result", paymentName)
return payUrl, "", nil
return returnUrl, "", nil
}
func (pp *DummyPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {

View File

@@ -114,8 +114,8 @@ func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, auth
if err != nil {
return nil, err
}
if captureRsp.Code != paypal.Success {
errDetail := captureRsp.ErrorResponse.Details[0]
if detailRsp.Code != paypal.Success {
errDetail := detailRsp.ErrorResponse.Details[0]
switch errDetail.Issue {
case "ORDER_NOT_APPROVED":
notifyResult.PaymentStatus = PaymentStateCanceled

View File

@@ -14,9 +14,7 @@
package pp
import (
"net/http"
)
import "net/http"
type PaymentState string

View File

@@ -181,9 +181,6 @@ func initAPI() {
beego.Router("/api/login/oauth/access_token", &controllers.ApiController{}, "POST:GetOAuthToken")
beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken")
beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken")
beego.Router("/api/get-records", &controllers.ApiController{}, "GET:GetRecords")
beego.Router("/api/get-records-filter", &controllers.ApiController{}, "POST:GetRecordsByFilter")
beego.Router("/api/add-record", &controllers.ApiController{}, "POST:AddRecord")
beego.Router("/api/get-sessions", &controllers.ApiController{}, "GET:GetSessions")
beego.Router("/api/get-session", &controllers.ApiController{}, "GET:GetSingleSession")
@@ -248,6 +245,7 @@ func initAPI() {
beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail")
beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms")
beego.Router("/api/send-notification", &controllers.ApiController{}, "POST:SendNotification")
beego.Router("/api/webauthn/signup/begin", &controllers.ApiController{}, "Get:WebAuthnSignupBegin")
beego.Router("/api/webauthn/signup/finish", &controllers.ApiController{}, "Post:WebAuthnSignupFinish")
@@ -275,7 +273,7 @@ func initAPI() {
beego.Router("/cas/:organization/:application/proxy", &controllers.RootController{}, "GET:CasProxy")
beego.Router("/cas/:organization/:application/validate", &controllers.RootController{}, "GET:CasValidate")
beego.Router("/cas/:organization/:application/p3/serviceValidate", &controllers.RootController{}, "GET:CasP3ServiceAndProxyValidate")
beego.Router("/cas/:organization/:application/p3/proxyValidate", &controllers.RootController{}, "GET:CasP3ServiceAndProxyValidate")
beego.Router("/cas/:organization/:application/p3/serviceValidate", &controllers.RootController{}, "GET:CasP3ServiceValidate")
beego.Router("/cas/:organization/:application/p3/proxyValidate", &controllers.RootController{}, "GET:CasP3ProxyValidate")
beego.Router("/cas/:organization/:application/samlValidate", &controllers.RootController{}, "POST:SamlValidate")
}

View File

@@ -16,6 +16,7 @@ package routers
import (
"compress/gzip"
"fmt"
"io"
"net/http"
"os"
@@ -59,6 +60,13 @@ func StaticFilter(ctx *context.Context) {
path = "web/build/index.html"
}
if !util.FileExist(path) {
dir, err := os.Getwd()
if err != nil {
panic(err)
}
dir = strings.ReplaceAll(dir, "\\", "/")
errorText := fmt.Sprintf("The Casdoor frontend HTML file: \"index.html\" was not found, it should be placed at: \"%s/web/build/index.html\". For more information, see: https://casdoor.org/docs/basic/server-installation/#frontend-1", dir)
http.ServeContent(ctx.ResponseWriter, ctx.Request, "Casdoor frontend has encountered error...", time.Now(), strings.NewReader(errorText))
return
}

View File

@@ -131,34 +131,6 @@
}
}
},
"/api/add-chat": {
"post": {
"tags": [
"Chat API"
],
"description": "add chat",
"operationId": "ApiController.AddChat",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the chat",
"required": true,
"schema": {
"$ref": "#/definitions/object.Chat"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/add-enforcer": {
"post": {
"tags": [
@@ -243,34 +215,6 @@
}
}
},
"/api/add-message": {
"post": {
"tags": [
"Message API"
],
"description": "add message",
"operationId": "ApiController.AddMessage",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the message",
"required": true,
"schema": {
"$ref": "#/definitions/object.Message"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/add-model": {
"post": {
"tags": [
@@ -1077,34 +1021,6 @@
}
}
},
"/api/delete-chat": {
"post": {
"tags": [
"Chat API"
],
"description": "delete chat",
"operationId": "ApiController.DeleteChat",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the chat",
"required": true,
"schema": {
"$ref": "#/definitions/object.Chat"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/delete-enforcer": {
"post": {
"tags": [
@@ -1189,34 +1105,6 @@
}
}
},
"/api/delete-message": {
"post": {
"tags": [
"Message API"
],
"description": "delete message",
"operationId": "ApiController.DeleteMessage",
"parameters": [
{
"in": "body",
"name": "body",
"description": "The details of the message",
"required": true,
"schema": {
"$ref": "#/definitions/object.Message"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/delete-mfa/": {
"post": {
"tags": [
@@ -1964,56 +1852,18 @@
}
}
},
"/api/get-chat": {
"/api/get-dashboard": {
"get": {
"tags": [
"Chat API"
],
"description": "get chat",
"operationId": "ApiController.GetChat",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the chat",
"required": true,
"type": "string"
}
"GetDashboard API"
],
"description": "get information of dashboard",
"operationId": "ApiController.GetDashboard",
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/object.Chat"
}
}
}
}
},
"/api/get-chats": {
"get": {
"tags": [
"Chat API"
],
"description": "get chats",
"operationId": "ApiController.GetChats",
"parameters": [
{
"in": "query",
"name": "owner",
"description": "The owner of chats",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Chat"
}
"$ref": "#/definitions/controllers.Response"
}
}
}
@@ -2319,87 +2169,6 @@
}
}
},
"/api/get-message": {
"get": {
"tags": [
"Message API"
],
"description": "get message",
"operationId": "ApiController.GetMessage",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the message",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/object.Message"
}
}
}
}
},
"/api/get-message-answer": {
"get": {
"tags": [
"Message API"
],
"description": "get message answer",
"operationId": "ApiController.GetMessageAnswer",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the message",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/object.Message"
}
}
}
}
},
"/api/get-messages": {
"get": {
"tags": [
"Message API"
],
"description": "get messages",
"operationId": "ApiController.GetMessages",
"parameters": [
{
"in": "query",
"name": "owner",
"description": "The owner of messages",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/object.Message"
}
}
}
}
}
},
"/api/get-model": {
"get": {
"tags": [
@@ -4481,41 +4250,6 @@
}
}
},
"/api/update-chat": {
"post": {
"tags": [
"Chat API"
],
"description": "update chat",
"operationId": "ApiController.UpdateChat",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the chat",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "body",
"description": "The details of the chat",
"required": true,
"schema": {
"$ref": "#/definitions/object.Chat"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/update-enforcer": {
"post": {
"tags": [
@@ -4614,41 +4348,6 @@
}
}
},
"/api/update-message": {
"post": {
"tags": [
"Message API"
],
"description": "update message",
"operationId": "ApiController.UpdateMessage",
"parameters": [
{
"in": "query",
"name": "id",
"description": "The id ( owner/name ) of the message",
"required": true,
"type": "string"
},
{
"in": "body",
"name": "body",
"description": "The details of the message",
"required": true,
"schema": {
"$ref": "#/definitions/object.Message"
}
}
],
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
}
},
"/api/update-model": {
"post": {
"tags": [
@@ -5431,6 +5130,14 @@
}
},
"definitions": {
"1183.0xc000639290.false": {
"title": "false",
"type": "object"
},
"1217.0xc0006392c0.false": {
"title": "false",
"type": "object"
},
"LaravelResponse": {
"title": "LaravelResponse",
"type": "object"
@@ -5484,16 +5191,10 @@
"type": "object",
"properties": {
"data": {
"additionalProperties": {
"description": "support string, struct or []struct",
"type": "string"
}
"$ref": "#/definitions/1183.0xc000639290.false"
},
"data2": {
"additionalProperties": {
"description": "support string, struct or []struct",
"type": "string"
}
"$ref": "#/definitions/1217.0xc0006392c0.false"
},
"msg": {
"type": "string"
@@ -5531,10 +5232,18 @@
"title": "JSONWebKey",
"type": "object"
},
"model.Model": {
"title": "Model",
"type": "object"
},
"object": {
"title": "object",
"type": "object"
},
"object.\u0026{197049 0xc000a2cd50 false}": {
"title": "\u0026{197049 0xc000a2cd50 false}",
"type": "object"
},
"object.AccountItem": {
"title": "AccountItem",
"type": "object",
@@ -5557,16 +5266,41 @@
"title": "Adapter",
"type": "object",
"properties": {
"Engine": {
"$ref": "#/definitions/xorm.Engine"
},
"dataSourceName": {
"createdTime": {
"type": "string"
},
"dbName": {
"database": {
"type": "string"
},
"driverName": {
"databaseType": {
"type": "string"
},
"host": {
"type": "string"
},
"name": {
"type": "string"
},
"owner": {
"type": "string"
},
"password": {
"type": "string"
},
"port": {
"type": "integer",
"format": "int64"
},
"table": {
"type": "string"
},
"tableNamePrefix": {
"type": "string"
},
"type": {
"type": "string"
},
"user": {
"type": "string"
}
}
@@ -5728,7 +5462,7 @@
"title": "CasbinRequest",
"type": "array",
"items": {
"$ref": "#/definitions/object.CasbinRequest"
"$ref": "#/definitions/object.\u0026{197049 0xc000a2cd50 false}"
}
},
"object.Cert": {
@@ -5778,52 +5512,6 @@
}
}
},
"object.Chat": {
"title": "Chat",
"type": "object",
"properties": {
"category": {
"type": "string"
},
"createdTime": {
"type": "string"
},
"displayName": {
"type": "string"
},
"messageCount": {
"type": "integer",
"format": "int64"
},
"name": {
"type": "string"
},
"organization": {
"type": "string"
},
"owner": {
"type": "string"
},
"type": {
"type": "string"
},
"updatedTime": {
"type": "string"
},
"user1": {
"type": "string"
},
"user2": {
"type": "string"
},
"users": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"object.Enforce": {
"title": "Enforce",
"type": "object"
@@ -5844,12 +5532,14 @@
"displayName": {
"type": "string"
},
"isEnabled": {
"type": "boolean"
},
"model": {
"type": "string"
},
"modelCfg": {
"additionalProperties": {
"type": "string"
}
},
"name": {
"type": "string"
},
@@ -6084,36 +5774,6 @@
}
}
},
"object.Message": {
"title": "Message",
"type": "object",
"properties": {
"author": {
"type": "string"
},
"chat": {
"type": "string"
},
"createdTime": {
"type": "string"
},
"name": {
"type": "string"
},
"organization": {
"type": "string"
},
"owner": {
"type": "string"
},
"replyTo": {
"type": "string"
},
"text": {
"type": "string"
}
}
},
"object.MfaItem": {
"title": "MfaItem",
"type": "object",
@@ -6169,9 +5829,6 @@
"displayName": {
"type": "string"
},
"isEnabled": {
"type": "boolean"
},
"modelText": {
"type": "string"
},
@@ -6349,6 +6006,24 @@
}
}
},
"object.Ormer": {
"title": "Ormer",
"type": "object",
"properties": {
"Engine": {
"$ref": "#/definitions/xorm.Engine"
},
"dataSourceName": {
"type": "string"
},
"dbName": {
"type": "string"
},
"driverName": {
"type": "string"
}
}
},
"object.Payment": {
"title": "Payment",
"type": "object",
@@ -6386,7 +6061,7 @@
"name": {
"type": "string"
},
"organization": {
"outOrderId": {
"type": "string"
},
"owner": {
@@ -6424,7 +6099,7 @@
"type": "string"
},
"state": {
"type": "string"
"$ref": "#/definitions/pp.PaymentState"
},
"tag": {
"type": "string"
@@ -7124,9 +6799,6 @@
"$ref": "#/definitions/object.TableColumn"
}
},
"tablePrimaryKey": {
"type": "string"
},
"type": {
"type": "string"
},
@@ -7145,6 +6817,9 @@
"isHashed": {
"type": "boolean"
},
"isKey": {
"type": "boolean"
},
"name": {
"type": "string"
},
@@ -7464,9 +7139,6 @@
"isForbidden": {
"type": "boolean"
},
"isGlobalAdmin": {
"type": "boolean"
},
"isOnline": {
"type": "boolean"
},
@@ -7692,6 +7364,9 @@
"vk": {
"type": "string"
},
"web3onboard": {
"type": "string"
},
"webauthnCredentials": {
"type": "array",
"items": {
@@ -7811,6 +7486,18 @@
}
}
},
"pp.PaymentState": {
"title": "PaymentState",
"type": "string",
"enum": [
"PaymentStatePaid = \"Paid\"",
"PaymentStateCreated = \"Created\"",
"PaymentStateCanceled = \"Canceled\"",
"PaymentStateTimeout = \"Timeout\"",
"PaymentStateError = \"Error\""
],
"example": "Paid"
},
"protocol.CredentialAssertion": {
"title": "CredentialAssertion",
"type": "object"
@@ -7871,6 +7558,10 @@
"xorm.Engine": {
"title": "Engine",
"type": "object"
},
"xormadapter.Adapter": {
"title": "Adapter",
"type": "object"
}
},
"securityDefinitions": {
@@ -7879,9 +7570,5 @@
"name": "Authorization",
"in": "header"
}
},
"externalDocs": {
"description": "Find out more about Casdoor",
"url": "https://casdoor.org/"
}
}

View File

@@ -85,24 +85,6 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-chat:
post:
tags:
- Chat API
description: add chat
operationId: ApiController.AddChat
parameters:
- in: body
name: body
description: The details of the chat
required: true
schema:
$ref: '#/definitions/object.Chat'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-enforcer:
post:
tags:
@@ -157,24 +139,6 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-message:
post:
tags:
- Message API
description: add message
operationId: ApiController.AddMessage
parameters:
- in: body
name: body
description: The details of the message
required: true
schema:
$ref: '#/definitions/object.Message'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/add-model:
post:
tags:
@@ -696,24 +660,6 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/delete-chat:
post:
tags:
- Chat API
description: delete chat
operationId: ApiController.DeleteChat
parameters:
- in: body
name: body
description: The details of the chat
required: true
schema:
$ref: '#/definitions/object.Chat'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/delete-enforcer:
post:
tags:
@@ -768,24 +714,6 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/delete-message:
post:
tags:
- Message API
description: delete message
operationId: ApiController.DeleteMessage
parameters:
- in: body
name: body
description: The details of the message
required: true
schema:
$ref: '#/definitions/object.Message'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/delete-mfa/:
post:
tags:
@@ -1271,42 +1199,17 @@ paths:
type: array
items:
$ref: '#/definitions/object.Cert'
/api/get-chat:
/api/get-dashboard:
get:
tags:
- Chat API
description: get chat
operationId: ApiController.GetChat
parameters:
- in: query
name: id
description: The id ( owner/name ) of the chat
required: true
type: string
- GetDashboard API
description: get information of dashboard
operationId: ApiController.GetDashboard
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/object.Chat'
/api/get-chats:
get:
tags:
- Chat API
description: get chats
operationId: ApiController.GetChats
parameters:
- in: query
name: owner
description: The owner of chats
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Chat'
$ref: '#/definitions/controllers.Response'
/api/get-default-application:
get:
tags:
@@ -1503,59 +1406,6 @@ paths:
type: array
items:
$ref: '#/definitions/object.Ldap'
/api/get-message:
get:
tags:
- Message API
description: get message
operationId: ApiController.GetMessage
parameters:
- in: query
name: id
description: The id ( owner/name ) of the message
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/object.Message'
/api/get-message-answer:
get:
tags:
- Message API
description: get message answer
operationId: ApiController.GetMessageAnswer
parameters:
- in: query
name: id
description: The id ( owner/name ) of the message
required: true
type: string
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/object.Message'
/api/get-messages:
get:
tags:
- Message API
description: get messages
operationId: ApiController.GetMessages
parameters:
- in: query
name: owner
description: The owner of messages
required: true
type: string
responses:
"200":
description: The Response object
schema:
type: array
items:
$ref: '#/definitions/object.Message'
/api/get-model:
get:
tags:
@@ -2925,29 +2775,6 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/update-chat:
post:
tags:
- Chat API
description: update chat
operationId: ApiController.UpdateChat
parameters:
- in: query
name: id
description: The id ( owner/name ) of the chat
required: true
type: string
- in: body
name: body
description: The details of the chat
required: true
schema:
$ref: '#/definitions/object.Chat'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/update-enforcer:
post:
tags:
@@ -3012,29 +2839,6 @@ paths:
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/update-message:
post:
tags:
- Message API
description: update message
operationId: ApiController.UpdateMessage
parameters:
- in: query
name: id
description: The id ( owner/name ) of the message
required: true
type: string
- in: body
name: body
description: The details of the message
required: true
schema:
$ref: '#/definitions/object.Message'
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
/api/update-model:
post:
tags:
@@ -3549,6 +3353,12 @@ paths:
schema:
$ref: '#/definitions/controllers.Response'
definitions:
1183.0xc000639290.false:
title: "false"
type: object
1217.0xc0006392c0.false:
title: "false"
type: object
LaravelResponse:
title: LaravelResponse
type: object
@@ -3588,13 +3398,9 @@ definitions:
type: object
properties:
data:
additionalProperties:
description: support string, struct or []struct
type: string
$ref: '#/definitions/1183.0xc000639290.false'
data2:
additionalProperties:
description: support string, struct or []struct
type: string
$ref: '#/definitions/1217.0xc0006392c0.false'
msg:
type: string
name:
@@ -3618,9 +3424,15 @@ definitions:
jose.JSONWebKey:
title: JSONWebKey
type: object
model.Model:
title: Model
type: object
object:
title: object
type: object
object.&{197049 0xc000a2cd50 false}:
title: '&{197049 0xc000a2cd50 false}'
type: object
object.AccountItem:
title: AccountItem
type: object
@@ -3637,13 +3449,30 @@ definitions:
title: Adapter
type: object
properties:
Engine:
$ref: '#/definitions/xorm.Engine'
dataSourceName:
createdTime:
type: string
dbName:
database:
type: string
driverName:
databaseType:
type: string
host:
type: string
name:
type: string
owner:
type: string
password:
type: string
port:
type: integer
format: int64
table:
type: string
tableNamePrefix:
type: string
type:
type: string
user:
type: string
object.Application:
title: Application
@@ -3752,7 +3581,7 @@ definitions:
title: CasbinRequest
type: array
items:
$ref: '#/definitions/object.CasbinRequest'
$ref: '#/definitions/object.&{197049 0xc000a2cd50 false}'
object.Cert:
title: Cert
type: object
@@ -3785,37 +3614,6 @@ definitions:
type: string
type:
type: string
object.Chat:
title: Chat
type: object
properties:
category:
type: string
createdTime:
type: string
displayName:
type: string
messageCount:
type: integer
format: int64
name:
type: string
organization:
type: string
owner:
type: string
type:
type: string
updatedTime:
type: string
user1:
type: string
user2:
type: string
users:
type: array
items:
type: string
object.Enforce:
title: Enforce
type: object
@@ -3831,10 +3629,11 @@ definitions:
type: string
displayName:
type: string
isEnabled:
type: boolean
model:
type: string
modelCfg:
additionalProperties:
type: string
name:
type: string
owner:
@@ -3992,26 +3791,6 @@ definitions:
type: string
username:
type: string
object.Message:
title: Message
type: object
properties:
author:
type: string
chat:
type: string
createdTime:
type: string
name:
type: string
organization:
type: string
owner:
type: string
replyTo:
type: string
text:
type: string
object.MfaItem:
title: MfaItem
type: object
@@ -4050,8 +3829,6 @@ definitions:
type: string
displayName:
type: string
isEnabled:
type: boolean
modelText:
type: string
name:
@@ -4169,6 +3946,18 @@ definitions:
$ref: '#/definitions/object.ThemeData'
websiteUrl:
type: string
object.Ormer:
title: Ormer
type: object
properties:
Engine:
$ref: '#/definitions/xorm.Engine'
dataSourceName:
type: string
dbName:
type: string
driverName:
type: string
object.Payment:
title: Payment
type: object
@@ -4195,7 +3984,7 @@ definitions:
type: string
name:
type: string
organization:
outOrderId:
type: string
owner:
type: string
@@ -4221,7 +4010,7 @@ definitions:
returnUrl:
type: string
state:
type: string
$ref: '#/definitions/pp.PaymentState'
tag:
type: string
type:
@@ -4692,8 +4481,6 @@ definitions:
type: array
items:
$ref: '#/definitions/object.TableColumn'
tablePrimaryKey:
type: string
type:
type: string
user:
@@ -4706,6 +4493,8 @@ definitions:
type: string
isHashed:
type: boolean
isKey:
type: boolean
name:
type: string
type:
@@ -4920,8 +4709,6 @@ definitions:
type: boolean
isForbidden:
type: boolean
isGlobalAdmin:
type: boolean
isOnline:
type: boolean
kakao:
@@ -5073,6 +4860,8 @@ definitions:
type: string
vk:
type: string
web3onboard:
type: string
webauthnCredentials:
type: array
items:
@@ -5151,6 +4940,16 @@ definitions:
type: string
url:
type: string
pp.PaymentState:
title: PaymentState
type: string
enum:
- PaymentStatePaid = "Paid"
- PaymentStateCreated = "Created"
- PaymentStateCanceled = "Canceled"
- PaymentStateTimeout = "Timeout"
- PaymentStateError = "Error"
example: Paid
protocol.CredentialAssertion:
title: CredentialAssertion
type: object
@@ -5195,11 +4994,11 @@ definitions:
xorm.Engine:
title: Engine
type: object
xormadapter.Adapter:
title: Adapter
type: object
securityDefinitions:
AccessToken:
type: apiKey
name: Authorization
in: header
externalDocs:
description: Find out more about Casdoor
url: https://casdoor.org/

View File

@@ -34,16 +34,6 @@ func GetPath(path string) string {
return filepath.Dir(path)
}
func EnsureFileFolderExists(path string) {
p := GetPath(path)
if !FileExist(p) {
err := os.MkdirAll(p, os.ModePerm)
if err != nil {
panic(err)
}
}
}
func ListFiles(path string) []string {
res := []string{}

View File

@@ -14,8 +14,13 @@
package util
import "fmt"
import "io/ioutil"
func GetUploadXlsxPath(fileId string) string {
return fmt.Sprintf("tmpFiles/%s.xlsx", fileId)
file, err := ioutil.TempFile("", fileId)
if err != nil {
panic(err)
}
return file.Name()
}

View File

@@ -1,40 +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 util
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGetUploadXlsxPath(t *testing.T) {
scenarios := []struct {
description string
input string
expected interface{}
}{
{"scenery one", "casdoor", "tmpFiles/casdoor.xlsx"},
{"scenery two", "casbin", "tmpFiles/casbin.xlsx"},
{"scenery three", "loremIpsum", "tmpFiles/loremIpsum.xlsx"},
{"scenery four", "", "tmpFiles/.xlsx"},
}
for _, scenery := range scenarios {
t.Run(scenery.description, func(t *testing.T) {
actual := GetUploadXlsxPath(scenery.input)
assert.Equal(t, scenery.expected, actual, "The returned value not is expected")
})
}
}

View File

@@ -23,6 +23,7 @@ import (
"math/rand"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
@@ -315,3 +316,13 @@ func ParseIdToString(input interface{}) (string, error) {
return "", fmt.Errorf("unsupported id type: %T", input)
}
}
func GetValueFromDataSourceName(key string, dataSourceName string) string {
reg := regexp.MustCompile(key + "=([^ ]+)")
matches := reg.FindStringSubmatch(dataSourceName)
if len(matches) >= 2 {
return matches[1]
}
return ""
}

View File

@@ -10,9 +10,6 @@
"@ctrl/tinycolor": "^3.5.0",
"@emotion/react": "^11.10.5",
"@metamask/eth-sig-util": "^6.0.0",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"@web3-onboard/coinbase": "^2.2.5",
"@web3-onboard/core": "^2.20.5",
"@web3-onboard/frontier": "^2.0.4",
@@ -31,7 +28,6 @@
"core-js": "^3.25.0",
"craco-less": "^2.0.0",
"echarts": "^5.4.3",
"eslint-plugin-unused-imports": "^2.0.0",
"ethers": "5.6.9",
"file-saver": "^2.0.5",
"i18n-iso-countries": "^7.0.0",
@@ -56,7 +52,7 @@
},
"scripts": {
"start": "cross-env PORT=7001 craco start",
"build": "craco --max_old_space_size=4096 build",
"build": "craco build",
"test": "craco test",
"eject": "craco eject",
"crowdin:sync": "crowdin upload && crowdin download",
@@ -84,16 +80,21 @@
"devDependencies": {
"@babel/core": "^7.18.13",
"@babel/eslint-parser": "^7.18.9",
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-react": "^7.18.6",
"cross-env": "^7.0.3",
"cypress": "^12.5.1",
"eslint": "8.22.0",
"eslint-plugin-react": "^7.31.1",
"eslint-plugin-unused-imports": "^2.0.0",
"husky": "^4.3.8",
"lint-staged": "^13.0.3",
"stylelint": "^14.11.0",
"stylelint-config-recommended-less": "^1.0.4",
"stylelint-config-standard": "^28.0.0"
"stylelint-config-standard": "^28.0.0",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2"
},
"lint-staged": {
"src/**/*.{css,less}": [

View File

@@ -15,6 +15,8 @@
import React, {Component} from "react";
import "./App.less";
import {Helmet} from "react-helmet";
import Dashboard from "./basic/Dashboard";
import ShortcutsPage from "./basic/ShortcutsPage";
import {MfaRuleRequired} from "./Setting";
import * as Setting from "./Setting";
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
@@ -43,7 +45,6 @@ import LdapEditPage from "./LdapEditPage";
import LdapSyncPage from "./LdapSyncPage";
import TokenListPage from "./TokenListPage";
import TokenEditPage from "./TokenEditPage";
import RecordListPage from "./RecordListPage";
import WebhookListPage from "./WebhookListPage";
import WebhookEditPage from "./WebhookEditPage";
import SyncerListPage from "./SyncerListPage";
@@ -70,7 +71,7 @@ import SessionListPage from "./SessionListPage";
import MfaSetupPage from "./auth/MfaSetupPage";
import SystemInfo from "./SystemInfo";
import AccountPage from "./account/AccountPage";
import HomePage from "./basic/HomePage";
import AppListPage from "./basic/AppListPage";
import CustomGithubCorner from "./common/CustomGithubCorner";
import * as Conf from "./Conf";
@@ -88,6 +89,7 @@ import ThemeSelect from "./common/select/ThemeSelect";
import OrganizationSelect from "./common/select/OrganizationSelect";
import {clearWeb3AuthToken} from "./auth/Web3Auth";
import AccountAvatar from "./account/AccountAvatar";
import OpenTour from "./common/OpenTour";
const {Header, Footer, Content} = Layout;
@@ -149,8 +151,8 @@ class App extends Component {
this.setState({
uri: uri,
});
if (uri === "/") {
this.setState({selectedMenuKey: "/"});
if (uri === "/" || uri.includes("/shortcuts") || uri.includes("/apps")) {
this.setState({selectedMenuKey: "/home"});
} else if (uri.includes("/organizations") || uri.includes("/trees") || uri.includes("/users") || uri.includes("/groups")) {
this.setState({selectedMenuKey: "/orgs"});
} else if (uri.includes("/applications") || uri.includes("/providers") || uri.includes("/resources") || uri.includes("/certs")) {
@@ -378,6 +380,7 @@ class App extends Component {
});
}} />
<LanguageSelect languages={this.state.account.organization.languages} />
<OpenTour />
{Setting.isAdminUser(this.state.account) && !Setting.isMobile() &&
<OrganizationSelect
initValue={Setting.getOrganization()}
@@ -401,7 +404,13 @@ class App extends Component {
return [];
}
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/", <HomeTwoTone />));
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/home", <HomeTwoTone />, [
Setting.getItem(<Link to="/">{i18next.t("general:Dashboard")}</Link>, "/"),
Setting.getItem(<Link to="/shortcuts">{i18next.t("general:Shortcuts")}</Link>, "/shortcuts"),
Setting.getItem(<Link to="/apps">{i18next.t("general:Apps")}</Link>, "/apps"),
].filter(item => {
return Setting.isLocalAdminUser(this.state.account);
})));
if (Setting.isLocalAdminUser(this.state.account)) {
if (Conf.ShowGithubCorner) {
@@ -439,10 +448,10 @@ class App extends Component {
}
})));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/records">{i18next.t("general:Logging & Auditing")}</Link>, "/logs", <WalletTwoTone />, [
Setting.getItem(<Link to="/records">{i18next.t("general:Records")}</Link>, "/records"),
Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>, "/tokens"),
res.push(Setting.getItem(<Link style={{color: "black"}} to="/sessions">{i18next.t("general:Logging & Auditing")}</Link>, "/logs", <WalletTwoTone />, [
Setting.getItem(<Link to="/sessions">{i18next.t("general:Sessions")}</Link>, "/sessions"),
Setting.getItem(<a target="_blank" rel="noreferrer" href={Conf.CasvisorUrl}>{i18next.t("general:Records")}</a>, "/records"),
Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>, "/tokens"),
]));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/products">{i18next.t("general:Business & Payments")}</Link>, "/business", <DollarTwoTone />, [
@@ -453,11 +462,17 @@ class App extends Component {
Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>, "/subscriptions"),
]));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/sysinfo">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone />, [
Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>, "/sysinfo"),
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks"),
Setting.getItem(<a target="_blank" rel="noreferrer" href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>, "/swagger")]));
if (Setting.isAdminUser(this.state.account)) {
res.push(Setting.getItem(<Link style={{color: "black"}} to="/sysinfo">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone />, [
Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>, "/sysinfo"),
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks"),
Setting.getItem(<a target="_blank" rel="noreferrer" href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>, "/swagger")]));
} else {
res.push(Setting.getItem(<Link style={{color: "black"}} to="/syncers">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone />, [
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks")]));
}
}
return res;
@@ -477,7 +492,9 @@ class App extends Component {
renderRouter() {
return (
<Switch>
<Route exact path="/" render={(props) => this.renderLoginIfNotLoggedIn(<HomePage account={this.state.account} {...props} />)} />
<Route exact path="/" render={(props) => this.renderLoginIfNotLoggedIn(<Dashboard account={this.state.account} {...props} />)} />
<Route exact path="/apps" render={(props) => this.renderLoginIfNotLoggedIn(<AppListPage account={this.state.account} {...props} />)} />
<Route exact path="/shortcuts" render={(props) => this.renderLoginIfNotLoggedIn(<ShortcutsPage account={this.state.account} {...props} />)} />
<Route exact path="/account" render={(props) => this.renderLoginIfNotLoggedIn(<AccountPage account={this.state.account} {...props} />)} />
<Route exact path="/organizations" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationListPage account={this.state.account} {...props} />)} />
<Route exact path="/organizations/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationEditPage account={this.state.account} onChangeTheme={this.setTheme} {...props} />)} />
@@ -527,7 +544,6 @@ class App extends Component {
<Route exact path="/payments" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentListPage account={this.state.account} {...props} />)} />
<Route exact path="/payments/:organizationName/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} />
<Route exact path="/payments/:organizationName/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
<Route exact path="/records" render={(props) => this.renderLoginIfNotLoggedIn(<RecordListPage account={this.state.account} {...props} />)} />
<Route exact path="/mfa/setup" render={(props) => this.renderLoginIfNotLoggedIn(<MfaSetupPage account={this.state.account} onfinish={() => this.setState({requiredEnableMfa: false})} {...props} />)} />
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
<Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} />
@@ -555,9 +571,7 @@ class App extends Component {
renderContent() {
const onClick = ({key}) => {
if (key === "/swagger") {
window.open(Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger", "_blank");
} else {
if (key !== "/swagger" && key !== "/records") {
if (this.state.requiredEnableMfa) {
Setting.showMessage("info", "Please enable MFA first!");
} else {
@@ -649,7 +663,8 @@ class App extends Component {
window.location.pathname.startsWith("/result") ||
window.location.pathname.startsWith("/cas") ||
window.location.pathname.startsWith("/auto-signup") ||
window.location.pathname.startsWith("/select-plan");
window.location.pathname.startsWith("/select-plan") ||
window.location.pathname.startsWith("/buy-plan");
}
renderPage() {

View File

@@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {Button, Card, Col, ConfigProvider, Input, Popover, Radio, Result, Row, Select, Switch, Upload} from "antd";
import {Button, Card, Col, ConfigProvider, Input, List, Popover, Radio, Result, Row, Select, Space, Switch, Upload} from "antd";
import {CopyOutlined, LinkOutlined, UploadOutlined} from "@ant-design/icons";
import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as CertBackend from "./backend/CertBackend";
@@ -138,6 +138,10 @@ class ApplicationEditPage extends React.Component {
application.tags = [];
}
if (application.invitationCodes === null) {
application.invitationCodes = [];
}
this.setState({
application: application,
});
@@ -201,7 +205,6 @@ class ApplicationEditPage extends React.Component {
updateApplicationField(key, value) {
value = this.parseApplicationField(key, value);
const application = this.state.application;
application[key] = value;
this.setState({
@@ -813,18 +816,68 @@ class ApplicationEditPage extends React.Component {
</Row>
{
!this.state.application.enableSignUp ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Signup items"), i18next.t("application:Signup items - Tooltip"))} :
</Col>
<Col span={22} >
<SignupTable
title={i18next.t("application:Signup items")}
table={this.state.application.signupItems}
onUpdateTable={(value) => {this.updateApplicationField("signupItems", value);}}
/>
</Col>
</Row>
<React.Fragment>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Signup items"), i18next.t("application:Signup items - Tooltip"))} :
</Col>
<Col span={22} >
<SignupTable
title={i18next.t("application:Signup items")}
table={this.state.application.signupItems}
onUpdateTable={(value) => {
this.updateApplicationField("signupItems", value);
}}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Invitation code"), i18next.t("application:Invitation code - Tooltip"))} :
</Col>
<Col span={22} >
<List
header={
<Button type="primary" onClick={() => {
this.updateApplicationField("invitationCodes", Setting.addRow(this.state.application.invitationCodes, Setting.getRandomName()));
}
}>
{i18next.t("general:Add")}
</Button>
}
dataSource={this.state.application.invitationCodes.map(code => {
return {code: code};
})}
renderItem={(item, index) => (
<List.Item key={index}>
<Space>
<Input value={item.code} onChange={e => {
const invitationCodes = [...this.state.application.invitationCodes];
invitationCodes[index] = e.target.value;
this.updateApplicationField("invitationCodes", invitationCodes);
}} />
</Space>
<Space>
<Button icon={<CopyOutlined />} onClick={() => {
copy(item.code);
Setting.showMessage("success", i18next.t("application:Invitation code copied to clipboard successfully"));
}
}>
{i18next.t("general:Copy")}
</Button>
<Button type="primary" danger onClick={() => {
this.updateApplicationField("invitationCodes", this.state.application.invitationCodes.filter(code => code !== item.code));
}
}>
{i18next.t("general:Delete")}
</Button>
</Space>
</List.Item>
)}
/>
</Col>
</Row>
</React.Fragment>
)
}
<Row style={{marginTop: "20px"}} >

View File

@@ -13,11 +13,12 @@
// limitations under the License.
import React from "react";
import {Button, Input, Result, Space} from "antd";
import {Button, Input, Result, Space, Tour} from "antd";
import {SearchOutlined} from "@ant-design/icons";
import Highlighter from "react-highlight-words";
import i18next from "i18next";
import * as Setting from "./Setting";
import * as TourConfig from "./TourConfig";
class BaseListPage extends React.Component {
constructor(props) {
@@ -33,6 +34,7 @@ class BaseListPage extends React.Component {
searchText: "",
searchedColumn: "",
isAuthorized: true,
isTourVisible: TourConfig.getTourVisible(),
};
}
@@ -41,14 +43,23 @@ class BaseListPage extends React.Component {
this.fetch({pagination});
};
handleTourChange = () => {
this.setState({isTourVisible: TourConfig.getTourVisible()});
};
componentDidMount() {
window.addEventListener("storageOrganizationChanged", this.handleOrganizationChange);
window.addEventListener("storageTourChanged", this.handleTourChange);
if (!Setting.isAdminUser(this.props.account)) {
Setting.setOrganization("All");
}
}
componentWillUnmount() {
if (this.state.intervalId !== null) {
clearInterval(this.state.intervalId);
}
window.removeEventListener("storageTourChanged", this.handleTourChange);
window.removeEventListener("storageOrganizationChanged", this.handleOrganizationChange);
}
@@ -144,6 +155,37 @@ class BaseListPage extends React.Component {
});
};
setIsTourVisible = () => {
TourConfig.setIsTourVisible(false);
this.setState({isTourVisible: false});
};
getSteps = () => {
const nextPathName = TourConfig.getNextUrl();
const steps = TourConfig.getSteps();
steps.map((item, index) => {
if (!index) {
item.target = () => document.querySelector("table");
} else {
item.target = () => document.getElementById(item.id) || null;
}
if (index === steps.length - 1) {
item.nextButtonProps = {
children: TourConfig.getNextButtonChild(nextPathName),
};
}
});
return steps;
};
handleTourComplete = () => {
const nextPathName = TourConfig.getNextUrl();
if (nextPathName !== "") {
this.props.history.push("/" + nextPathName);
TourConfig.setIsTourVisible(true);
}
};
render() {
if (!this.state.isAuthorized) {
return (
@@ -161,6 +203,17 @@ class BaseListPage extends React.Component {
{
this.renderTable(this.state.data)
}
<Tour
open={this.state.isTourVisible}
onClose={this.setIsTourVisible}
steps={this.getSteps()}
indicatorsRender={(current, total) => (
<span>
{current + 1} / {total}
</span>
)}
onFinish={this.handleTourComplete}
/>
</div>
);
}

View File

@@ -14,6 +14,8 @@
export const DefaultApplication = "app-built-in";
export const CasvisorUrl = "https://github.com/casbin/casvisor";
export const ShowGithubCorner = false;
export const IsDemoMode = false;

View File

@@ -29,6 +29,8 @@ import PromptPage from "./auth/PromptPage";
import ResultPage from "./auth/ResultPage";
import CasLogout from "./auth/CasLogout";
import {authConfig} from "./auth/Auth";
import ProductBuyPage from "./ProductBuyPage";
import PaymentResultPage from "./PaymentResultPage";
class EntryPage extends React.Component {
constructor(props) {
@@ -108,7 +110,9 @@ class EntryPage extends React.Component {
<Route exact path="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage {...this.props} application={this.state.application} type={"cas"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />);}} />
<Route exact path="/select-plan/:owner/:pricingName" render={(props) => this.renderHomeIfLoggedIn(<PricingPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />)} />
<Route exact path="/select-plan/:owner/:pricingName" render={(props) => <PricingPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
</Switch>
</div>
);

View File

@@ -81,7 +81,6 @@ class OrganizationListPage extends BaseListPage {
{name: "Properties", visible: false, viewRule: "Admin", modifyRule: "Admin"},
{name: "Is online", visible: true, viewRule: "Admin", modifyRule: "Admin"},
{name: "Is admin", visible: true, viewRule: "Admin", modifyRule: "Admin"},
{name: "Is global admin", visible: true, viewRule: "Admin", modifyRule: "Admin"},
{name: "Is forbidden", visible: true, viewRule: "Admin", modifyRule: "Admin"},
{name: "Is deleted", visible: true, viewRule: "Admin", modifyRule: "Admin"},
{Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"},

View File

@@ -101,6 +101,21 @@ class PaymentListPage extends BaseListPage {
);
},
},
{
title: i18next.t("general:Organization"),
dataIndex: "owner",
key: "owner",
width: "120px",
sorter: true,
...this.getColumnSearchProps("owner"),
render: (text, record, index) => {
return (
<Link to={`/organizations/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Provider"),
dataIndex: "provider",
@@ -117,21 +132,6 @@ class PaymentListPage extends BaseListPage {
);
},
},
{
title: i18next.t("general:Organization"),
dataIndex: "owner",
key: "owner",
width: "120px",
sorter: true,
...this.getColumnSearchProps("owner"),
render: (text, record, index) => {
return (
<Link to={`/organizations/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:User"),
dataIndex: "user",
@@ -158,14 +158,6 @@ class PaymentListPage extends BaseListPage {
return Setting.getFormattedDate(text);
},
},
// {
// title: i18next.t("general:Display name"),
// dataIndex: 'displayName',
// key: 'displayName',
// width: '160px',
// sorter: true,
// ...this.getColumnSearchProps('displayName'),
// },
{
title: i18next.t("provider:Type"),
dataIndex: "type",
@@ -187,6 +179,13 @@ class PaymentListPage extends BaseListPage {
// width: '160px',
sorter: true,
...this.getColumnSearchProps("productDisplayName"),
render: (text, record, index) => {
return (
<Link to={`/products/${record.owner}/${record.productName}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("product:Price"),
@@ -265,7 +264,7 @@ class PaymentListPage extends BaseListPage {
value = params.type;
}
this.setState({loading: true});
PaymentBackend.getPayments(Setting.getRequestOrganization(this.props.account), Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
PaymentBackend.getPayments(Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
.then((res) => {
this.setState({
loading: false,

View File

@@ -15,17 +15,24 @@
import React from "react";
import {Button, Result, Spin} from "antd";
import * as PaymentBackend from "./backend/PaymentBackend";
import * as PricingBackend from "./backend/PricingBackend";
import * as SubscriptionBackend from "./backend/SubscriptionBackend";
import * as Setting from "./Setting";
import i18next from "i18next";
class PaymentResultPage extends React.Component {
constructor(props) {
super(props);
const params = new URLSearchParams(window.location.search);
this.state = {
classes: props,
paymentName: props.match.params.paymentName,
organizationName: props.match.params.organizationName,
owner: props.match?.params?.organizationName ?? props.match?.params?.owner ?? null,
paymentName: props.match?.params?.paymentName ?? null,
pricingName: props.pricingName ?? props.match?.params?.pricingName ?? null,
subscriptionName: params.get("subscription"),
payment: null,
pricing: props.pricing ?? null,
subscription: props.subscription ?? null,
timeout: null,
};
}
@@ -40,28 +47,77 @@ class PaymentResultPage extends React.Component {
}
}
getPayment() {
PaymentBackend.getPayment(this.state.organizationName, this.state.paymentName)
.then((res) => {
this.setState({
payment: res.data,
});
// window.console.log("payment=", res.data);
if (res.data.state === "Created") {
if (["PayPal", "Stripe"].includes(res.data.type)) {
this.setState({
timeout: setTimeout(() => {
PaymentBackend.notifyPayment(this.state.organizationName, this.state.paymentName)
.then((res) => {
this.getPayment();
});
}, 1000),
});
} else {
this.setState({timeout: setTimeout(() => this.getPayment(), 1000)});
}
}
setStateAsync(state) {
return new Promise((resolve, reject) => {
this.setState(state, () => {
resolve();
});
});
}
onUpdatePricing(pricing) {
this.props.onUpdatePricing(pricing);
}
async getPayment() {
if (!(this.state.owner && (this.state.paymentName || (this.state.pricingName && this.state.subscriptionName)))) {
return ;
}
try {
// loading price & subscription
if (this.state.pricingName && this.state.subscriptionName) {
if (!this.state.pricing) {
const res = await PricingBackend.getPricing(this.state.owner, this.state.pricingName);
if (res.status !== "ok") {
throw new Error(res.msg);
}
const pricing = res.data;
await this.setStateAsync({
pricing: pricing,
});
}
if (!this.state.subscription) {
const res = await SubscriptionBackend.getSubscription(this.state.owner, this.state.subscriptionName);
if (res.status !== "ok") {
throw new Error(res.msg);
}
const subscription = res.data;
await this.setStateAsync({
subscription: subscription,
});
}
const paymentName = this.state.subscription.payment;
await this.setStateAsync({
paymentName: paymentName,
});
this.onUpdatePricing(this.state.pricing);
}
const res = await PaymentBackend.getPayment(this.state.owner, this.state.paymentName);
if (res.status !== "ok") {
throw new Error(res.msg);
}
const payment = res.data;
await this.setStateAsync({
payment: payment,
});
if (payment.state === "Created") {
if (["PayPal", "Stripe"].includes(payment.type)) {
this.setState({
timeout: setTimeout(async() => {
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
this.getPayment();
}, 1000),
});
} else {
this.setState({
timeout: setTimeout(() => this.getPayment(), 1000),
});
}
}
} catch (err) {
Setting.showMessage("error", err.message);
return;
}
}
goToPaymentUrl(payment) {
@@ -81,7 +137,7 @@ class PaymentResultPage extends React.Component {
if (payment.state === "Paid") {
return (
<div>
<div className="login-content">
{
Setting.renderHelmet(payment)
}
@@ -101,7 +157,7 @@ class PaymentResultPage extends React.Component {
);
} else if (payment.state === "Created") {
return (
<div>
<div className="login-content">
{
Setting.renderHelmet(payment)
}
@@ -117,7 +173,7 @@ class PaymentResultPage extends React.Component {
);
} else if (payment.state === "Canceled") {
return (
<div>
<div className="login-content">
{
Setting.renderHelmet(payment)
}
@@ -137,7 +193,7 @@ class PaymentResultPage extends React.Component {
);
} else if (payment.state === "Timeout") {
return (
<div>
<div className="login-content">
{
Setting.renderHelmet(payment)
}
@@ -157,7 +213,7 @@ class PaymentResultPage extends React.Component {
);
} else {
return (
<div>
<div className="login-content">
{
Setting.renderHelmet(payment)
}

View File

@@ -110,11 +110,12 @@ class PermissionListPage extends BaseListPage {
return (
<Upload {...props}>
<Button type="primary" size="small">
<Button id="upload-button" type="primary" size="small">
<UploadOutlined /> {i18next.t("user:Upload (.xlsx)")}
</Button></Upload>
);
}
renderTable(permissions) {
const columns = [
// https://github.com/ant-design/ant-design/issues/22184
@@ -361,7 +362,7 @@ class PermissionListPage extends BaseListPage {
title={() => (
<div>
{i18next.t("general:Permissions")}&nbsp;&nbsp;&nbsp;&nbsp;
<Button style={{marginRight: "5px"}} type="primary" size="small" onClick={this.addPermission.bind(this)}>{i18next.t("general:Add")}</Button>
<Button id="add-button" style={{marginRight: "5px"}} type="primary" size="small" onClick={this.addPermission.bind(this)}>{i18next.t("general:Add")}</Button>
{
this.renderPermissionUpload()
}

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