Compare commits

...

62 Commits

Author SHA1 Message Date
4123d47174 feat: callback will jump to blank page when from param start with "http" (#2778) 2024-03-06 01:07:52 +08:00
fbdd5a926d Fix normal user my-account page blank bug 2024-03-06 01:07:28 +08:00
92b6fda0f6 feat: support more objects in init_data JSON (#2776) 2024-03-05 23:41:46 +08:00
6a7ac35e65 fix: fix wechat media account can not bind issue (#2774)
* fix: fix wechat media account can not bind

* fix: improve code format
2024-03-05 18:46:28 +08:00
fc137b9f76 feat: fix custom JS doesn't reload after refresh bug (#2773) 2024-03-05 15:03:25 +08:00
11dbd5ba9a fix: fix duplicated load bug of custom JS (#2771) 2024-03-05 00:09:37 +08:00
19942a8bd4 Add webhook.SingleOrgOnly 2024-03-04 21:14:52 +08:00
f9ee8a68cb Support Chrome extension redirecting 2024-03-04 18:31:56 +08:00
Ron
f241336ad7 feat: add OSON SMS provider (#2769)
* implemented SMS provider 'OSON SMS' for frontend

* feat: add 'OSON SMS' provider for frontend
2024-03-04 01:05:53 +08:00
8b64d113fb Upgrade go-sms-sender dependency to 0.20.0 2024-03-04 01:05:28 +08:00
a8800c4d5c fix: add missing / for style tag in signin items (#2768) 2024-03-03 23:46:57 +08:00
75fc9ab9f7 Improve GetMaskedApplication()'s logic 2024-03-03 22:01:49 +08:00
d06da76c3d feat: fix bug in /get-organization-applications API 2024-03-03 21:08:36 +08:00
bc399837cc Rename label to "Custom CSS" 2024-03-03 20:45:14 +08:00
265abfe102 fix: handle error in storage.GetStorageProvider() 2024-03-03 18:18:54 +08:00
12acb24dbc feat: add transaction pages (#2761) 2024-03-02 10:41:16 +08:00
ba1ddc7e50 fix: admin can modify user ID now 2024-02-28 18:07:53 +08:00
59e07a35aa Add balance to user 2024-02-28 16:54:30 +08:00
cabe830f55 feat: use dynamic import to load web3Auth (#2757)
* feat: use dynamic import to load web3Auth and success reduce the size of signin page to 720KB when web3 idp disabled

* feat: avoid frequent import in OAuthWidget.js which may cause e2e test EPIPE error

* feat: remove import may cause e2e error

* feat: remove import may cause e2e error

* feat: remove bug may cause e2e error

* feat: try use chrome in ci/cd instead of electron to solve e2e error
2024-02-28 15:58:04 +08:00
78af5daec3 feat: use resourcesToBackend to load i18n files (#2755) 2024-02-28 01:43:55 +08:00
6c76913f71 fix: Set default value for email and SMS rule to all instead of none (#2754) 2024-02-28 01:28:59 +08:00
5a0d1bcb6e Support login by user ID 2024-02-28 01:28:24 +08:00
37232faa07 feat: fix bug for missing SMS and Email provider in application 2024-02-27 22:54:35 +08:00
4d9c81ef96 Fix broken error messages 2024-02-27 22:48:33 +08:00
b0d87f60ae feat: use lazy load to load management pages (#2752) 2024-02-27 22:31:02 +08:00
a5499219d1 fix: refactor out ManagementPage.js from App.js (#2750)
* feat: basic separate

* feat: nearly fully separate

* feat: add License

* feat: full load application in /login url, lazy load in /login/oauth... etc

* fix: fix onChangeTheme error in organization edit page

* fix: revert lazy load
2024-02-27 18:49:23 +08:00
6a813a1f8c feat: fix headerHtml script not running bug (#2749)
* fix: fix custom head not exec <script> tag

* fix: fix create element bug
2024-02-26 20:21:07 +08:00
e4cf244cf8 fix: theme will fully restore after page reload (#2743)
* fix: theme will set to default after flush

* fix: use consume theme to ensure EntryPage will always use default themeAlgorithm

* fix: fix logo render, add try catch to handle
potential err cause by JSON.parse
2024-02-25 00:05:13 +08:00
f5a6415e57 feat: improve dark theme UI (#2742) 2024-02-24 20:11:42 +08:00
13e871043c fix: fix theme switch bug (#2741) 2024-02-24 16:56:12 +08:00
a8699d0b87 feat: use React routing to remove spin between signup and signin pages (#2740)
* fix: Regarding the color of loading

* fix: use goToLinkSoft and use same code format with result and forget psw

* fix: update signup url
2024-02-24 12:59:09 +08:00
6621d693de feat: revert "feat: use i18next-resources-to-backend to lazy load i18n" (#2739)
This reverts commit dc3131c683.
2024-02-23 23:38:49 +08:00
dc3131c683 feat: use i18next-resources-to-backend to lazy load i18n (#2738)
* feat: use i18next-resources-to-backend to lazy load i18n file

* feat: change source in yarn.lock
2024-02-23 22:35:59 +08:00
042a8d0ad6 feat: add rule for SMS and Email provider (#2733)
* add phonecoderule

* feat:add phone code rule

* feat: add email rule

* fix: merge
2024-02-23 00:09:37 +08:00
44abfb3430 feat: support custom header HTML in entry pages (#2731) 2024-02-22 17:56:47 +08:00
53b8424a1f feat: fix JSON typo in init_data.json template 2024-02-21 17:33:08 +08:00
23c2ba3a2b feat: support ssh key/pem file in DB syncer (#2727)
* feat: support connect database with ssh tunnel in syncer

* feat: improve i18n translate

* feat: improve code format and i18n
2024-02-21 17:27:37 +08:00
3a9ffedce4 feat: support phone and Email in /api/login/oauth/access_token API (#2725)
Phone Number supports for /api/login/oauth/access_token as username

 Closes: #2724
2024-02-21 17:27:24 +08:00
03f005389f feat: fix organizationChangeTrigger() and userChangeTrigger() bugs 2024-02-21 01:14:32 +08:00
69a8346d05 Remove "/auto-signup/oauth/authorize" path introduced in PR: #896 2024-02-20 17:40:39 +08:00
546512a0ea Fix getCasvisorApplication() 2024-02-20 13:45:03 +08:00
c4a307b9ec feat: add built-in "Records" pages back (#2720) 2024-02-20 13:28:29 +08:00
d731c3c934 feat: add regex support for account item (#2714)
* feat: add regex support for account item

* feat: use reflect to process user field

* fix: fix lint problem

* feat: improve code format and fix reflect error
2024-02-17 15:24:36 +08:00
4a68dd65cd Fix typo in renderFormItem() 2024-02-16 10:13:50 +08:00
d59148890e Improve error handling for CheckVerificationCode() 2024-02-16 08:53:56 +08:00
7f52755e32 feat: improve error messages 2024-02-16 01:13:34 +08:00
eaa6f50085 Add initial value for grantTypes 2024-02-15 23:18:23 +08:00
f35a5f9a47 feat: fix issue that admin cannot enable MFA for user (#2702) 2024-02-14 23:29:04 +08:00
7481b229a4 feat: show domain field for MinIO storage provider 2024-02-14 13:54:17 +08:00
39e485ae82 Fix SigninTable issue 2024-02-14 12:20:03 +08:00
764c64e67c Fix SigninTable CSS 2024-02-14 12:10:30 +08:00
e755a7331d Fix renderLink() 2024-02-14 09:45:21 +08:00
6d9d595f86 fix: Revert "fix: fix display bug in SigninTable" (#2700)
This reverts commit d52058d2ae.
2024-02-14 09:44:42 +08:00
d52058d2ae fix: fix display bug in SigninTable (#2698)
* fix: fix display bug in SigninTable

* fix: fix code bug

* feat: improve code format

* feat: improve code format
2024-02-14 09:26:51 +08:00
bcfbfc6947 Support "signinUrl" in forget page 2024-02-14 02:36:52 +08:00
75699c4a26 feat: improve code in getObject() 2024-02-13 23:50:21 +08:00
3e8bfb52a8 feat: add signin items table (#2695)
* feat: add signin items table

* fix:unable to login

* feat: improve code format

* fix: fix display err on signup link

* feat: improve display of sign up link
2024-02-13 23:12:40 +08:00
bbbd857a45 fix: fix bug that failed to run initApi adapter in docker (#2696) 2024-02-13 23:12:25 +08:00
498900df76 feat: allow dot in the username (like john.smith) (#2692) 2024-02-12 20:52:17 +08:00
7e3c1a6581 fix: improve goth code (#2693)
Signed-off-by: Dmitrii Aleksandrov <goodmobiledevices@gmail.com>
2024-02-12 20:51:58 +08:00
6e28043dba refactor: New Crowdin translations (#2648)
* refactor: New Crowdin translations by Github Action

* refactor: New Crowdin Backend translations by Github Action

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2024-02-12 18:54:31 +08:00
cb200687dc feat: fix GetUserByUserId() API crash issue 2024-02-12 18:51:55 +08:00
129 changed files with 5357 additions and 1257 deletions

View File

@ -108,6 +108,7 @@ jobs:
working-directory: ./web working-directory: ./web
- uses: cypress-io/github-action@v5 - uses: cypress-io/github-action@v5
with: with:
browser: chrome
start: yarn start start: yarn start
wait-on: 'http://localhost:7001' wait-on: 'http://localhost:7001'
wait-on-timeout: 210 wait-on-timeout: 210

View File

@ -71,7 +71,10 @@ func GetConfigInt64(key string) (int64, error) {
func GetConfigDataSourceName() string { func GetConfigDataSourceName() string {
dataSourceName := GetConfigString("dataSourceName") dataSourceName := GetConfigString("dataSourceName")
return ReplaceDataSourceNameByDocker(dataSourceName)
}
func ReplaceDataSourceNameByDocker(dataSourceName string) string {
runningInDocker := os.Getenv("RUNNING_IN_DOCKER") runningInDocker := os.Getenv("RUNNING_IN_DOCKER")
if runningInDocker == "true" { if runningInDocker == "true" {
// https://stackoverflow.com/questions/48546124/what-is-linux-equivalent-of-host-docker-internal // https://stackoverflow.com/questions/48546124/what-is-linux-equivalent-of-host-docker-internal
@ -81,7 +84,6 @@ func GetConfigDataSourceName() string {
dataSourceName = strings.ReplaceAll(dataSourceName, "localhost", "host.docker.internal") dataSourceName = strings.ReplaceAll(dataSourceName, "localhost", "host.docker.internal")
} }
} }
return dataSourceName return dataSourceName
} }
@ -108,13 +110,3 @@ func GetConfigBatchSize() int {
} }
return res return res
} }
func GetConfigRealDataSourceName(driverName string) string {
var dataSourceName string
if driverName != "mysql" {
dataSourceName = GetConfigDataSourceName()
} else {
dataSourceName = GetConfigDataSourceName() + GetConfigString("dbName")
}
return dataSourceName
}

View File

@ -131,7 +131,12 @@ func (c *ApiController) Signup() {
} }
if application.IsSignupItemVisible("Email") && application.GetSignupItemRule("Email") != "No verification" && authForm.Email != "" { if application.IsSignupItemVisible("Email") && application.GetSignupItemRule("Email") != "No verification" && authForm.Email != "" {
checkResult := object.CheckVerificationCode(authForm.Email, authForm.EmailCode, c.GetAcceptLanguage()) var checkResult *object.VerifyResult
checkResult, err = object.CheckVerificationCode(authForm.Email, authForm.EmailCode, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(c.T(err.Error()))
return
}
if checkResult.Code != object.VerificationSuccess { if checkResult.Code != object.VerificationSuccess {
c.ResponseError(checkResult.Msg) c.ResponseError(checkResult.Msg)
return return
@ -141,7 +146,13 @@ func (c *ApiController) Signup() {
var checkPhone string var checkPhone string
if application.IsSignupItemVisible("Phone") && application.GetSignupItemRule("Phone") != "No verification" && authForm.Phone != "" { if application.IsSignupItemVisible("Phone") && application.GetSignupItemRule("Phone") != "No verification" && authForm.Phone != "" {
checkPhone, _ = util.GetE164Number(authForm.Phone, authForm.CountryCode) checkPhone, _ = util.GetE164Number(authForm.Phone, authForm.CountryCode)
checkResult := object.CheckVerificationCode(checkPhone, authForm.PhoneCode, c.GetAcceptLanguage())
var checkResult *object.VerifyResult
checkResult, err = object.CheckVerificationCode(checkPhone, authForm.PhoneCode, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(c.T(err.Error()))
return
}
if checkResult.Code != object.VerificationSuccess { if checkResult.Code != object.VerificationSuccess {
c.ResponseError(checkResult.Msg) c.ResponseError(checkResult.Msg)
return return

View File

@ -177,7 +177,7 @@ func (c *ApiController) GetOrganizationApplications() {
return return
} }
applications, err = object.GetAllowedApplications(applications, userId) applications, err = object.GetAllowedApplications(applications, userId, c.GetAcceptLanguage())
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -194,13 +194,19 @@ func (c *ApiController) GetOrganizationApplications() {
} }
paginator := pagination.SetPaginator(c.Ctx, limit, count) paginator := pagination.SetPaginator(c.Ctx, limit, count)
application, err := object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder) applications, err := object.GetPaginationOrganizationApplications(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
applications := object.GetMaskedApplications(application, userId) applications, err = object.GetAllowedApplications(applications, userId, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
applications = object.GetMaskedApplications(applications, userId)
c.ResponseOk(applications, paginator.Nums()) c.ResponseOk(applications, paginator.Nums())
} }
} }

View File

@ -85,7 +85,7 @@ func (c *ApiController) Enforce() {
return return
} }
if permission == nil { if permission == nil {
c.ResponseError(fmt.Sprintf("permission: %s doesn't exist", permissionId)) c.ResponseError(fmt.Sprintf(c.T("permission:The permission: \"%s\" doesn't exist"), permissionId))
return return
} }
@ -209,7 +209,7 @@ func (c *ApiController) BatchEnforce() {
return return
} }
if permission == nil { if permission == nil {
c.ResponseError(fmt.Sprintf("permission: %s doesn't exist", permissionId)) c.ResponseError(fmt.Sprintf(c.T("permission:The permission: \"%s\" doesn't exist"), permissionId))
return return
} }

View File

@ -110,7 +110,7 @@ func (c *ApiController) MfaSetupVerify() {
return return
} }
config.Secret = secret.(string) config.Secret = secret.(string)
} else if mfaType == object.EmailType || mfaType == object.SmsType { } else if mfaType == object.SmsType {
dest := c.GetSession(MfaDestSession) dest := c.GetSession(MfaDestSession)
if dest == nil { if dest == nil {
c.ResponseError("destination is missing") c.ResponseError("destination is missing")
@ -123,6 +123,13 @@ func (c *ApiController) MfaSetupVerify() {
return return
} }
config.CountryCode = countryCode.(string) config.CountryCode = countryCode.(string)
} else if mfaType == object.EmailType {
dest := c.GetSession(MfaDestSession)
if dest == nil {
c.ResponseError("destination is missing")
return
}
config.Secret = dest.(string)
} }
mfaUtil := object.GetMfaUtil(mfaType, config) mfaUtil := object.GetMfaUtil(mfaType, config)
@ -175,19 +182,30 @@ func (c *ApiController) MfaSetupEnable() {
return return
} }
config.Secret = secret.(string) config.Secret = secret.(string)
} else if mfaType == object.EmailType || mfaType == object.SmsType { } else if mfaType == object.EmailType {
dest := c.GetSession(MfaDestSession) if user.Email == "" {
if dest == nil { dest := c.GetSession(MfaDestSession)
c.ResponseError("destination is missing") if dest == nil {
return c.ResponseError("destination is missing")
return
}
user.Email = dest.(string)
} }
config.Secret = dest.(string) } else if mfaType == object.SmsType {
countryCode := c.GetSession(MfaCountryCodeSession) if user.Phone == "" {
if countryCode == nil { dest := c.GetSession(MfaDestSession)
c.ResponseError("country code is missing") if dest == nil {
return c.ResponseError("destination is missing")
return
}
user.Phone = dest.(string)
countryCode := c.GetSession(MfaCountryCodeSession)
if countryCode == nil {
c.ResponseError("country code is missing")
return
}
user.CountryCode = countryCode.(string)
} }
config.CountryCode = countryCode.(string)
} }
recoveryCodes := c.GetSession(MfaRecoveryCodesSession) recoveryCodes := c.GetSession(MfaRecoveryCodesSession)
if recoveryCodes == nil { if recoveryCodes == nil {

123
controllers/record.go Normal file
View File

@ -0,0 +1,123 @@
// 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/casvisor/casvisor-go-sdk/casvisorsdk"
"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 := &casvisorsdk.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 := &casvisorsdk.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 casvisorsdk.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

@ -168,3 +168,20 @@ func (c *ApiController) RunSyncer() {
c.ResponseOk() c.ResponseOk()
} }
func (c *ApiController) TestSyncerDb() {
var syncer object.Syncer
err := json.Unmarshal(c.Ctx.Input.RequestBody, &syncer)
if err != nil {
c.ResponseError(err.Error())
return
}
err = object.TestSyncerDb(syncer)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk()
}

167
controllers/transaction.go Normal file
View File

@ -0,0 +1,167 @@
// Copyright 2024 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"
)
// GetTransactions
// @Title GetTransactions
// @Tag Transaction API
// @Description get transactions
// @Param owner query string true "The owner of transactions"
// @Success 200 {array} object.Transaction The Response object
// @router /get-transactions [get]
func (c *ApiController) GetTransactions() {
owner := c.Input().Get("owner")
limit := c.Input().Get("pageSize")
page := c.Input().Get("p")
field := c.Input().Get("field")
value := c.Input().Get("value")
sortField := c.Input().Get("sortField")
sortOrder := c.Input().Get("sortOrder")
if limit == "" || page == "" {
transactions, err := object.GetTransactions(owner)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(transactions)
} else {
limit := util.ParseInt(limit)
count, err := object.GetTransactionCount(owner, field, value)
if err != nil {
c.ResponseError(err.Error())
return
}
paginator := pagination.SetPaginator(c.Ctx, limit, count)
transactions, err := object.GetPaginationTransactions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(transactions, paginator.Nums())
}
}
// GetUserTransactions
// @Title GetUserTransaction
// @Tag Transaction API
// @Description get transactions for a user
// @Param owner query string true "The owner of transactions"
// @Param organization query string true "The organization of the user"
// @Param user query string true "The username of the user"
// @Success 200 {array} object.Transaction The Response object
// @router /get-user-transactions [get]
func (c *ApiController) GetUserTransactions() {
owner := c.Input().Get("owner")
user := c.Input().Get("user")
transactions, err := object.GetUserTransactions(owner, user)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(transactions)
}
// GetTransaction
// @Title GetTransaction
// @Tag Transaction API
// @Description get transaction
// @Param id query string true "The id ( owner/name ) of the transaction"
// @Success 200 {object} object.Transaction The Response object
// @router /get-transaction [get]
func (c *ApiController) GetTransaction() {
id := c.Input().Get("id")
transaction, err := object.GetTransaction(id)
if err != nil {
c.ResponseError(err.Error())
return
}
c.ResponseOk(transaction)
}
// UpdateTransaction
// @Title UpdateTransaction
// @Tag Transaction API
// @Description update transaction
// @Param id query string true "The id ( owner/name ) of the transaction"
// @Param body body object.Transaction true "The details of the transaction"
// @Success 200 {object} controllers.Response The Response object
// @router /update-transaction [post]
func (c *ApiController) UpdateTransaction() {
id := c.Input().Get("id")
var transaction object.Transaction
err := json.Unmarshal(c.Ctx.Input.RequestBody, &transaction)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateTransaction(id, &transaction))
c.ServeJSON()
}
// AddTransaction
// @Title AddTransaction
// @Tag Transaction API
// @Description add transaction
// @Param body body object.Transaction true "The details of the transaction"
// @Success 200 {object} controllers.Response The Response object
// @router /add-transaction [post]
func (c *ApiController) AddTransaction() {
var transaction object.Transaction
err := json.Unmarshal(c.Ctx.Input.RequestBody, &transaction)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddTransaction(&transaction))
c.ServeJSON()
}
// DeleteTransaction
// @Title DeleteTransaction
// @Tag Transaction API
// @Description delete transaction
// @Param body body object.Transaction true "The details of the transaction"
// @Success 200 {object} controllers.Response The Response object
// @router /delete-transaction [post]
func (c *ApiController) DeleteTransaction() {
var transaction object.Transaction
err := json.Unmarshal(c.Ctx.Input.RequestBody, &transaction)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.DeleteTransaction(&transaction))
c.ServeJSON()
}

View File

@ -156,6 +156,10 @@ func (c *ApiController) GetUser() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if userFromUserId == nil {
c.ResponseOk(nil)
return
}
id = util.GetId(userFromUserId.Owner, userFromUserId.Name) id = util.GetId(userFromUserId.Owner, userFromUserId.Name)
} }
@ -200,7 +204,7 @@ func (c *ApiController) GetUser() {
return return
} }
if organization == nil { if organization == nil {
c.ResponseError(fmt.Sprintf("the organization: %s is not found", owner)) c.ResponseError(fmt.Sprintf(c.T("auth:The organization: %s does not exist"), owner))
return return
} }

View File

@ -164,13 +164,13 @@ func (c *ApiController) SendVerificationCode() {
c.SetSession(MfaDestSession, vform.Dest) c.SetSession(MfaDestSession, vform.Dest)
} }
provider, err := application.GetEmailProvider() provider, err = application.GetEmailProvider(vform.Method)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if provider == nil { if provider == nil {
c.ResponseError(fmt.Sprintf("please add an Email provider to the \"Providers\" list for the application: %s", application.Name)) c.ResponseError(fmt.Sprintf(c.T("verification:please add an Email provider to the \"Providers\" list for the application: %s"), application.Name))
return return
} }
@ -210,13 +210,13 @@ func (c *ApiController) SendVerificationCode() {
vform.CountryCode = mfaProps.CountryCode vform.CountryCode = mfaProps.CountryCode
} }
provider, err := application.GetSmsProvider() provider, err = application.GetSmsProvider(vform.Method)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if provider == nil { if provider == nil {
c.ResponseError(fmt.Sprintf("please add a SMS provider to the \"Providers\" list for the application: %s", application.Name)) c.ResponseError(fmt.Sprintf(c.T("verification:please add a SMS provider to the \"Providers\" list for the application: %s"), application.Name))
return return
} }
@ -343,7 +343,12 @@ func (c *ApiController) ResetEmailOrPhone() {
} }
} }
if result := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage()); result.Code != object.VerificationSuccess { result, err := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(c.T(err.Error()))
return
}
if result.Code != object.VerificationSuccess {
c.ResponseError(result.Msg) c.ResponseError(result.Msg)
return return
} }
@ -425,16 +430,22 @@ func (c *ApiController) VerifyCode() {
} }
} }
if result := object.CheckVerificationCode(checkDest, authForm.Code, c.GetAcceptLanguage()); result.Code != object.VerificationSuccess { result, err := object.CheckVerificationCode(checkDest, authForm.Code, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(c.T(err.Error()))
return
}
if result.Code != object.VerificationSuccess {
c.ResponseError(result.Msg) c.ResponseError(result.Msg)
return return
} }
err = object.DisableVerificationCode(checkDest) err = object.DisableVerificationCode(checkDest)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
c.SetSession("verifiedCode", authForm.Code)
c.SetSession("verifiedCode", authForm.Code)
c.ResponseOk() c.ResponseOk()
} }

View File

@ -27,7 +27,10 @@ import (
) )
func deployStaticFiles(provider *object.Provider) { func deployStaticFiles(provider *object.Provider) {
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint) storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
if err != nil {
panic(err)
}
if storageProvider == nil { if storageProvider == nil {
panic(fmt.Sprintf("the provider type: %s is not supported", provider.Type)) panic(fmt.Sprintf("the provider type: %s is not supported", provider.Type))
} }

6
go.mod
View File

@ -9,10 +9,10 @@ require (
github.com/beego/beego v1.12.12 github.com/beego/beego v1.12.12
github.com/beevik/etree v1.1.0 github.com/beevik/etree v1.1.0
github.com/casbin/casbin/v2 v2.77.2 github.com/casbin/casbin/v2 v2.77.2
github.com/casdoor/go-sms-sender v0.19.0 github.com/casdoor/go-sms-sender v0.20.0
github.com/casdoor/gomail/v2 v2.0.1 github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/notify v0.45.0 github.com/casdoor/notify v0.45.0
github.com/casdoor/oss v1.5.0 github.com/casdoor/oss v1.6.0
github.com/casdoor/xorm-adapter/v3 v3.1.0 github.com/casdoor/xorm-adapter/v3 v3.1.0
github.com/casvisor/casvisor-go-sdk v1.0.3 github.com/casvisor/casvisor-go-sdk v1.0.3
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
@ -59,7 +59,7 @@ require (
github.com/xorm-io/core v0.7.4 github.com/xorm-io/core v0.7.4
github.com/xorm-io/xorm v1.1.6 github.com/xorm-io/xorm v1.1.6
github.com/yusufpapurcu/wmi v1.2.2 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect
golang.org/x/crypto v0.14.0 golang.org/x/crypto v0.19.0
golang.org/x/net v0.17.0 golang.org/x/net v0.17.0
golang.org/x/oauth2 v0.13.0 golang.org/x/oauth2 v0.13.0
google.golang.org/api v0.150.0 google.golang.org/api v0.150.0

20
go.sum
View File

@ -1083,14 +1083,14 @@ github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk= github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXyD9XPs= github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXyD9XPs=
github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc= github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc=
github.com/casdoor/go-sms-sender v0.19.0 h1:qVz7RLXx8aGgfzLUGvhNe6pbhxDqP2/qNY+XPepfpyQ= github.com/casdoor/go-sms-sender v0.20.0 h1:yLbCakV04DzzehhgBklOrSeCFjMwpfKBeemz9b+Y8OM=
github.com/casdoor/go-sms-sender v0.19.0/go.mod h1:cQs7qqohMJBgIVZebOCB8ko09naG1vzFJEH59VNIscs= github.com/casdoor/go-sms-sender v0.20.0/go.mod h1:cQs7qqohMJBgIVZebOCB8ko09naG1vzFJEH59VNIscs=
github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w= github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w=
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q= github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk= github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk=
github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ= github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ=
github.com/casdoor/oss v1.5.0 h1:mi1htaXR5fynskDry1S3wk+Dd2nRY1z1pVcnGsqMqP4= github.com/casdoor/oss v1.6.0 h1:IOWrGLJ+VO82qS796eaRnzFPPA1Sn3cotYTi7O/VIlQ=
github.com/casdoor/oss v1.5.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE= github.com/casdoor/oss v1.6.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk= github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM= github.com/casdoor/xorm-adapter/v3 v3.1.0/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 h1:TKJQWKnhtznEBhzLPEdNsp7nJK2GgdD8JsB0lFPMW7U=
@ -2117,8 +2117,9 @@ golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -2447,8 +2448,9 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -2465,8 +2467,9 @@ golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -2486,8 +2489,9 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "Die Anmeldeart \"Anmeldung mit Passwort\" ist für die Anwendung nicht aktiviert", "The login method: login with password is not enabled for the application": "Die Anmeldeart \"Anmeldung mit Passwort\" ist für die Anwendung nicht aktiviert",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert", "The provider: %s is not enabled for the application": "Der Anbieter: %s ist nicht für die Anwendung aktiviert",
"Unauthorized operation": "Nicht autorisierte Operation", "Unauthorized operation": "Nicht autorisierte Operation",
"Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s", "Unknown authentication type (not password or provider), form = %s": "Unbekannter Authentifizierungstyp (nicht Passwort oder Anbieter), Formular = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "Dem Benutzer ist der Zugang verboten, bitte kontaktieren Sie den Administrator", "The user is forbidden to sign in, please contact the administrator": "Dem Benutzer ist der Zugang verboten, bitte kontaktieren Sie den Administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.": "Der Benutzername darf nur alphanumerische Zeichen, Unterstriche oder Bindestriche enthalten, keine aufeinanderfolgenden Bindestriche oder Unterstriche haben und darf nicht mit einem Bindestrich oder Unterstrich beginnen oder enden.", "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.": "Der Benutzername darf nur alphanumerische Zeichen, Unterstriche oder Bindestriche enthalten, keine aufeinanderfolgenden Bindestriche oder Unterstriche haben und darf nicht mit einem Bindestrich oder Unterstrich beginnen oder enden.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Benutzername existiert bereits", "Username already exists": "Benutzername existiert bereits",
"Username cannot be an email address": "Benutzername kann keine E-Mail-Adresse sein", "Username cannot be an email address": "Benutzername kann keine E-Mail-Adresse sein",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Fehlender Parameter", "Missing parameter": "Fehlender Parameter",
"Please login first": "Bitte zuerst einloggen", "Please login first": "Bitte zuerst einloggen",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "Der Benutzer %s existiert nicht", "The user: %s doesn't exist": "Der Benutzer %s existiert nicht",
"don't support captchaProvider: ": "Unterstütze captchaProvider nicht:", "don't support captchaProvider: ": "Unterstütze captchaProvider nicht:",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "Das %s ist unveränderlich.", "The %s is immutable.": "Das %s ist unveränderlich.",
"Unknown modify rule %s.": "Unbekannte Änderungsregel %s." "Unknown modify rule %s.": "Unbekannte Änderungsregel %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Ungültige Anwendungs-ID", "Invalid application id": "Ungültige Anwendungs-ID",
"the provider: %s does not exist": "Der Anbieter %s existiert nicht" "the provider: %s does not exist": "Der Anbieter %s existiert nicht"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "Der Anbieter-Typ %s wird nicht unterstützt" "The provider type: %s is not supported": "Der Anbieter-Typ %s wird nicht unterstützt"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "Leerer clientId oder clientSecret",
"Grant_type: %s is not supported in this application": "Grant_type: %s wird von dieser Anwendung nicht unterstützt", "Grant_type: %s is not supported in this application": "Grant_type: %s wird von dieser Anwendung nicht unterstützt",
"Invalid application or wrong clientSecret": "Ungültige Anwendung oder falsches clientSecret", "Invalid application or wrong clientSecret": "Ungültige Anwendung oder falsches clientSecret",
"Invalid client_id": "Ungültige client_id", "Invalid client_id": "Ungültige client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unbekannter Typ", "Unknown type": "Unbekannter Typ",
"Wrong verification code!": "Falscher Bestätigungscode!", "Wrong verification code!": "Falscher Bestätigungscode!",
"You should verify your code in %d min!": "Du solltest deinen Code in %d Minuten verifizieren!", "You should verify your code in %d min!": "Du solltest deinen Code in %d Minuten verifizieren!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "Der Benutzer existiert nicht, bitte zuerst anmelden" "the user does not exist, please sign up first": "Der Benutzer existiert nicht, bitte zuerst anmelden"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "El método de inicio de sesión: inicio de sesión con contraseña no está habilitado para la aplicación", "The login method: login with password is not enabled for the application": "El método de inicio de sesión: inicio de sesión con contraseña no está habilitado para la aplicación",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "El proveedor: %s no está habilitado para la aplicación", "The provider: %s is not enabled for the application": "El proveedor: %s no está habilitado para la aplicación",
"Unauthorized operation": "Operación no autorizada", "Unauthorized operation": "Operación no autorizada",
"Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s", "Unknown authentication type (not password or provider), form = %s": "Tipo de autenticación desconocido (no es contraseña o proveedor), formulario = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "El usuario no está autorizado a iniciar sesión, por favor contacte al administrador", "The user is forbidden to sign in, please contact the administrator": "El usuario no está autorizado a iniciar sesión, por favor contacte al administrador",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.": "El nombre de usuario solo puede contener caracteres alfanuméricos, guiones bajos o guiones, no puede tener guiones o subrayados consecutivos, y no puede comenzar ni terminar con un guión o subrayado.", "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.": "El nombre de usuario solo puede contener caracteres alfanuméricos, guiones bajos o guiones, no puede tener guiones o subrayados consecutivos, y no puede comenzar ni terminar con un guión o subrayado.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "El nombre de usuario ya existe", "Username already exists": "El nombre de usuario ya existe",
"Username cannot be an email address": "Nombre de usuario no puede ser una dirección de correo electrónico", "Username cannot be an email address": "Nombre de usuario no puede ser una dirección de correo electrónico",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Parámetro faltante", "Missing parameter": "Parámetro faltante",
"Please login first": "Por favor, inicia sesión primero", "Please login first": "Por favor, inicia sesión primero",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "El usuario: %s no existe", "The user: %s doesn't exist": "El usuario: %s no existe",
"don't support captchaProvider: ": "No apoyo a captchaProvider", "don't support captchaProvider: ": "No apoyo a captchaProvider",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "El %s es inmutable.", "The %s is immutable.": "El %s es inmutable.",
"Unknown modify rule %s.": "Regla de modificación desconocida %s." "Unknown modify rule %s.": "Regla de modificación desconocida %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Identificación de aplicación no válida", "Invalid application id": "Identificación de aplicación no válida",
"the provider: %s does not exist": "El proveedor: %s no existe" "the provider: %s does not exist": "El proveedor: %s no existe"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "El tipo de proveedor: %s no es compatible" "The provider type: %s is not supported": "El tipo de proveedor: %s no es compatible"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "ClienteId o clienteSecret vacío",
"Grant_type: %s is not supported in this application": "El tipo de subvención: %s no es compatible con esta aplicación", "Grant_type: %s is not supported in this application": "El tipo de subvención: %s no es compatible con esta aplicación",
"Invalid application or wrong clientSecret": "Solicitud inválida o clientSecret incorrecto", "Invalid application or wrong clientSecret": "Solicitud inválida o clientSecret incorrecto",
"Invalid client_id": "Identificador de cliente no válido", "Invalid client_id": "Identificador de cliente no válido",
@ -146,6 +151,8 @@
"Unknown type": "Tipo desconocido", "Unknown type": "Tipo desconocido",
"Wrong verification code!": "¡Código de verificación incorrecto!", "Wrong verification code!": "¡Código de verificación incorrecto!",
"You should verify your code in %d min!": "¡Deberías verificar tu código en %d minutos!", "You should verify your code in %d min!": "¡Deberías verificar tu código en %d minutos!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "El usuario no existe, por favor regístrese primero" "the user does not exist, please sign up first": "El usuario no existe, por favor regístrese primero"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "La méthode de connexion : connexion avec mot de passe n'est pas activée pour l'application", "The login method: login with password is not enabled for the application": "La méthode de connexion : connexion avec mot de passe n'est pas activée pour l'application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "Le fournisseur :%s n'est pas activé pour l'application", "The provider: %s is not enabled for the application": "Le fournisseur :%s n'est pas activé pour l'application",
"Unauthorized operation": "Opération non autorisée", "Unauthorized operation": "Opération non autorisée",
"Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s", "Unknown authentication type (not password or provider), form = %s": "Type d'authentification inconnu (pas de mot de passe ou de fournisseur), formulaire = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "L'utilisateur est interdit de se connecter, veuillez contacter l'administrateur", "The user is forbidden to sign in, please contact the administrator": "L'utilisateur est interdit de se connecter, veuillez contacter l'administrateur",
"The user: %s doesn't exist in LDAP server": "L'utilisateur %s n'existe pas sur le serveur LDAP", "The user: %s doesn't exist in LDAP server": "L'utilisateur %s n'existe pas sur le serveur LDAP",
"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.": "Le nom d'utilisateur ne peut contenir que des caractères alphanumériques, des traits soulignés ou des tirets, ne peut pas avoir de tirets ou de traits soulignés consécutifs et ne peut pas commencer ou se terminer par un tiret ou un trait souligné.", "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.": "Le nom d'utilisateur ne peut contenir que des caractères alphanumériques, des traits soulignés ou des tirets, ne peut pas avoir de tirets ou de traits soulignés consécutifs et ne peut pas commencer ou se terminer par un tiret ou un trait souligné.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Nom d'utilisateur existe déjà", "Username already exists": "Nom d'utilisateur existe déjà",
"Username cannot be an email address": "Nom d'utilisateur ne peut pas être une adresse e-mail", "Username cannot be an email address": "Nom d'utilisateur ne peut pas être une adresse e-mail",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Paramètre manquant", "Missing parameter": "Paramètre manquant",
"Please login first": "Veuillez d'abord vous connecter", "Please login first": "Veuillez d'abord vous connecter",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "L'utilisateur : %s n'existe pas", "The user: %s doesn't exist": "L'utilisateur : %s n'existe pas",
"don't support captchaProvider: ": "ne prend pas en charge captchaProvider: ", "don't support captchaProvider: ": "ne prend pas en charge captchaProvider: ",
"this operation is not allowed in demo mode": "cette opération nest pas autorisée en mode démo" "this operation is not allowed in demo mode": "cette opération nest pas autorisée en mode démo"
@ -93,6 +96,9 @@
"The %s is immutable.": "Le %s est immuable.", "The %s is immutable.": "Le %s est immuable.",
"Unknown modify rule %s.": "Règle de modification inconnue %s." "Unknown modify rule %s.": "Règle de modification inconnue %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Identifiant d'application invalide", "Invalid application id": "Identifiant d'application invalide",
"the provider: %s does not exist": "Le fournisseur : %s n'existe pas" "the provider: %s does not exist": "Le fournisseur : %s n'existe pas"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "Le type de fournisseur : %s n'est pas pris en charge" "The provider type: %s is not supported": "Le type de fournisseur : %s n'est pas pris en charge"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "clientId ou clientSecret vide",
"Grant_type: %s is not supported in this application": "Type_de_subvention : %s n'est pas pris en charge dans cette application", "Grant_type: %s is not supported in this application": "Type_de_subvention : %s n'est pas pris en charge dans cette application",
"Invalid application or wrong clientSecret": "Application invalide ou clientSecret incorrect", "Invalid application or wrong clientSecret": "Application invalide ou clientSecret incorrect",
"Invalid client_id": "Identifiant de client invalide", "Invalid client_id": "Identifiant de client invalide",
@ -146,6 +151,8 @@
"Unknown type": "Type inconnu", "Unknown type": "Type inconnu",
"Wrong verification code!": "Mauvais code de vérification !", "Wrong verification code!": "Mauvais code de vérification !",
"You should verify your code in %d min!": "Vous devriez vérifier votre code en %d min !", "You should verify your code in %d min!": "Vous devriez vérifier votre code en %d min !",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "L'utilisateur n'existe pas, veuillez vous inscrire d'abord" "the user does not exist, please sign up first": "L'utilisateur n'existe pas, veuillez vous inscrire d'abord"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "Metode login: login dengan kata sandi tidak diaktifkan untuk aplikasi tersebut", "The login method: login with password is not enabled for the application": "Metode login: login dengan kata sandi tidak diaktifkan untuk aplikasi tersebut",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini", "The provider: %s is not enabled for the application": "Penyedia: %s tidak diaktifkan untuk aplikasi ini",
"Unauthorized operation": "Operasi tidak sah", "Unauthorized operation": "Operasi tidak sah",
"Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s", "Unknown authentication type (not password or provider), form = %s": "Jenis otentikasi tidak diketahui (bukan kata sandi atau pemberi), formulir = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "Pengguna dilarang masuk, silakan hubungi administrator", "The user is forbidden to sign in, please contact the administrator": "Pengguna dilarang masuk, silakan hubungi administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.": "Nama pengguna hanya bisa menggunakan karakter alfanumerik, garis bawah atau tanda hubung, tidak boleh memiliki dua tanda hubung atau garis bawah berurutan, dan tidak boleh diawali atau diakhiri dengan tanda hubung atau garis bawah.", "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.": "Nama pengguna hanya bisa menggunakan karakter alfanumerik, garis bawah atau tanda hubung, tidak boleh memiliki dua tanda hubung atau garis bawah berurutan, dan tidak boleh diawali atau diakhiri dengan tanda hubung atau garis bawah.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Nama pengguna sudah ada", "Username already exists": "Nama pengguna sudah ada",
"Username cannot be an email address": "Username tidak bisa menjadi alamat email", "Username cannot be an email address": "Username tidak bisa menjadi alamat email",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Parameter hilang", "Missing parameter": "Parameter hilang",
"Please login first": "Silahkan login terlebih dahulu", "Please login first": "Silahkan login terlebih dahulu",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "Pengguna: %s tidak ada", "The user: %s doesn't exist": "Pengguna: %s tidak ada",
"don't support captchaProvider: ": "Jangan mendukung captchaProvider:", "don't support captchaProvider: ": "Jangan mendukung captchaProvider:",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "%s tidak dapat diubah.", "The %s is immutable.": "%s tidak dapat diubah.",
"Unknown modify rule %s.": "Aturan modifikasi tidak diketahui %s." "Unknown modify rule %s.": "Aturan modifikasi tidak diketahui %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "ID aplikasi tidak valid", "Invalid application id": "ID aplikasi tidak valid",
"the provider: %s does not exist": "provider: %s tidak ada" "the provider: %s does not exist": "provider: %s tidak ada"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "Jenis penyedia: %s tidak didukung" "The provider type: %s is not supported": "Jenis penyedia: %s tidak didukung"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "Kosong clientId atau clientSecret",
"Grant_type: %s is not supported in this application": "Jenis grant (grant_type) %s tidak didukung dalam aplikasi ini", "Grant_type: %s is not supported in this application": "Jenis grant (grant_type) %s tidak didukung dalam aplikasi ini",
"Invalid application or wrong clientSecret": "Aplikasi tidak valid atau clientSecret salah", "Invalid application or wrong clientSecret": "Aplikasi tidak valid atau clientSecret salah",
"Invalid client_id": "Invalid client_id = ID klien tidak valid", "Invalid client_id": "Invalid client_id = ID klien tidak valid",
@ -146,6 +151,8 @@
"Unknown type": "Tipe tidak diketahui", "Unknown type": "Tipe tidak diketahui",
"Wrong verification code!": "Kode verifikasi salah!", "Wrong verification code!": "Kode verifikasi salah!",
"You should verify your code in %d min!": "Anda harus memverifikasi kode Anda dalam %d menit!", "You should verify your code in %d min!": "Anda harus memverifikasi kode Anda dalam %d menit!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "Pengguna tidak ada, silakan daftar terlebih dahulu" "the user does not exist, please sign up first": "Pengguna tidak ada, silakan daftar terlebih dahulu"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません", "The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません", "The provider: %s is not enabled for the application": "プロバイダー:%sはアプリケーションでは有効化されていません",
"Unauthorized operation": "不正操作", "Unauthorized operation": "不正操作",
"Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s", "Unknown authentication type (not password or provider), form = %s": "不明な認証タイプ(パスワードまたはプロバイダーではない)フォーム=%s",
@ -57,6 +58,7 @@
"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 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.": "ユーザー名には英数字、アンダースコア、ハイフンしか含めることができません。連続したハイフンまたはアンダースコアは不可であり、ハイフンまたはアンダースコアで始まるまたは終わることもできません。",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "ユーザー名はすでに存在しています", "Username already exists": "ユーザー名はすでに存在しています",
"Username cannot be an email address": "ユーザー名には電子メールアドレスを使用できません", "Username cannot be an email address": "ユーザー名には電子メールアドレスを使用できません",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "不足しているパラメーター", "Missing parameter": "不足しているパラメーター",
"Please login first": "最初にログインしてください", "Please login first": "最初にログインしてください",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "そのユーザー:%sは存在しません", "The user: %s doesn't exist": "そのユーザー:%sは存在しません",
"don't support captchaProvider: ": "captchaProviderをサポートしないでください", "don't support captchaProvider: ": "captchaProviderをサポートしないでください",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "%sは不変です。", "The %s is immutable.": "%sは不変です。",
"Unknown modify rule %s.": "未知の変更ルール%s。" "Unknown modify rule %s.": "未知の変更ルール%s。"
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "アプリケーションIDが無効です", "Invalid application id": "アプリケーションIDが無効です",
"the provider: %s does not exist": "プロバイダー%sは存在しません" "the provider: %s does not exist": "プロバイダー%sは存在しません"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "プロバイダータイプ:%sはサポートされていません" "The provider type: %s is not supported": "プロバイダータイプ:%sはサポートされていません"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "クライアントIDまたはクライアントシークレットが空です",
"Grant_type: %s is not supported in this application": "grant_type%sはこのアプリケーションでサポートされていません", "Grant_type: %s is not supported in this application": "grant_type%sはこのアプリケーションでサポートされていません",
"Invalid application or wrong clientSecret": "無効なアプリケーションまたは誤ったクライアントシークレットです", "Invalid application or wrong clientSecret": "無効なアプリケーションまたは誤ったクライアントシークレットです",
"Invalid client_id": "client_idが無効です", "Invalid client_id": "client_idが無効です",
@ -146,6 +151,8 @@
"Unknown type": "不明なタイプ", "Unknown type": "不明なタイプ",
"Wrong verification code!": "誤った検証コードです!", "Wrong verification code!": "誤った検証コードです!",
"You should verify your code in %d min!": "あなたは%d分であなたのコードを確認する必要があります", "You should verify your code in %d min!": "あなたは%d分であなたのコードを確認する必要があります",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "ユーザーは存在しません。まず登録してください" "the user does not exist, please sign up first": "ユーザーは存在しません。まず登録してください"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "어플리케이션에서는 암호를 사용한 로그인 방법이 활성화되어 있지 않습니다", "The login method: login with password is not enabled for the application": "어플리케이션에서는 암호를 사용한 로그인 방법이 활성화되어 있지 않습니다",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다", "The provider: %s is not enabled for the application": "제공자 %s은(는) 응용 프로그램에서 활성화되어 있지 않습니다",
"Unauthorized operation": "무단 조작", "Unauthorized operation": "무단 조작",
"Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s", "Unknown authentication type (not password or provider), form = %s": "알 수 없는 인증 유형(암호 또는 공급자가 아님), 폼 = %s",
@ -57,6 +58,7 @@
"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 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.": "사용자 이름은 알파벳, 숫자, 밑줄 또는 하이픈만 포함할 수 있으며, 연속된 하이픈 또는 밑줄을 가질 수 없으며, 하이픈 또는 밑줄로 시작하거나 끝날 수 없습니다.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "사용자 이름이 이미 존재합니다", "Username already exists": "사용자 이름이 이미 존재합니다",
"Username cannot be an email address": "사용자 이름은 이메일 주소가 될 수 없습니다", "Username cannot be an email address": "사용자 이름은 이메일 주소가 될 수 없습니다",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "누락된 매개변수", "Missing parameter": "누락된 매개변수",
"Please login first": "먼저 로그인 하십시오", "Please login first": "먼저 로그인 하십시오",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "사용자 %s는 존재하지 않습니다", "The user: %s doesn't exist": "사용자 %s는 존재하지 않습니다",
"don't support captchaProvider: ": "CaptchaProvider를 지원하지 마세요", "don't support captchaProvider: ": "CaptchaProvider를 지원하지 마세요",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "%s 는 변경할 수 없습니다.", "The %s is immutable.": "%s 는 변경할 수 없습니다.",
"Unknown modify rule %s.": "미확인 수정 규칙 %s." "Unknown modify rule %s.": "미확인 수정 규칙 %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "잘못된 애플리케이션 ID입니다", "Invalid application id": "잘못된 애플리케이션 ID입니다",
"the provider: %s does not exist": "제공자 %s가 존재하지 않습니다" "the provider: %s does not exist": "제공자 %s가 존재하지 않습니다"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "제공자 유형: %s은/는 지원되지 않습니다" "The provider type: %s is not supported": "제공자 유형: %s은/는 지원되지 않습니다"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "클라이언트 ID 또는 클라이언트 비밀번호가 비어 있습니다",
"Grant_type: %s is not supported in this application": "그랜트 유형: %s은(는) 이 어플리케이션에서 지원되지 않습니다", "Grant_type: %s is not supported in this application": "그랜트 유형: %s은(는) 이 어플리케이션에서 지원되지 않습니다",
"Invalid application or wrong clientSecret": "잘못된 어플리케이션 또는 올바르지 않은 클라이언트 시크릿입니다", "Invalid application or wrong clientSecret": "잘못된 어플리케이션 또는 올바르지 않은 클라이언트 시크릿입니다",
"Invalid client_id": "잘못된 클라이언트 ID입니다", "Invalid client_id": "잘못된 클라이언트 ID입니다",
@ -146,6 +151,8 @@
"Unknown type": "알 수 없는 유형", "Unknown type": "알 수 없는 유형",
"Wrong verification code!": "잘못된 인증 코드입니다!", "Wrong verification code!": "잘못된 인증 코드입니다!",
"You should verify your code in %d min!": "당신은 %d분 안에 코드를 검증해야 합니다!", "You should verify your code in %d min!": "당신은 %d분 안에 코드를 검증해야 합니다!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "사용자가 존재하지 않습니다. 먼저 회원 가입 해주세요" "the user does not exist, please sign up first": "사용자가 존재하지 않습니다. 먼저 회원 가입 해주세요"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "O %s é imutável.", "The %s is immutable.": "O %s é imutável.",
"Unknown modify rule %s.": "Regra de modificação %s desconhecida." "Unknown modify rule %s.": "Regra de modificação %s desconhecida."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Id do aplicativo inválido", "Invalid application id": "Id do aplicativo inválido",
"the provider: %s does not exist": "o provedor: %s não existe" "the provider: %s does not exist": "o provedor: %s não existe"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "ClientId ou clientSecret vazio",
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Aplicativo inválido ou clientSecret errado", "Invalid application or wrong clientSecret": "Aplicativo inválido ou clientSecret errado",
"Invalid client_id": "client_id inválido", "Invalid client_id": "client_id inválido",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения", "The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения", "The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
"Unauthorized operation": "Несанкционированная операция", "Unauthorized operation": "Несанкционированная операция",
"Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s", "Unknown authentication type (not password or provider), form = %s": "Неизвестный тип аутентификации (не пароль и не провайдер), форма = %s",
@ -57,6 +58,7 @@
"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": "Пользователь %s не существует на LDAP сервере", "The user: %s doesn't exist in LDAP server": "Пользователь %s не существует на LDAP сервере",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "Имя пользователя может состоять только из буквенно-цифровых символов, нижних подчеркиваний или дефисов, не может содержать последовательные дефисы или подчеркивания, а также не может начинаться или заканчиваться на дефис или подчеркивание.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "Имя пользователя может состоять только из буквенно-цифровых символов, нижних подчеркиваний или дефисов, не может содержать последовательные дефисы или подчеркивания, а также не может начинаться или заканчиваться на дефис или подчеркивание.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Имя пользователя уже существует", "Username already exists": "Имя пользователя уже существует",
"Username cannot be an email address": "Имя пользователя не может быть адресом электронной почты", "Username cannot be an email address": "Имя пользователя не может быть адресом электронной почты",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Отсутствующий параметр", "Missing parameter": "Отсутствующий параметр",
"Please login first": "Пожалуйста, сначала войдите в систему", "Please login first": "Пожалуйста, сначала войдите в систему",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "Пользователь %s не существует", "The user: %s doesn't exist": "Пользователь %s не существует",
"don't support captchaProvider: ": "неподдерживаемый captchaProvider: ", "don't support captchaProvider: ": "неподдерживаемый captchaProvider: ",
"this operation is not allowed in demo mode": "эта операция не разрешена в демо-режиме" "this operation is not allowed in demo mode": "эта операция не разрешена в демо-режиме"
@ -93,6 +96,9 @@
"The %s is immutable.": "%s неизменяемый.", "The %s is immutable.": "%s неизменяемый.",
"Unknown modify rule %s.": "Неизвестное изменение правила %s." "Unknown modify rule %s.": "Неизвестное изменение правила %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Неверный идентификатор приложения", "Invalid application id": "Неверный идентификатор приложения",
"the provider: %s does not exist": "провайдер: %s не существует" "the provider: %s does not exist": "провайдер: %s не существует"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "Тип провайдера: %s не поддерживается" "The provider type: %s is not supported": "Тип провайдера: %s не поддерживается"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "Пустой идентификатор клиента или секрет клиента",
"Grant_type: %s is not supported in this application": "Тип предоставления: %s не поддерживается в данном приложении", "Grant_type: %s is not supported in this application": "Тип предоставления: %s не поддерживается в данном приложении",
"Invalid application or wrong clientSecret": "Недействительное приложение или неправильный clientSecret", "Invalid application or wrong clientSecret": "Недействительное приложение или неправильный clientSecret",
"Invalid client_id": "Недействительный идентификатор клиента", "Invalid client_id": "Недействительный идентификатор клиента",
@ -146,6 +151,8 @@
"Unknown type": "Неизвестный тип", "Unknown type": "Неизвестный тип",
"Wrong verification code!": "Неправильный код подтверждения!", "Wrong verification code!": "Неправильный код подтверждения!",
"You should verify your code in %d min!": "Вы должны проверить свой код через %d минут!", "You should verify your code in %d min!": "Вы должны проверить свой код через %d минут!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь" "the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Kullanıcı adı zaten var", "Username already exists": "Kullanıcı adı zaten var",
"Username cannot be an email address": "Kullanıcı adı bir e-mail adresi olamaz", "Username cannot be an email address": "Kullanıcı adı bir e-mail adresi olamaz",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "Unauthorized operation", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Username already exists", "Username already exists": "Username already exists",
"Username cannot be an email address": "Username cannot be an email address", "Username cannot be an email address": "Username cannot be an email address",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Missing parameter", "Missing parameter": "Missing parameter",
"Please login first": "Please login first", "Please login first": "Please login first",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "The user: %s doesn't exist", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "don't support captchaProvider: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "The %s is immutable.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "Unknown modify rule %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Invalid application id", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "the provider: %s does not exist" "the provider: %s does not exist": "the provider: %s does not exist"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "The provider type: %s is not supported" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "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", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "Invalid client_id", "Invalid client_id": "Invalid client_id",
@ -146,6 +151,8 @@
"Unknown type": "Unknown type", "Unknown type": "Unknown type",
"Wrong verification code!": "Wrong verification code!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "the user does not exist, please sign up first" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {

View File

@ -19,6 +19,7 @@
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "Phương thức đăng nhập: đăng nhập bằng mật khẩu không được kích hoạt cho ứng dụng", "The login method: login with password is not enabled for the application": "Phương thức đăng nhập: đăng nhập bằng mật khẩu không được kích hoạt cho ứng dụng",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "Nhà cung cấp: %s không được kích hoạt cho ứng dụng", "The provider: %s is not enabled for the application": "Nhà cung cấp: %s không được kích hoạt cho ứng dụng",
"Unauthorized operation": "Hoạt động không được ủy quyền", "Unauthorized operation": "Hoạt động không được ủy quyền",
"Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s", "Unknown authentication type (not password or provider), form = %s": "Loại xác thực không xác định (không phải mật khẩu hoặc nhà cung cấp), biểu mẫu = %s",
@ -57,6 +58,7 @@
"The user is forbidden to sign in, please contact the administrator": "Người dùng bị cấm đăng nhập, vui lòng liên hệ với quản trị viên", "The user is forbidden to sign in, please contact the administrator": "Người dùng bị cấm đăng nhập, vui lòng liên hệ với quản trị viên",
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server", "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.": "Tên người dùng chỉ có thể chứa các ký tự chữ và số, gạch dưới hoặc gạch ngang, không được có hai ký tự gạch dưới hoặc gạch ngang liền kề và không được bắt đầu hoặc kết thúc bằng dấu gạch dưới hoặc gạch ngang.", "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.": "Tên người dùng chỉ có thể chứa các ký tự chữ và số, gạch dưới hoặc gạch ngang, không được có hai ký tự gạch dưới hoặc gạch ngang liền kề và không được bắt đầu hoặc kết thúc bằng dấu gạch dưới hoặc gạch ngang.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Tên đăng nhập đã tồn tại", "Username already exists": "Tên đăng nhập đã tồn tại",
"Username cannot be an email address": "Tên người dùng không thể là địa chỉ email", "Username cannot be an email address": "Tên người dùng không thể là địa chỉ email",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "Thiếu tham số", "Missing parameter": "Thiếu tham số",
"Please login first": "Vui lòng đăng nhập trước", "Please login first": "Vui lòng đăng nhập trước",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "Người dùng: %s không tồn tại", "The user: %s doesn't exist": "Người dùng: %s không tồn tại",
"don't support captchaProvider: ": "không hỗ trợ captchaProvider: ", "don't support captchaProvider: ": "không hỗ trợ captchaProvider: ",
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode" "this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
@ -93,6 +96,9 @@
"The %s is immutable.": "%s không thể thay đổi được.", "The %s is immutable.": "%s không thể thay đổi được.",
"Unknown modify rule %s.": "Quy tắc thay đổi không xác định %s." "Unknown modify rule %s.": "Quy tắc thay đổi không xác định %s."
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
},
"provider": { "provider": {
"Invalid application id": "Sai ID ứng dụng", "Invalid application id": "Sai ID ứng dụng",
"the provider: %s does not exist": "Nhà cung cấp: %s không tồn tại" "the provider: %s does not exist": "Nhà cung cấp: %s không tồn tại"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "Loại nhà cung cấp: %s không được hỗ trợ" "The provider type: %s is not supported": "Loại nhà cung cấp: %s không được hỗ trợ"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "ClientId hoặc clientSecret trống",
"Grant_type: %s is not supported in this application": "Loại cấp phép: %s không được hỗ trợ trong ứng dụng này", "Grant_type: %s is not supported in this application": "Loại cấp phép: %s không được hỗ trợ trong ứng dụng này",
"Invalid application or wrong clientSecret": "Đơn đăng ký không hợp lệ hoặc sai clientSecret", "Invalid application or wrong clientSecret": "Đơn đăng ký không hợp lệ hoặc sai clientSecret",
"Invalid client_id": "Client_id không hợp lệ", "Invalid client_id": "Client_id không hợp lệ",
@ -146,6 +151,8 @@
"Unknown type": "Loại không xác định", "Unknown type": "Loại không xác định",
"Wrong verification code!": "Mã xác thực sai!", "Wrong verification code!": "Mã xác thực sai!",
"You should verify your code in %d min!": "Bạn nên kiểm tra mã của mình trong %d phút!", "You should verify your code in %d min!": "Bạn nên kiểm tra mã của mình trong %d phút!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "Người dùng không tồn tại, vui lòng đăng ký trước" "the user does not exist, please sign up first": "Người dùng không tồn tại, vui lòng đăng ký trước"
}, },
"webauthn": { "webauthn": {

View File

@ -19,11 +19,12 @@
"The login method: login with SMS is not enabled for the application": "该应用禁止采用短信登录方式", "The login method: login with SMS is not enabled for the application": "该应用禁止采用短信登录方式",
"The login method: login with email is not enabled for the application": "该应用禁止采用邮箱登录方式", "The login method: login with email is not enabled for the application": "该应用禁止采用邮箱登录方式",
"The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式", "The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式",
"The organization: %s does not exist": "组织: %s 不存在",
"The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用", "The provider: %s is not enabled for the application": "该应用的提供商: %s未被启用",
"Unauthorized operation": "未授权的操作", "Unauthorized operation": "未授权的操作",
"Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s", "Unknown authentication type (not password or provider), form = %s": "未知的认证类型(非密码或第三方提供商):%s",
"User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中", "User's tag: %s is not listed in the application's tags": "用户的标签: %s不在该应用的标签列表中",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing" "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s 没有激活或正在等待订阅并且应用: %s 没有默认值"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "服务%s与%s不匹配" "Service %s and %s do not match": "服务%s与%s不匹配"
@ -38,9 +39,9 @@
"Email is invalid": "无效邮箱", "Email is invalid": "无效邮箱",
"Empty username.": "用户名不可为空", "Empty username.": "用户名不可为空",
"FirstName cannot be blank": "名不可以为空", "FirstName cannot be blank": "名不可以为空",
"Invitation code cannot be blank": "Invitation code cannot be blank", "Invitation code cannot be blank": "邀请码不能为空",
"Invitation code exhausted": "邀请码使用次数已耗尽", "Invitation code exhausted": "邀请码使用次数已耗尽",
"Invitation code is invalid": "Invitation code is invalid", "Invitation code is invalid": "邀请码无效",
"Invitation code suspended": "邀请码已被禁止使用", "Invitation code suspended": "邀请码已被禁止使用",
"LDAP user name or password incorrect": "LDAP密码错误", "LDAP user name or password incorrect": "LDAP密码错误",
"LastName cannot be blank": "姓不可以为空", "LastName cannot be blank": "姓不可以为空",
@ -57,6 +58,7 @@
"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": "用户: %s 在LDAP服务器中未找到", "The user: %s doesn't exist in LDAP server": "用户: %s 在LDAP服务器中未找到",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "用户名只能包含字母数字字符、下划线或连字符,不能有连续的连字符或下划线,也不能以连字符或下划线开头或结尾", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "用户名只能包含字母数字字符、下划线或连字符,不能有连续的连字符或下划线,也不能以连字符或下划线开头或结尾",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "值 \\\"%s\\\"在账户信息字段\\\"%s\\\" 中与应用的账户项正则表达式不匹配",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "值\\\"%s\\\"在注册字段\\\"%s\\\"中与应用\\\"%s\\\"的注册项正则表达式不匹配", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "值\\\"%s\\\"在注册字段\\\"%s\\\"中与应用\\\"%s\\\"的注册项正则表达式不匹配",
"Username already exists": "用户名已存在", "Username already exists": "用户名已存在",
"Username cannot be an email address": "用户名不可以是邮箱地址", "Username cannot be an email address": "用户名不可以是邮箱地址",
@ -73,6 +75,7 @@
"general": { "general": {
"Missing parameter": "缺少参数", "Missing parameter": "缺少参数",
"Please login first": "请先登录", "Please login first": "请先登录",
"The organization: %s should have one application at least": "组织: %s 应该拥有至少一个应用",
"The user: %s doesn't exist": "用户: %s不存在", "The user: %s doesn't exist": "用户: %s不存在",
"don't support captchaProvider: ": "不支持验证码提供商: ", "don't support captchaProvider: ": "不支持验证码提供商: ",
"this operation is not allowed in demo mode": "demo模式下不允许该操作" "this operation is not allowed in demo mode": "demo模式下不允许该操作"
@ -93,6 +96,9 @@
"The %s is immutable.": "%s 是不可变的", "The %s is immutable.": "%s 是不可变的",
"Unknown modify rule %s.": "未知的修改规则: %s" "Unknown modify rule %s.": "未知的修改规则: %s"
}, },
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "权限: \\\"%s\\\" 不存在"
},
"provider": { "provider": {
"Invalid application id": "无效的应用ID", "Invalid application id": "无效的应用ID",
"the provider: %s does not exist": "提供商: %s不存在" "the provider: %s does not exist": "提供商: %s不存在"
@ -117,7 +123,6 @@
"The provider type: %s is not supported": "不支持的提供商类型: %s" "The provider type: %s is not supported": "不支持的提供商类型: %s"
}, },
"token": { "token": {
"Empty clientId or clientSecret": "clientId或clientSecret为空",
"Grant_type: %s is not supported in this application": "该应用不支持Grant_type: %s", "Grant_type: %s is not supported in this application": "该应用不支持Grant_type: %s",
"Invalid application or wrong clientSecret": "无效应用或错误的clientSecret", "Invalid application or wrong clientSecret": "无效应用或错误的clientSecret",
"Invalid client_id": "无效的ClientId", "Invalid client_id": "无效的ClientId",
@ -146,6 +151,8 @@
"Unknown type": "未知类型", "Unknown type": "未知类型",
"Wrong verification code!": "验证码错误!", "Wrong verification code!": "验证码错误!",
"You should verify your code in %d min!": "请在 %d 分钟内输入正确验证码", "You should verify your code in %d min!": "请在 %d 分钟内输入正确验证码",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "请添加一个SMS提供商到应用 %s 的 \\\"提供商 \\\"列表",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "请添加一个Email提供商到应用 %s 的 \\\"提供商 \\\"列表",
"the user does not exist, please sign up first": "用户不存在,请先注册" "the user does not exist, please sign up first": "用户不存在,请先注册"
}, },
"webauthn": { "webauthn": {

View File

@ -75,6 +75,7 @@ import (
"github.com/markbates/goth/providers/twitterv2" "github.com/markbates/goth/providers/twitterv2"
"github.com/markbates/goth/providers/typetalk" "github.com/markbates/goth/providers/typetalk"
"github.com/markbates/goth/providers/uber" "github.com/markbates/goth/providers/uber"
"github.com/markbates/goth/providers/vk"
"github.com/markbates/goth/providers/wepay" "github.com/markbates/goth/providers/wepay"
"github.com/markbates/goth/providers/xero" "github.com/markbates/goth/providers/xero"
"github.com/markbates/goth/providers/yahoo" "github.com/markbates/goth/providers/yahoo"
@ -371,6 +372,11 @@ func NewGothIdProvider(providerType string, clientId string, clientSecret string
Provider: uber.New(clientId, clientSecret, redirectUrl), Provider: uber.New(clientId, clientSecret, redirectUrl),
Session: &uber.Session{}, Session: &uber.Session{},
} }
case "VK":
idp = GothIdProvider{
Provider: vk.New(clientId, clientSecret, redirectUrl),
Session: &vk.Session{},
}
case "Wepay": case "Wepay":
idp = GothIdProvider{ idp = GothIdProvider{
Provider: wepay.New(clientId, clientSecret, redirectUrl), Provider: wepay.New(clientId, clientSecret, redirectUrl),

View File

@ -8,12 +8,62 @@
"favicon": "", "favicon": "",
"passwordType": "plain", "passwordType": "plain",
"passwordSalt": "", "passwordSalt": "",
"passwordOptions": ["AtLeast6"], "passwordOptions": [
"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"], "AtLeast6"
],
"countryCodes": [
"US",
"GB",
"ES",
"FR",
"DE",
"CN",
"JP",
"KR",
"VN",
"ID",
"SG",
"IN",
"IT",
"MY",
"TR",
"DZ",
"IL",
"PH",
"NL",
"PL",
"FI",
"SE",
"UA",
"KZ"
],
"defaultAvatar": "", "defaultAvatar": "",
"defaultApplication": "", "defaultApplication": "",
"tags": [], "tags": [],
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "it", "ms", "tr","ar", "he", "nl", "pl", "fi", "sv", "uk", "kk", "fa"], "languages": [
"en",
"zh",
"es",
"fr",
"de",
"id",
"ja",
"ko",
"ru",
"vi",
"it",
"ms",
"tr",
"ar",
"he",
"nl",
"pl",
"fi",
"sv",
"uk",
"kk",
"fa"
],
"masterPassword": "", "masterPassword": "",
"defaultPassword": "", "defaultPassword": "",
"initScore": 2000, "initScore": 2000,
@ -49,18 +99,18 @@
{ {
"name": "Password", "name": "Password",
"displayName": "Password", "displayName": "Password",
"rule": "All", "rule": "All"
}, },
{ {
"name": "Verification code", "name": "Verification code",
"displayName": "Verification code", "displayName": "Verification code",
"rule": "All", "rule": "All"
}, },
{ {
"name": "WebAuthn", "name": "WebAuthn",
"displayName": "WebAuthn", "displayName": "WebAuthn",
"rule": "None", "rule": "None"
}, }
], ],
"signupItems": [ "signupItems": [
{ {
@ -72,55 +122,65 @@
}, },
{ {
"name": "Username", "name": "Username",
"visible": true, "visible": true,
"required": true, "required": true,
"prompted": false, "prompted": false,
"rule": "None" "rule": "None"
}, },
{ {
"name": "Display name", "name": "Display name",
"visible": true, "visible": true,
"required": true, "required": true,
"prompted": false, "prompted": false,
"rule": "None" "rule": "None"
}, },
{ {
"name": "Password", "name": "Password",
"visible": true, "visible": true,
"required": true, "required": true,
"prompted": false, "prompted": false,
"rule": "None" "rule": "None"
}, },
{ {
"name": "Confirm password", "name": "Confirm password",
"visible": true, "visible": true,
"required": true, "required": true,
"prompted": false, "prompted": false,
"rule": "None" "rule": "None"
}, },
{ {
"name": "Email", "name": "Email",
"visible": true, "visible": true,
"required": true, "required": true,
"prompted": false, "prompted": false,
"rule": "None" "rule": "None"
}, },
{ {
"name": "Phone", "name": "Phone",
"visible": true, "visible": true,
"required": true, "required": true,
"prompted": false, "prompted": false,
"rule": "None" "rule": "None"
}, },
{ {
"name": "Agreement", "name": "Agreement",
"visible": true, "visible": true,
"required": true, "required": true,
"prompted": false, "prompted": false,
"rule": "None" "rule": "None"
} }
], ],
"redirectUris": [""], "grantTypes": [
"authorization_code",
"password",
"client_credentials",
"token",
"id_token",
"refresh_token"
],
"redirectUris": [
""
],
"expireInHours": 168, "expireInHours": 168,
"failedSigninLimit": 5, "failedSigninLimit": 5,
"failedSigninFrozenTime": 15 "failedSigninFrozenTime": 15
@ -353,71 +413,71 @@
], ],
"groups": [ "groups": [
{ {
"owner": "", "owner": "",
"name":"", "name": "",
"displayName": "", "displayName": "",
"manager": "", "manager": "",
"contactEmail": "", "contactEmail": "",
"type": "", "type": "",
"parent_id": "", "parent_id": "",
"isTopGroup": true, "isTopGroup": true,
"title": "", "title": "",
"key": "", "key": "",
"children": "", "children": "",
"isEnabled": true "isEnabled": true
} }
], ],
"adapters": [ "adapters": [
{ {
"owner": "", "owner": "",
"name": "", "name": "",
"table": "", "table": "",
"useSameDb": true, "useSameDb": true,
"type": "", "type": "",
"databaseType": "", "databaseType": "",
"database": "", "database": "",
"host": "", "host": "",
"port": 0, "port": 0,
"user": "", "user": "",
"password": "", "password": ""
} }
], ],
"enforcers": [ "enforcers": [
{ {
"owner": "", "owner": "",
"name": "", "name": "",
"displayName": "", "displayName": "",
"description": "", "description": "",
"model": "", "model": "",
"adapter": "", "adapter": "",
"enforcer": "" "enforcer": ""
} }
], ],
"plans": [ "plans": [
{ {
"owner": "", "owner": "",
"name": "", "name": "",
"displayName": "", "displayName": "",
"description": "", "description": "",
"price": 0, "price": 0,
"currency": "", "currency": "",
"period": "", "period": "",
"product": "", "product": "",
"paymentProviders": [], "paymentProviders": [],
"isEnabled": true, "isEnabled": true,
"role", "" "role": ""
} }
], ],
"pricings": [ "pricings": [
{ {
"owner": "", "owner": "",
"name": "", "name": "",
"displayName": "", "displayName": "",
"description": "", "description": "",
"plans": [], "plans": [],
"isEnabled": true, "isEnabled": true,
"trialDuration": 0, "trialDuration": 0,
"application": "", "application": ""
} }
] ]
} }

View File

@ -178,6 +178,7 @@ func (adapter *Adapter) InitAdapter() error {
dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.") dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.")
} }
dataSourceName = conf.ReplaceDataSourceNameByDocker(dataSourceName)
engine, err := xorm.NewEngine(driverName, dataSourceName) engine, err := xorm.NewEngine(driverName, dataSourceName)
if err != nil { if err != nil {
return err return err

View File

@ -19,6 +19,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/xorm-io/core" "github.com/xorm-io/core"
) )
@ -40,6 +41,15 @@ type SignupItem struct {
Rule string `json:"rule"` Rule string `json:"rule"`
} }
type SigninItem struct {
Name string `json:"name"`
Visible bool `json:"visible"`
Label string `json:"label"`
Placeholder string `json:"placeholder"`
Rule string `json:"rule"`
IsCustom bool `json:"isCustom"`
}
type SamlItem struct { type SamlItem struct {
Name string `json:"name"` Name string `json:"name"`
NameFormat string `json:"nameFormat"` NameFormat string `json:"nameFormat"`
@ -57,6 +67,7 @@ type Application struct {
Description string `xorm:"varchar(100)" json:"description"` Description string `xorm:"varchar(100)" json:"description"`
Organization string `xorm:"varchar(100)" json:"organization"` Organization string `xorm:"varchar(100)" json:"organization"`
Cert string `xorm:"varchar(100)" json:"cert"` Cert string `xorm:"varchar(100)" json:"cert"`
HeaderHtml string `xorm:"mediumtext" json:"headerHtml"`
EnablePassword bool `json:"enablePassword"` EnablePassword bool `json:"enablePassword"`
EnableSignUp bool `json:"enableSignUp"` EnableSignUp bool `json:"enableSignUp"`
EnableSigninSession bool `json:"enableSigninSession"` EnableSigninSession bool `json:"enableSigninSession"`
@ -72,6 +83,7 @@ type Application struct {
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"` Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"` SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"`
SignupItems []*SignupItem `xorm:"varchar(2000)" json:"signupItems"` SignupItems []*SignupItem `xorm:"varchar(2000)" json:"signupItems"`
SigninItems []*SigninItem `xorm:"mediumtext" json:"signinItems"`
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"` GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
OrganizationObj *Organization `xorm:"-" json:"organizationObj"` OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
CertPublicKey string `xorm:"-" json:"certPublicKey"` CertPublicKey string `xorm:"-" json:"certPublicKey"`
@ -190,6 +202,100 @@ func extendApplicationWithOrg(application *Application) (err error) {
return return
} }
func extendApplicationWithSigninItems(application *Application) (err error) {
if len(application.SigninItems) == 0 {
signinItem := &SigninItem{
Name: "Back button",
Visible: true,
Label: "\n<style>\n .back-button {\n top: 65px;\n left: 15px;\n position: absolute;\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
application.SigninItems = append(application.SigninItems, signinItem)
signinItem = &SigninItem{
Name: "Languages",
Visible: true,
Label: "\n<style>\n .login-languages {\n top: 55px;\n right: 5px;\n position: absolute;\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
application.SigninItems = append(application.SigninItems, signinItem)
signinItem = &SigninItem{
Name: "Logo",
Visible: true,
Label: "\n<style>\n .login-logo-box {\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
application.SigninItems = append(application.SigninItems, signinItem)
signinItem = &SigninItem{
Name: "Signin methods",
Visible: true,
Label: "\n<style>\n .signin-methods {\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
application.SigninItems = append(application.SigninItems, signinItem)
signinItem = &SigninItem{
Name: "Username",
Visible: true,
Label: "\n<style>\n .login-username {\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
application.SigninItems = append(application.SigninItems, signinItem)
signinItem = &SigninItem{
Name: "Password",
Visible: true,
Label: "\n<style>\n .login-password {\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
application.SigninItems = append(application.SigninItems, signinItem)
signinItem = &SigninItem{
Name: "Agreement",
Visible: true,
Label: "\n<style>\n .login-agreement {\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
application.SigninItems = append(application.SigninItems, signinItem)
signinItem = &SigninItem{
Name: "Forgot password?",
Visible: true,
Label: "\n<style>\n .login-forget-password {\n display: inline-flex;\n justify-content: space-between;\n width: 320px;\n margin-bottom: 25px;\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
application.SigninItems = append(application.SigninItems, signinItem)
signinItem = &SigninItem{
Name: "Login button",
Visible: true,
Label: "\n<style>\n .login-button-box {\n margin-bottom: 5px;\n }\n .login-button {\n width: 100%;\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
application.SigninItems = append(application.SigninItems, signinItem)
signinItem = &SigninItem{
Name: "Signup link",
Visible: true,
Label: "\n<style>\n .login-signup-link {\n margin-bottom: 24px;\n display: flex;\n justify-content: end;\n}\n</style>\n",
Placeholder: "",
Rule: "None",
}
application.SigninItems = append(application.SigninItems, signinItem)
signinItem = &SigninItem{
Name: "Providers",
Visible: true,
Label: "\n<style>\n .provider-img {\n width: 30px;\n margin: 5px;\n }\n .provider-big-img {\n margin-bottom: 10px;\n }\n</style>\n",
Placeholder: "",
Rule: "None",
}
application.SigninItems = append(application.SigninItems, signinItem)
}
return
}
func extendApplicationWithSigninMethods(application *Application) (err error) { func extendApplicationWithSigninMethods(application *Application) (err error) {
if len(application.SigninMethods) == 0 { if len(application.SigninMethods) == 0 {
if application.EnablePassword { if application.EnablePassword {
@ -240,6 +346,10 @@ func getApplication(owner string, name string) (*Application, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = extendApplicationWithSigninItems(&application)
if err != nil {
return nil, err
}
return &application, nil return &application, nil
} else { } else {
@ -270,6 +380,11 @@ func GetApplicationByOrganizationName(organization string) (*Application, error)
return nil, err return nil, err
} }
err = extendApplicationWithSigninItems(&application)
if err != nil {
return nil, err
}
return &application, nil return &application, nil
} else { } else {
return nil, nil return nil, nil
@ -322,6 +437,11 @@ func GetApplicationByClientId(clientId string) (*Application, error) {
return nil, err return nil, err
} }
err = extendApplicationWithSigninItems(&application)
if err != nil {
return nil, err
}
return &application, nil return &application, nil
} else { } else {
return nil, nil return nil, nil
@ -349,36 +469,70 @@ func GetMaskedApplication(application *Application, userId string) *Application
application.FailedSigninFrozenTime = DefaultFailedSigninFrozenTime application.FailedSigninFrozenTime = DefaultFailedSigninFrozenTime
} }
isOrgUser := false
if userId != "" { if userId != "" {
if isUserIdGlobalAdmin(userId) { if isUserIdGlobalAdmin(userId) {
return application return application
} }
user, _ := GetUser(userId) user, err := GetUser(userId)
if user != nil && user.IsApplicationAdmin(application) { if err != nil {
return application panic(err)
}
if user != nil {
if user.IsApplicationAdmin(application) {
return application
}
if user.Owner == application.Organization {
isOrgUser = true
}
} }
} }
if application.ClientSecret != "" { application.ClientSecret = "***"
application.ClientSecret = "***" application.Cert = "***"
application.EnablePassword = false
application.EnableSigninSession = false
application.EnableCodeSignin = false
application.EnableSamlCompress = false
application.EnableSamlC14n10 = false
application.EnableSamlPostBinding = false
application.EnableWebAuthn = false
application.EnableLinkWithEmail = false
application.SamlReplyUrl = "***"
providerItems := []*ProviderItem{}
for _, providerItem := range application.Providers {
if providerItem.Provider != nil && (providerItem.Provider.Category == "OAuth" || providerItem.Provider.Category == "Web3") {
providerItems = append(providerItems, providerItem)
}
} }
application.Providers = providerItems
application.GrantTypes = nil
application.Tags = nil
application.RedirectUris = nil
application.TokenFormat = "***"
application.TokenFields = nil
application.ExpireInHours = -1
application.RefreshExpireInHours = -1
application.FailedSigninLimit = -1
application.FailedSigninFrozenTime = -1
if application.OrganizationObj != nil { if application.OrganizationObj != nil {
if application.OrganizationObj.MasterPassword != "" { application.OrganizationObj.MasterPassword = "***"
application.OrganizationObj.MasterPassword = "***" application.OrganizationObj.DefaultPassword = "***"
} application.OrganizationObj.MasterVerificationCode = "***"
if application.OrganizationObj.DefaultPassword != "" { application.OrganizationObj.PasswordType = "***"
application.OrganizationObj.DefaultPassword = "***" application.OrganizationObj.PasswordSalt = "***"
} application.OrganizationObj.InitScore = -1
if application.OrganizationObj.MasterVerificationCode != "" { application.OrganizationObj.EnableSoftDeletion = false
application.OrganizationObj.MasterVerificationCode = "***" application.OrganizationObj.IsProfilePublic = false
}
if application.OrganizationObj.PasswordType != "" { if !isOrgUser {
application.OrganizationObj.PasswordType = "***" application.OrganizationObj.MfaItems = nil
} application.OrganizationObj.AccountItems = nil
if application.OrganizationObj.PasswordSalt != "" {
application.OrganizationObj.PasswordSalt = "***"
} }
} }
@ -396,8 +550,12 @@ func GetMaskedApplications(applications []*Application, userId string) []*Applic
return applications return applications
} }
func GetAllowedApplications(applications []*Application, userId string) ([]*Application, error) { func GetAllowedApplications(applications []*Application, userId string, lang string) ([]*Application, error) {
if userId == "" || isUserIdGlobalAdmin(userId) { if userId == "" {
return nil, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
}
if isUserIdGlobalAdmin(userId) {
return applications, nil return applications, nil
} }
@ -405,7 +563,11 @@ func GetAllowedApplications(applications []*Application, userId string) ([]*Appl
if err != nil { if err != nil {
return nil, err return nil, err
} }
if user != nil && user.IsAdmin { if user == nil {
return nil, fmt.Errorf(i18n.Translate(lang, "auth:Unauthorized operation"))
}
if user.IsAdmin {
return applications, nil return applications, nil
} }
@ -520,7 +682,7 @@ func (application *Application) GetId() string {
} }
func (application *Application) IsRedirectUriValid(redirectUri string) bool { func (application *Application) IsRedirectUriValid(redirectUri string) bool {
redirectUris := append([]string{"http://localhost:", "https://localhost:", "http://127.0.0.1:", "http://casdoor-app"}, application.RedirectUris...) redirectUris := append([]string{"http://localhost:", "https://localhost:", "http://127.0.0.1:", "http://casdoor-app", ".chromiumapp.org"}, application.RedirectUris...)
for _, targetUri := range redirectUris { for _, targetUri := range redirectUris {
targetUriRegex := regexp.MustCompile(targetUri) targetUriRegex := regexp.MustCompile(targetUri)
if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) { if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) {

View File

@ -38,12 +38,38 @@ func (application *Application) GetProviderByCategory(category string) (*Provide
return nil, nil return nil, nil
} }
func (application *Application) GetEmailProvider() (*Provider, error) { func (application *Application) GetProviderByCategoryAndRule(category string, method string) (*Provider, error) {
return application.GetProviderByCategory("Email") providers, err := GetProviders(application.Organization)
if err != nil {
return nil, err
}
m := map[string]*Provider{}
for _, provider := range providers {
if provider.Category != category {
continue
}
m[provider.Name] = provider
}
for _, providerItem := range application.Providers {
if providerItem.Rule == method || (providerItem.Rule == "all" || providerItem.Rule == "" || providerItem.Rule == "None") {
if provider, ok := m[providerItem.Name]; ok {
return provider, nil
}
}
}
return nil, nil
} }
func (application *Application) GetSmsProvider() (*Provider, error) { func (application *Application) GetEmailProvider(method string) (*Provider, error) {
return application.GetProviderByCategory("SMS") return application.GetProviderByCategoryAndRule("Email", method)
}
func (application *Application) GetSmsProvider(method string) (*Provider, error) {
return application.GetProviderByCategoryAndRule("SMS", method)
} }
func (application *Application) GetStorageProvider() (*Provider, error) { func (application *Application) GetStorageProvider() (*Provider, error) {

View File

@ -17,29 +17,35 @@ package object
import ( import (
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
) )
type InitData struct { type InitData struct {
Organizations []*Organization `json:"organizations"` Organizations []*Organization `json:"organizations"`
Applications []*Application `json:"applications"` Applications []*Application `json:"applications"`
Users []*User `json:"users"` Users []*User `json:"users"`
Certs []*Cert `json:"certs"` Certs []*Cert `json:"certs"`
Providers []*Provider `json:"providers"` Providers []*Provider `json:"providers"`
Ldaps []*Ldap `json:"ldaps"` Ldaps []*Ldap `json:"ldaps"`
Models []*Model `json:"models"` Models []*Model `json:"models"`
Permissions []*Permission `json:"permissions"` Permissions []*Permission `json:"permissions"`
Payments []*Payment `json:"payments"` Payments []*Payment `json:"payments"`
Products []*Product `json:"products"` Products []*Product `json:"products"`
Resources []*Resource `json:"resources"` Resources []*Resource `json:"resources"`
Roles []*Role `json:"roles"` Roles []*Role `json:"roles"`
Syncers []*Syncer `json:"syncers"` Syncers []*Syncer `json:"syncers"`
Tokens []*Token `json:"tokens"` Tokens []*Token `json:"tokens"`
Webhooks []*Webhook `json:"webhooks"` Webhooks []*Webhook `json:"webhooks"`
Groups []*Group `json:"groups"` Groups []*Group `json:"groups"`
Adapters []*Adapter `json:"adapters"` Adapters []*Adapter `json:"adapters"`
Enforcers []*Enforcer `json:"enforcers"` Enforcers []*Enforcer `json:"enforcers"`
Plans []*Plan `json:"plans"` Plans []*Plan `json:"plans"`
Pricings []*Pricing `json:"pricings"` Pricings []*Pricing `json:"pricings"`
Invitations []*Invitation `json:"invitations"`
Records []*casvisorsdk.Record `json:"records"`
Sessions []*Session `json:"sessions"`
Subscriptions []*Subscription `json:"subscriptions"`
Transactions []*Transaction `json:"transactions"`
} }
func InitFromFile() { func InitFromFile() {
@ -114,6 +120,21 @@ func InitFromFile() {
for _, pricing := range initData.Pricings { for _, pricing := range initData.Pricings {
initDefinedPricing(pricing) initDefinedPricing(pricing)
} }
for _, invitation := range initData.Invitations {
initDefinedInvitation(invitation)
}
for _, record := range initData.Records {
initDefinedRecord(record)
}
for _, session := range initData.Sessions {
initDefinedSession(session)
}
for _, subscription := range initData.Subscriptions {
initDefinedSubscription(subscription)
}
for _, transaction := range initData.Transactions {
initDefinedTransaction(transaction)
}
} }
} }
@ -145,6 +166,11 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
Enforcers: []*Enforcer{}, Enforcers: []*Enforcer{},
Plans: []*Plan{}, Plans: []*Plan{},
Pricings: []*Pricing{}, Pricings: []*Pricing{},
Invitations: []*Invitation{},
Records: []*casvisorsdk.Record{},
Sessions: []*Session{},
Subscriptions: []*Subscription{},
Transactions: []*Transaction{},
} }
err := util.JsonToStruct(s, data) err := util.JsonToStruct(s, data)
if err != nil { if err != nil {
@ -225,6 +251,11 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
pricing.Plans = []string{} pricing.Plans = []string{}
} }
} }
for _, session := range data.Sessions {
if session.SessionId == nil {
session.SessionId = []string{}
}
}
return data, nil return data, nil
} }
@ -543,3 +574,61 @@ func initDefinedPricing(pricing *Pricing) {
panic(err) panic(err)
} }
} }
func initDefinedInvitation(invitation *Invitation) {
existed, err := getInvitation(invitation.Owner, invitation.Name)
if err != nil {
panic(err)
}
if existed != nil {
return
}
invitation.CreatedTime = util.GetCurrentTime()
_, err = AddInvitation(invitation, "en")
if err != nil {
panic(err)
}
}
func initDefinedRecord(record *casvisorsdk.Record) {
record.CreatedTime = util.GetCurrentTime()
_ = AddRecord(record)
}
func initDefinedSession(session *Session) {
session.CreatedTime = util.GetCurrentTime()
_, err := AddSession(session)
if err != nil {
panic(err)
}
}
func initDefinedSubscription(subscription *Subscription) {
existed, err := getSubscription(subscription.Owner, subscription.Name)
if err != nil {
panic(err)
}
if existed != nil {
return
}
subscription.CreatedTime = util.GetCurrentTime()
_, err = AddSubscription(subscription)
if err != nil {
panic(err)
}
}
func initDefinedTransaction(transaction *Transaction) {
existed, err := getTransaction(transaction.Owner, transaction.Name)
if err != nil {
panic(err)
}
if existed != nil {
return
}
transaction.CreatedTime = util.GetCurrentTime()
_, err = AddTransaction(transaction)
if err != nil {
panic(err)
}
}

View File

@ -121,6 +121,31 @@ func writeInitDataToFile(filePath string) error {
return err return err
} }
invitations, err := GetInvitations("")
if err != nil {
return err
}
records, err := GetRecords()
if err != nil {
return err
}
sessions, err := GetSessions("")
if err != nil {
return err
}
subscriptions, err := GetSubscriptions("")
if err != nil {
return err
}
transactions, err := GetTransactions("")
if err != nil {
return err
}
data := &InitData{ data := &InitData{
Organizations: organizations, Organizations: organizations,
Applications: applications, Applications: applications,
@ -142,6 +167,11 @@ func writeInitDataToFile(filePath string) error {
Enforcers: enforcers, Enforcers: enforcers,
Plans: plans, Plans: plans,
Pricings: pricings, Pricings: pricings,
Invitations: invitations,
Records: records,
Sessions: sessions,
Subscriptions: subscriptions,
Transactions: transactions,
} }
text := util.StructToJsonFormatted(data) text := util.StructToJsonFormatted(data)

View File

@ -36,9 +36,14 @@ func (mfa *SmsMfa) SetupVerify(passCode string) error {
mfa.Secret, _ = util.GetE164Number(mfa.Secret, mfa.CountryCode) mfa.Secret, _ = util.GetE164Number(mfa.Secret, mfa.CountryCode)
} }
if result := CheckVerificationCode(mfa.Secret, passCode, "en"); result.Code != VerificationSuccess { result, err := CheckVerificationCode(mfa.Secret, passCode, "en")
if err != nil {
return err
}
if result.Code != VerificationSuccess {
return errors.New(result.Msg) return errors.New(result.Msg)
} }
return nil return nil
} }
@ -52,21 +57,10 @@ func (mfa *SmsMfa) Enable(user *User) error {
if mfa.MfaType == SmsType { if mfa.MfaType == SmsType {
user.MfaPhoneEnabled = true user.MfaPhoneEnabled = true
columns = append(columns, "mfa_phone_enabled") columns = append(columns, "mfa_phone_enabled", "phone", "country_code")
if user.Phone == "" {
user.Phone = mfa.Secret
user.CountryCode = mfa.CountryCode
columns = append(columns, "phone", "country_code")
}
} else if mfa.MfaType == EmailType { } else if mfa.MfaType == EmailType {
user.MfaEmailEnabled = true user.MfaEmailEnabled = true
columns = append(columns, "mfa_email_enabled") columns = append(columns, "mfa_email_enabled", "email")
if user.Email == "" {
user.Email = mfa.Secret
columns = append(columns, "email")
}
} }
_, err := UpdateUser(user.GetId(), user, columns, false) _, err := UpdateUser(user.GetId(), user, columns, false)
@ -81,9 +75,15 @@ func (mfa *SmsMfa) Verify(passCode string) error {
if !util.IsEmailValid(mfa.Secret) { if !util.IsEmailValid(mfa.Secret) {
mfa.Secret, _ = util.GetE164Number(mfa.Secret, mfa.CountryCode) mfa.Secret, _ = util.GetE164Number(mfa.Secret, mfa.CountryCode)
} }
if result := CheckVerificationCode(mfa.Secret, passCode, "en"); result.Code != VerificationSuccess {
result, err := CheckVerificationCode(mfa.Secret, passCode, "en")
if err != nil {
return err
}
if result.Code != VerificationSuccess {
return errors.New(result.Msg) return errors.New(result.Msg)
} }
return nil return nil
} }

View File

@ -31,6 +31,7 @@ type AccountItem struct {
Visible bool `json:"visible"` Visible bool `json:"visible"`
ViewRule string `json:"viewRule"` ViewRule string `json:"viewRule"`
ModifyRule string `json:"modifyRule"` ModifyRule string `json:"modifyRule"`
Regex string `json:"regex"`
} }
type ThemeData struct { type ThemeData struct {
@ -342,6 +343,11 @@ func GetDefaultApplication(id string) (*Application, error) {
return nil, err return nil, err
} }
err = extendApplicationWithSigninItems(defaultApplication)
if err != nil {
return nil, err
}
return defaultApplication, nil return defaultApplication, nil
} }
@ -453,6 +459,16 @@ func organizationChangeTrigger(oldName string, newName string) error {
return err return err
} }
record := new(Record)
record.Owner = newName
record.Organization = newName
_, err = session.Where("organization=?", oldName).Update(record)
if err != nil {
if err.Error() != "no columns found to be updated" {
return err
}
}
resource := new(Resource) resource := new(Resource)
resource.Owner = newName resource.Owner = newName
_, err = session.Where("owner=?", oldName).Update(resource) _, err = session.Where("owner=?", oldName).Update(resource)

View File

@ -23,6 +23,8 @@ import (
"runtime" "runtime"
"strings" "strings"
"github.com/casvisor/casvisor-go-sdk/casvisorsdk"
"github.com/beego/beego" "github.com/beego/beego"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
@ -30,8 +32,9 @@ import (
_ "github.com/denisenkom/go-mssqldb" // db = mssql _ "github.com/denisenkom/go-mssqldb" // db = mssql
_ "github.com/go-sql-driver/mysql" // db = mysql _ "github.com/go-sql-driver/mysql" // db = mysql
_ "github.com/lib/pq" // db = postgres _ "github.com/lib/pq" // db = postgres
"github.com/xorm-io/core"
"github.com/xorm-io/xorm" "github.com/xorm-io/xorm"
"github.com/xorm-io/xorm/core"
"github.com/xorm-io/xorm/names"
_ "modernc.org/sqlite" // db = sqlite _ "modernc.org/sqlite" // db = sqlite
) )
@ -96,7 +99,7 @@ func InitAdapter() {
} }
tableNamePrefix := conf.GetConfigString("tableNamePrefix") tableNamePrefix := conf.GetConfigString("tableNamePrefix")
tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, tableNamePrefix) tbMapper := names.NewPrefixMapper(names.SnakeMapper{}, tableNamePrefix)
ormer.Engine.SetTableMapper(tbMapper) ormer.Engine.SetTableMapper(tbMapper)
} }
@ -116,6 +119,7 @@ type Ormer struct {
driverName string driverName string
dataSourceName string dataSourceName string
dbName string dbName string
Db *sql.DB
Engine *xorm.Engine Engine *xorm.Engine
} }
@ -125,6 +129,13 @@ func finalizer(a *Ormer) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
if a.Db != nil {
err = a.Db.Close()
if err != nil {
panic(err)
}
}
} }
// NewAdapter is the constructor for Ormer. // NewAdapter is the constructor for Ormer.
@ -146,6 +157,26 @@ func NewAdapter(driverName string, dataSourceName string, dbName string) (*Ormer
return a, nil return a, nil
} }
// NewAdapterFromdb is the constructor for Ormer.
func NewAdapterFromDb(driverName string, dataSourceName string, dbName string, db *sql.DB) (*Ormer, error) {
a := &Ormer{}
a.driverName = driverName
a.dataSourceName = dataSourceName
a.dbName = dbName
a.Db = db
// Open the DB, create it if not existed.
err := a.openFromDb(a.Db)
if err != nil {
return nil, err
}
// Call the destructor when the object is released.
runtime.SetFinalizer(a, finalizer)
return a, nil
}
func refineDataSourceNameForPostgres(dataSourceName string) string { func refineDataSourceNameForPostgres(dataSourceName string) string {
reg := regexp.MustCompile(`dbname=[^ ]+\s*`) reg := regexp.MustCompile(`dbname=[^ ]+\s*`)
return reg.ReplaceAllString(dataSourceName, "") return reg.ReplaceAllString(dataSourceName, "")
@ -224,6 +255,30 @@ func (a *Ormer) open() error {
return nil return nil
} }
func (a *Ormer) openFromDb(db *sql.DB) error {
dataSourceName := a.dataSourceName + a.dbName
if a.driverName != "mysql" {
dataSourceName = a.dataSourceName
}
xormDb := core.FromDB(db)
engine, err := xorm.NewEngineWithDB(a.driverName, dataSourceName, xormDb)
if err != nil {
return err
}
if a.driverName == "postgres" {
schema := util.GetValueFromDataSourceName("search_path", dataSourceName)
if schema != "" {
engine.SetSchema(schema)
}
}
a.Engine = engine
return nil
}
func (a *Ormer) close() { func (a *Ormer) close() {
_ = a.Engine.Close() _ = a.Engine.Close()
a.Engine = nil a.Engine = nil
@ -333,11 +388,21 @@ func (a *Ormer) createTable() {
panic(err) panic(err)
} }
err = a.Engine.Sync2(new(Transaction))
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(Syncer)) err = a.Engine.Sync2(new(Syncer))
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = a.Engine.Sync2(new(casvisorsdk.Record))
if err != nil {
panic(err)
}
err = a.Engine.Sync2(new(Webhook)) err = a.Engine.Sync2(new(Webhook))
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -82,7 +82,12 @@ func AddRecord(record *casvisorsdk.Record) bool {
} }
if casvisorsdk.GetClient() == nil { if casvisorsdk.GetClient() == nil {
return false affected, err := ormer.Engine.Insert(record)
if err != nil {
panic(err)
}
return affected != 0
} }
affected, err := casvisorsdk.AddRecord(record) affected, err := casvisorsdk.AddRecord(record)
@ -93,13 +98,55 @@ func AddRecord(record *casvisorsdk.Record) bool {
return affected return affected
} }
func getFilteredWebhooks(webhooks []*Webhook, action string) []*Webhook { func GetRecordCount(field, value string, filterRecord *casvisorsdk.Record) (int64, error) {
session := GetSession("", -1, -1, field, value, "", "")
return session.Count(filterRecord)
}
func GetRecords() ([]*casvisorsdk.Record, error) {
records := []*casvisorsdk.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 *casvisorsdk.Record) ([]*casvisorsdk.Record, error) {
records := []*casvisorsdk.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 *casvisorsdk.Record) ([]*casvisorsdk.Record, error) {
records := []*casvisorsdk.Record{}
err := ormer.Engine.Find(&records, record)
if err != nil {
return records, err
}
return records, nil
}
func getFilteredWebhooks(webhooks []*Webhook, organization string, action string) []*Webhook {
res := []*Webhook{} res := []*Webhook{}
for _, webhook := range webhooks { for _, webhook := range webhooks {
if !webhook.IsEnabled { if !webhook.IsEnabled {
continue continue
} }
if webhook.SingleOrgOnly {
if webhook.Organization != organization {
continue
}
}
matched := false matched := false
for _, event := range webhook.Events { for _, event := range webhook.Events {
if action == event { if action == event {
@ -122,7 +169,7 @@ func SendWebhooks(record *casvisorsdk.Record) error {
} }
errs := []error{} errs := []error{}
webhooks = getFilteredWebhooks(webhooks, record.Action) webhooks = getFilteredWebhooks(webhooks, record.Organization, record.Action)
for _, webhook := range webhooks { for _, webhook := range webhooks {
var user *User var user *User
if webhook.IsUserExtended { if webhook.IsUserExtended {

View File

@ -27,7 +27,7 @@ func getCasvisorApplication() *Application {
} }
for _, application := range applications { for _, application := range applications {
if strings.Contains(strings.ToLower(application.Name), "casvisor") { if strings.Contains(strings.ToLower(application.Name), "casvisor-my") {
return application return application
} }
} }

View File

@ -109,14 +109,17 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface, error) { func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface, error) {
endpoint := getProviderEndpoint(provider) endpoint := getProviderEndpoint(provider)
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint) storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
if err != nil {
return nil, err
}
if storageProvider == nil { if storageProvider == nil {
return nil, fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type) return nil, fmt.Errorf(i18n.Translate(lang, "storage:The provider type: %s is not supported"), provider.Type)
} }
if provider.Domain == "" { if provider.Domain == "" {
provider.Domain = storageProvider.GetEndpoint() provider.Domain = storageProvider.GetEndpoint()
_, err := UpdateProvider(provider.GetId(), provider) _, err = UpdateProvider(provider.GetId(), provider)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -39,11 +39,17 @@ type Syncer struct {
Type string `xorm:"varchar(100)" json:"type"` Type string `xorm:"varchar(100)" json:"type"`
DatabaseType string `xorm:"varchar(100)" json:"databaseType"` DatabaseType string `xorm:"varchar(100)" json:"databaseType"`
SslMode string `xorm:"varchar(100)" json:"sslMode"` SslMode string `xorm:"varchar(100)" json:"sslMode"`
SshType string `xorm:"varchar(100)" json:"sshType"`
Host string `xorm:"varchar(100)" json:"host"` Host string `xorm:"varchar(100)" json:"host"`
Port int `json:"port"` Port int `json:"port"`
User string `xorm:"varchar(100)" json:"user"` User string `xorm:"varchar(100)" json:"user"`
Password string `xorm:"varchar(150)" json:"password"` Password string `xorm:"varchar(150)" json:"password"`
SshHost string `xorm:"varchar(100)" json:"sshHost"`
SshPort int `json:"sshPort"`
SshUser string `xorm:"varchar(100)" json:"sshUser"`
SshPassword string `xorm:"varchar(150)" json:"sshPassword"`
Cert string `xorm:"varchar(100)" json:"cert"`
Database string `xorm:"varchar(100)" json:"database"` Database string `xorm:"varchar(100)" json:"database"`
Table string `xorm:"varchar(100)" json:"table"` Table string `xorm:"varchar(100)" json:"table"`
TableColumns []*TableColumn `xorm:"mediumtext" json:"tableColumns"` TableColumns []*TableColumn `xorm:"mediumtext" json:"tableColumns"`
@ -279,3 +285,25 @@ func RunSyncer(syncer *Syncer) error {
return syncer.syncUsers() return syncer.syncUsers()
} }
func TestSyncerDb(syncer Syncer) error {
oldSyncer, err := getSyncer(syncer.Owner, syncer.Name)
if err != nil {
return err
}
if syncer.Password == "***" {
syncer.Password = oldSyncer.Password
}
err = syncer.initAdapter()
if err != nil {
return err
}
err = syncer.Ormer.Engine.Ping()
if err != nil {
return err
}
return nil
}

View File

@ -15,12 +15,17 @@
package object package object
import ( import (
"context"
"database/sql" "database/sql"
"database/sql/driver"
"fmt" "fmt"
"strings" "strings"
"time" "time"
"golang.org/x/crypto/ssh"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/go-sql-driver/mysql"
) )
type OriginalUser = User type OriginalUser = User
@ -124,6 +129,19 @@ func (syncer *Syncer) calculateHash(user *OriginalUser) string {
return util.GetMd5Hash(s) return util.GetMd5Hash(s)
} }
type dsnConnector struct {
dsn string
driver driver.Driver
}
func (t dsnConnector) Connect(ctx context.Context) (driver.Conn, error) {
return t.driver.Open(t.dsn)
}
func (t dsnConnector) Driver() driver.Driver {
return t.driver
}
func (syncer *Syncer) initAdapter() error { func (syncer *Syncer) initAdapter() error {
if syncer.Ormer != nil { if syncer.Ormer != nil {
return nil return nil
@ -142,12 +160,38 @@ func (syncer *Syncer) initAdapter() error {
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/", syncer.User, syncer.Password, syncer.Host, syncer.Port) dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/", syncer.User, syncer.Password, syncer.Host, syncer.Port)
} }
var db *sql.DB
var err error
if syncer.SshType != "" && (syncer.DatabaseType == "mysql" || syncer.DatabaseType == "postgres" || syncer.DatabaseType == "mssql") {
var dial *ssh.Client
if syncer.SshType == "password" {
dial, err = DialWithPassword(syncer.SshUser, syncer.SshPassword, syncer.SshHost, syncer.SshPort)
} else {
dial, err = DialWithCert(syncer.SshUser, syncer.Owner+"/"+syncer.Cert, syncer.SshHost, syncer.SshPort)
}
if err != nil {
return err
}
if syncer.DatabaseType == "mysql" {
dataSourceName = fmt.Sprintf("%s:%s@%s(%s:%d)/", syncer.User, syncer.Password, syncer.Owner+syncer.Name, syncer.Host, syncer.Port)
mysql.RegisterDialContext(syncer.Owner+syncer.Name, (&ViaSSHDialer{Client: dial, Context: nil}).MysqlDial)
} else if syncer.DatabaseType == "postgres" || syncer.DatabaseType == "mssql" {
db = sql.OpenDB(dsnConnector{dsn: dataSourceName, driver: &ViaSSHDialer{Client: dial, Context: nil, DatabaseType: syncer.DatabaseType}})
}
}
if !isCloudIntranet { if !isCloudIntranet {
dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.") dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.")
} }
var err error if db != nil {
syncer.Ormer, err = NewAdapter(syncer.DatabaseType, dataSourceName, syncer.Database) syncer.Ormer, err = NewAdapterFromDb(syncer.DatabaseType, dataSourceName, syncer.Database, db)
} else {
syncer.Ormer, err = NewAdapter(syncer.DatabaseType, dataSourceName, syncer.Database)
}
return err return err
} }

View File

@ -681,7 +681,7 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
// GetPasswordToken // GetPasswordToken
// Resource Owner Password Credentials flow // Resource Owner Password Credentials flow
func GetPasswordToken(application *Application, username string, password string, scope string, host string) (*Token, *TokenError, error) { func GetPasswordToken(application *Application, username string, password string, scope string, host string) (*Token, *TokenError, error) {
user, err := getUser(application.Organization, username) user, err := GetUserByFields(application.Organization, username)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }

144
object/transaction.go Normal file
View File

@ -0,0 +1,144 @@
// Copyright 2024 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/util"
"github.com/xorm-io/core"
)
type Transaction 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"`
// Transaction Provider Info
Provider string `xorm:"varchar(100)" json:"provider"`
Category string `xorm:"varchar(100)" json:"category"`
Type string `xorm:"varchar(100)" json:"type"`
// Product Info
ProductName string `xorm:"varchar(100)" json:"productName"`
ProductDisplayName string `xorm:"varchar(100)" json:"productDisplayName"`
Detail string `xorm:"varchar(255)" json:"detail"`
Tag string `xorm:"varchar(100)" json:"tag"`
Currency string `xorm:"varchar(100)" json:"currency"`
Amount float64 `json:"amount"`
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
// User Info
User string `xorm:"varchar(100)" json:"user"`
Application string `xorm:"varchar(100)" json:"application"`
Payment string `xorm:"varchar(100)" json:"payment"`
State string `xorm:"varchar(100)" json:"state"`
}
func GetTransactionCount(owner, field, value string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "")
return session.Count(&Transaction{Owner: owner})
}
func GetTransactions(owner string) ([]*Transaction, error) {
transactions := []*Transaction{}
err := ormer.Engine.Desc("created_time").Find(&transactions, &Transaction{Owner: owner})
if err != nil {
return nil, err
}
return transactions, nil
}
func GetUserTransactions(owner, user string) ([]*Transaction, error) {
transactions := []*Transaction{}
err := ormer.Engine.Desc("created_time").Find(&transactions, &Transaction{Owner: owner, User: user})
if err != nil {
return nil, err
}
return transactions, nil
}
func GetPaginationTransactions(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Transaction, error) {
transactions := []*Transaction{}
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
err := session.Find(&transactions, &Transaction{Owner: owner})
if err != nil {
return nil, err
}
return transactions, nil
}
func getTransaction(owner string, name string) (*Transaction, error) {
if owner == "" || name == "" {
return nil, nil
}
transaction := Transaction{Owner: owner, Name: name}
existed, err := ormer.Engine.Get(&transaction)
if err != nil {
return nil, err
}
if existed {
return &transaction, nil
} else {
return nil, nil
}
}
func GetTransaction(id string) (*Transaction, error) {
owner, name := util.GetOwnerAndNameFromId(id)
return getTransaction(owner, name)
}
func UpdateTransaction(id string, transaction *Transaction) (bool, error) {
owner, name := util.GetOwnerAndNameFromId(id)
if p, err := getTransaction(owner, name); err != nil {
return false, err
} else if p == nil {
return false, nil
}
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(transaction)
if err != nil {
return false, err
}
return affected != 0, nil
}
func AddTransaction(transaction *Transaction) (bool, error) {
affected, err := ormer.Engine.Insert(transaction)
if err != nil {
return false, err
}
return affected != 0, nil
}
func DeleteTransaction(transaction *Transaction) (bool, error) {
affected, err := ormer.Engine.ID(core.PK{transaction.Owner, transaction.Name}).Delete(&Transaction{})
if err != nil {
return false, err
}
return affected != 0, nil
}
func (transaction *Transaction) GetId() string {
return fmt.Sprintf("%s/%s", transaction.Owner, transaction.Name)
}

View File

@ -15,7 +15,9 @@
package object package object
import ( import (
"encoding/json"
"fmt" "fmt"
"reflect"
"strconv" "strconv"
"strings" "strings"
@ -84,6 +86,8 @@ type User struct {
Score int `json:"score"` Score int `json:"score"`
Karma int `json:"karma"` Karma int `json:"karma"`
Ranking int `json:"ranking"` Ranking int `json:"ranking"`
Balance float64 `json:"balance"`
Currency string `xorm:"varchar(100)" json:"currency"`
IsDefaultAvatar bool `json:"isDefaultAvatar"` IsDefaultAvatar bool `json:"isDefaultAvatar"`
IsOnline bool `json:"isOnline"` IsOnline bool `json:"isOnline"`
IsAdmin bool `json:"isAdmin"` IsAdmin bool `json:"isAdmin"`
@ -221,6 +225,26 @@ type ManagedAccount struct {
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"` SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
} }
func GetUserFieldStringValue(user *User, fieldName string) (bool, string, error) {
val := reflect.ValueOf(*user)
fieldValue := val.FieldByName(fieldName)
if !fieldValue.IsValid() {
return false, "", nil
}
if fieldValue.Kind() == reflect.String {
return true, fieldValue.String(), nil
}
marshalValue, err := json.Marshal(fieldValue.Interface())
if err != nil {
return false, "", err
}
return true, string(marshalValue), nil
}
func GetGlobalUserCount(field, value string) (int64, error) { func GetGlobalUserCount(field, value string) (int64, error) {
session := GetSession("", -1, -1, field, value, "", "") session := GetSession("", -1, -1, field, value, "", "")
return session.Count(&User{}) return session.Count(&User{})
@ -653,7 +677,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
} }
} }
if isAdmin { if isAdmin {
columns = append(columns, "name", "email", "phone", "country_code", "type") columns = append(columns, "name", "id", "email", "phone", "country_code", "type")
} }
columns = append(columns, "updated_time") columns = append(columns, "updated_time")
@ -999,6 +1023,10 @@ func userChangeTrigger(oldName string, newName string) error {
} }
for _, permission := range permissions { for _, permission := range permissions {
for j, u := range permission.Users { for j, u := range permission.Users {
if u == "*" {
continue
}
// u = organization/username // u = organization/username
owner, name := util.GetOwnerAndNameFromId(u) owner, name := util.GetOwnerAndNameFromId(u)
if name == oldName { if name == oldName {

View File

@ -18,8 +18,11 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"reflect" "reflect"
"regexp"
"strings" "strings"
"github.com/casdoor/casdoor/i18n"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
"github.com/casdoor/casdoor/idp" "github.com/casdoor/casdoor/idp"
@ -74,6 +77,12 @@ func GetUserByFields(organization string, field string) (*User, error) {
return user, err return user, err
} }
// check user ID
user, err = GetUserByField(organization, "id", field)
if user != nil || err != nil {
return user, err
}
// check ID card // check ID card
user, err = GetUserByField(organization, "id_card", field) user, err = GetUserByField(organization, "id_card", field)
if user != nil || err != nil { if user != nil || err != nil {
@ -328,6 +337,31 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
itemsChanged = append(itemsChanged, item) itemsChanged = append(itemsChanged, item)
} }
if oldUser.Gender != newUser.Gender {
item := GetAccountItemByName("Gender", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Birthday != newUser.Birthday {
item := GetAccountItemByName("Birthday", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Education != newUser.Education {
item := GetAccountItemByName("Education", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.IdCard != newUser.IdCard {
item := GetAccountItemByName("ID card", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.IdCardType != newUser.IdCardType {
item := GetAccountItemByName("ID card type", organization)
itemsChanged = append(itemsChanged, item)
}
oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties) oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties)
newUserPropertiesJson, _ := json.Marshal(newUser.Properties) newUserPropertiesJson, _ := json.Marshal(newUser.Properties)
if string(oldUserPropertiesJson) != string(newUserPropertiesJson) { if string(oldUserPropertiesJson) != string(newUserPropertiesJson) {
@ -372,10 +406,33 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
itemsChanged = append(itemsChanged, item) itemsChanged = append(itemsChanged, item)
} }
for i := range itemsChanged { for _, accountItem := range itemsChanged {
if pass, err := CheckAccountItemModifyRule(itemsChanged[i], isAdmin, lang); !pass {
if pass, err := CheckAccountItemModifyRule(accountItem, isAdmin, lang); !pass {
return pass, err return pass, err
} }
exist, userValue, err := GetUserFieldStringValue(newUser, util.SpaceToCamel(accountItem.Name))
if err != nil {
return false, err.Error()
}
if !exist {
continue
}
if accountItem.Regex == "" {
continue
}
regexSignupItem, err := regexp.Compile(accountItem.Regex)
if err != nil {
return false, err.Error()
}
matched := regexSignupItem.MatchString(userValue)
if !matched {
return false, fmt.Sprintf(i18n.Translate(lang, "check:The value \"%s\" for account field \"%s\" doesn't match the account item regex"), userValue, accountItem.Name)
}
} }
return true, "" return true, ""
} }

View File

@ -66,6 +66,7 @@ func IsAllowSend(user *User, remoteAddr, recordType string) error {
if user != nil { if user != nil {
record.User = user.GetId() record.User = user.GetId()
} }
has, err := ormer.Engine.Desc("created_time").Get(&record) has, err := ormer.Engine.Desc("created_time").Get(&record)
if err != nil { if err != nil {
return err return err
@ -94,15 +95,18 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1) content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
} }
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil { err := IsAllowSend(user, remoteAddr, provider.Category)
if err != nil {
return err return err
} }
if err := SendEmail(provider, title, content, dest, sender); err != nil { err = SendEmail(provider, title, content, dest, sender)
if err != nil {
return err return err
} }
if err := AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code); err != nil { err = AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code)
if err != nil {
return err return err
} }
@ -110,7 +114,8 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
} }
func SendVerificationCodeToPhone(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error { func SendVerificationCodeToPhone(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
if err := IsAllowSend(user, remoteAddr, provider.Category); err != nil { err := IsAllowSend(user, remoteAddr, provider.Category)
if err != nil {
return err return err
} }
@ -119,11 +124,13 @@ func SendVerificationCodeToPhone(organization *Organization, user *User, provide
code = organization.MasterVerificationCode code = organization.MasterVerificationCode
} }
if err := SendSms(provider, code, dest); err != nil { err = SendSms(provider, code, dest)
if err != nil {
return err return err
} }
if err := AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code); err != nil { err = AddToVerificationRecord(user, provider, remoteAddr, provider.Category, dest, code)
if err != nil {
return err return err
} }
@ -158,6 +165,7 @@ func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordT
func getVerificationRecord(dest string) (*VerificationRecord, error) { func getVerificationRecord(dest string) (*VerificationRecord, error) {
var record VerificationRecord var record VerificationRecord
record.Receiver = dest record.Receiver = dest
has, err := ormer.Engine.Desc("time").Where("is_used = false").Get(&record) has, err := ormer.Engine.Desc("time").Where("is_used = false").Get(&record)
if err != nil { if err != nil {
return nil, err return nil, err
@ -165,34 +173,34 @@ func getVerificationRecord(dest string) (*VerificationRecord, error) {
if !has { if !has {
return nil, nil return nil, nil
} }
return &record, nil return &record, nil
} }
func CheckVerificationCode(dest string, code string, lang string) *VerifyResult { func CheckVerificationCode(dest string, code string, lang string) (*VerifyResult, error) {
record, err := getVerificationRecord(dest) record, err := getVerificationRecord(dest)
if err != nil { if err != nil {
panic(err) return nil, err
} }
if record == nil { if record == nil {
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:Code has not been sent yet!")} return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:Code has not been sent yet!")}, nil
} }
timeout, err := conf.GetConfigInt64("verificationCodeTimeout") timeout, err := conf.GetConfigInt64("verificationCodeTimeout")
if err != nil { if err != nil {
panic(err) return nil, err
} }
now := time.Now().Unix() now := time.Now().Unix()
if now-record.Time > timeout*60 { if now-record.Time > timeout*60 {
return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeout)} return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeout)}, nil
} }
if record.Code != code { if record.Code != code {
return &VerifyResult{wrongCodeError, i18n.Translate(lang, "verification:Wrong verification code!")} return &VerifyResult{wrongCodeError, i18n.Translate(lang, "verification:Wrong verification code!")}, nil
} }
return &VerifyResult{VerificationSuccess, ""} return &VerifyResult{VerificationSuccess, ""}, nil
} }
func DisableVerificationCode(dest string) error { func DisableVerificationCode(dest string) error {
@ -213,7 +221,11 @@ func CheckSigninCode(user *User, dest, code, lang string) error {
return err return err
} }
result := CheckVerificationCode(dest, code, lang) result, err := CheckVerificationCode(dest, code, lang)
if err != nil {
return err
}
switch result.Code { switch result.Code {
case VerificationSuccess: case VerificationSuccess:
return resetUserSigninErrorTimes(user) return resetUserSigninErrorTimes(user)

116
object/viaSSHDialer.go Normal file
View File

@ -0,0 +1,116 @@
// Copyright 2024 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"
"database/sql/driver"
"fmt"
"net"
"time"
mssql "github.com/denisenkom/go-mssqldb"
"github.com/lib/pq"
"golang.org/x/crypto/ssh"
)
type ViaSSHDialer struct {
Client *ssh.Client
Context *context.Context
DatabaseType string
}
func (v *ViaSSHDialer) MysqlDial(ctx context.Context, addr string) (net.Conn, error) {
return v.Client.Dial("tcp", addr)
}
func (v *ViaSSHDialer) Open(s string) (_ driver.Conn, err error) {
if v.DatabaseType == "mssql" {
c, err := mssql.NewConnector(s)
if err != nil {
return nil, err
}
c.Dialer = v
return c.Connect(context.Background())
} else if v.DatabaseType == "postgres" {
return pq.DialOpen(v, s)
}
return nil, nil
}
func (v *ViaSSHDialer) Dial(network, address string) (net.Conn, error) {
return v.Client.Dial(network, address)
}
func (v *ViaSSHDialer) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
return v.Client.DialContext(ctx, network, addr)
}
func (v *ViaSSHDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
return v.Client.Dial(network, address)
}
func DialWithPassword(SshUser string, SshPassword string, SshHost string, SshPort int) (*ssh.Client, error) {
address := fmt.Sprintf("%s:%d", SshHost, SshPort)
config := &ssh.ClientConfig{
User: SshUser,
Auth: []ssh.AuthMethod{
ssh.Password(SshPassword),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
return ssh.Dial("tcp", address, config)
}
func DialWithCert(SshUser string, CertId string, SshHost string, SshPort int) (*ssh.Client, error) {
address := fmt.Sprintf("%s:%d", SshHost, SshPort)
config := &ssh.ClientConfig{
User: SshUser,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
cert, err := GetCert(CertId)
if err != nil {
return nil, err
}
signer, err := ssh.ParsePrivateKey([]byte(cert.PrivateKey))
if err != nil {
return nil, err
}
config.Auth = []ssh.AuthMethod{
ssh.PublicKeys(signer),
}
return ssh.Dial("tcp", address, config)
}
func DialWithPrivateKey(SshUser string, PrivateKey []byte, SshHost string, SshPort int) (*ssh.Client, error) {
address := fmt.Sprintf("%s:%d", SshHost, SshPort)
config := &ssh.ClientConfig{
User: SshUser,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
signer, err := ssh.ParsePrivateKey(PrivateKey)
if err != nil {
return nil, err
}
config.Auth = []ssh.AuthMethod{
ssh.PublicKeys(signer),
}
return ssh.Dial("tcp", address, config)
}

View File

@ -39,6 +39,7 @@ type Webhook struct {
Headers []*Header `xorm:"mediumtext" json:"headers"` Headers []*Header `xorm:"mediumtext" json:"headers"`
Events []string `xorm:"varchar(1000)" json:"events"` Events []string `xorm:"varchar(1000)" json:"events"`
IsUserExtended bool `json:"isUserExtended"` IsUserExtended bool `json:"isUserExtended"`
SingleOrgOnly bool `json:"singleOrgOnly"`
IsEnabled bool `json:"isEnabled"` IsEnabled bool `json:"isEnabled"`
} }

View File

@ -73,10 +73,12 @@ func getObject(ctx *context.Context) (string, string) {
} }
} }
// query == "?id=built-in/admin" if !(strings.HasPrefix(ctx.Request.URL.Path, "/api/get-") && strings.HasSuffix(ctx.Request.URL.Path, "s")) {
id := ctx.Input.Query("id") // query == "?id=built-in/admin"
if id != "" { id := ctx.Input.Query("id")
return util.GetOwnerAndNameFromIdNoCheck(id) if id != "" {
return util.GetOwnerAndNameFromIdNoCheck(id)
}
} }
owner := ctx.Input.Query("owner") owner := ctx.Input.Query("owner")

View File

@ -48,7 +48,7 @@ func CorsFilter(ctx *context.Context) {
originHostname := getHostname(origin) originHostname := getHostname(origin)
host := removePort(ctx.Request.Host) host := removePort(ctx.Request.Host)
if strings.HasPrefix(origin, "http://localhost") || strings.HasPrefix(origin, "https://localhost") || strings.HasPrefix(origin, "http://127.0.0.1") || strings.HasPrefix(origin, "http://casdoor-app") { if strings.HasPrefix(origin, "http://localhost") || strings.HasPrefix(origin, "https://localhost") || strings.HasPrefix(origin, "http://127.0.0.1") || strings.HasPrefix(origin, "http://casdoor-app") || strings.Contains(origin, ".chromiumapp.org") {
setCorsHeaders(ctx, origin) setCorsHeaders(ctx, origin)
return return
} }

View File

@ -221,6 +221,12 @@ func initAPI() {
beego.Router("/api/add-subscription", &controllers.ApiController{}, "POST:AddSubscription") beego.Router("/api/add-subscription", &controllers.ApiController{}, "POST:AddSubscription")
beego.Router("/api/delete-subscription", &controllers.ApiController{}, "POST:DeleteSubscription") beego.Router("/api/delete-subscription", &controllers.ApiController{}, "POST:DeleteSubscription")
beego.Router("/api/get-transactions", &controllers.ApiController{}, "GET:GetTransactions")
beego.Router("/api/get-transaction", &controllers.ApiController{}, "GET:GetTransaction")
beego.Router("/api/update-transaction", &controllers.ApiController{}, "POST:UpdateTransaction")
beego.Router("/api/add-transaction", &controllers.ApiController{}, "POST:AddTransaction")
beego.Router("/api/delete-transaction", &controllers.ApiController{}, "POST:DeleteTransaction")
beego.Router("/api/get-system-info", &controllers.ApiController{}, "GET:GetSystemInfo") beego.Router("/api/get-system-info", &controllers.ApiController{}, "GET:GetSystemInfo")
beego.Router("/api/get-version-info", &controllers.ApiController{}, "GET:GetVersionInfo") beego.Router("/api/get-version-info", &controllers.ApiController{}, "GET:GetVersionInfo")
beego.Router("/api/health", &controllers.ApiController{}, "GET:Health") beego.Router("/api/health", &controllers.ApiController{}, "GET:Health")
@ -233,6 +239,7 @@ func initAPI() {
beego.Router("/api/add-syncer", &controllers.ApiController{}, "POST:AddSyncer") beego.Router("/api/add-syncer", &controllers.ApiController{}, "POST:AddSyncer")
beego.Router("/api/delete-syncer", &controllers.ApiController{}, "POST:DeleteSyncer") beego.Router("/api/delete-syncer", &controllers.ApiController{}, "POST:DeleteSyncer")
beego.Router("/api/run-syncer", &controllers.ApiController{}, "GET:RunSyncer") beego.Router("/api/run-syncer", &controllers.ApiController{}, "GET:RunSyncer")
beego.Router("/api/test-syncer-db", &controllers.ApiController{}, "POST:TestSyncerDb")
beego.Router("/api/get-webhooks", &controllers.ApiController{}, "GET:GetWebhooks") beego.Router("/api/get-webhooks", &controllers.ApiController{}, "GET:GetWebhooks")
beego.Router("/api/get-webhook", &controllers.ApiController{}, "GET:GetWebhook") beego.Router("/api/get-webhook", &controllers.ApiController{}, "GET:GetWebhook")
@ -261,6 +268,10 @@ func initAPI() {
beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken") beego.Router("/api/login/oauth/refresh_token", &controllers.ApiController{}, "POST:RefreshToken")
beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken") beego.Router("/api/login/oauth/introspect", &controllers.ApiController{}, "POST:IntrospectToken")
beego.Router("/api/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/send-email", &controllers.ApiController{}, "POST:SendEmail") beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail")
beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms") beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms")
beego.Router("/api/send-notification", &controllers.ApiController{}, "POST:SendNotification") beego.Router("/api/send-notification", &controllers.ApiController{}, "POST:SendNotification")

View File

@ -19,8 +19,8 @@ import (
"github.com/casdoor/oss/qiniu" "github.com/casdoor/oss/qiniu"
) )
func NewQiniuCloudKodoStorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface { func NewQiniuCloudKodoStorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) (oss.StorageInterface, error) {
sp := qiniu.New(&qiniu.Config{ sp, err := qiniu.New(&qiniu.Config{
AccessID: clientId, AccessID: clientId,
AccessKey: clientSecret, AccessKey: clientSecret,
Region: region, Region: region,
@ -28,5 +28,5 @@ func NewQiniuCloudKodoStorageProvider(clientId string, clientSecret string, regi
Endpoint: endpoint, Endpoint: endpoint,
}) })
return sp return sp, err
} }

View File

@ -16,27 +16,27 @@ package storage
import "github.com/casdoor/oss" import "github.com/casdoor/oss"
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface { func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string) (oss.StorageInterface, error) {
switch providerType { switch providerType {
case "Local File System": case "Local File System":
return NewLocalFileSystemStorageProvider() return NewLocalFileSystemStorageProvider(), nil
case "AWS S3": case "AWS S3":
return NewAwsS3StorageProvider(clientId, clientSecret, region, bucket, endpoint) return NewAwsS3StorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
case "MinIO": case "MinIO":
return NewMinIOS3StorageProvider(clientId, clientSecret, "_", bucket, endpoint) return NewMinIOS3StorageProvider(clientId, clientSecret, "_", bucket, endpoint), nil
case "Aliyun OSS": case "Aliyun OSS":
return NewAliyunOssStorageProvider(clientId, clientSecret, region, bucket, endpoint) return NewAliyunOssStorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
case "Tencent Cloud COS": case "Tencent Cloud COS":
return NewTencentCloudCosStorageProvider(clientId, clientSecret, region, bucket, endpoint) return NewTencentCloudCosStorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
case "Azure Blob": case "Azure Blob":
return NewAzureBlobStorageProvider(clientId, clientSecret, region, bucket, endpoint) return NewAzureBlobStorageProvider(clientId, clientSecret, region, bucket, endpoint), nil
case "Qiniu Cloud Kodo": case "Qiniu Cloud Kodo":
return NewQiniuCloudKodoStorageProvider(clientId, clientSecret, region, bucket, endpoint) return NewQiniuCloudKodoStorageProvider(clientId, clientSecret, region, bucket, endpoint)
case "Google Cloud Storage": case "Google Cloud Storage":
return NewGoogleCloudStorageProvider(clientSecret, bucket, endpoint) return NewGoogleCloudStorageProvider(clientSecret, bucket, endpoint), nil
case "Synology": case "Synology":
return NewSynologyNasStorageProvider(clientId, clientSecret, endpoint) return NewSynologyNasStorageProvider(clientId, clientSecret, endpoint), nil
} }
return nil return nil, nil
} }

View File

@ -113,6 +113,15 @@ func SnakeToCamel(snake string) string {
return strings.Join(words, "") return strings.Join(words, "")
} }
func SpaceToCamel(name string) string {
words := strings.Split(name, " ")
for i := range words {
words[i] = strings.ToLower(words[i])
words[i] = strings.Title(words[i])
}
return strings.Join(words, "")
}
func GetOwnerAndNameFromId(id string) (string, string) { func GetOwnerAndNameFromId(id string) (string, string) {
tokens := strings.Split(id, "/") tokens := strings.Split(id, "/")
if len(tokens) != 2 { if len(tokens) != 2 {

View File

@ -34,7 +34,7 @@ func init() {
rePhone, _ = regexp.Compile(`(\d{3})\d*(\d{4})`) rePhone, _ = regexp.Compile(`(\d{3})\d*(\d{4})`)
ReWhiteSpace, _ = regexp.Compile(`\s`) ReWhiteSpace, _ = regexp.Compile(`\s`)
ReFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`) ReFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
ReUserName, _ = regexp.Compile("^[a-zA-Z0-9]+((?:-[a-zA-Z0-9]+)|(?:_[a-zA-Z0-9]+))*$") ReUserName, _ = regexp.Compile("^[a-zA-Z0-9]+([-._][a-zA-Z0-9]+)*$")
} }
func IsEmailValid(email string) bool { func IsEmailValid(email string) bool {

View File

@ -12,67 +12,14 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import React, {Component} from "react"; import React, {Component, Suspense, lazy} from "react";
import "./App.less"; import "./App.less";
import {Helmet} from "react-helmet"; import {Helmet} from "react-helmet";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs"; import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
import {AppstoreTwoTone, BarsOutlined, DeploymentUnitOutlined, DollarTwoTone, DownOutlined, GithubOutlined, HomeTwoTone, InfoCircleFilled, LockTwoTone, LogoutOutlined, SafetyCertificateTwoTone, SettingOutlined, SettingTwoTone, ShareAltOutlined, WalletTwoTone} from "@ant-design/icons"; import {GithubOutlined, InfoCircleFilled, ShareAltOutlined} from "@ant-design/icons";
import {Alert, Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result, Tooltip} from "antd"; import {Alert, Button, ConfigProvider, Drawer, FloatButton, Layout, Result, Tooltip} from "antd";
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom"; import {Route, Switch, withRouter} from "react-router-dom";
import AccountPage from "./account/AccountPage";
import Dashboard from "./basic/Dashboard";
import ShortcutsPage from "./basic/ShortcutsPage";
import AppListPage from "./basic/AppListPage";
import OrganizationListPage from "./OrganizationListPage";
import OrganizationEditPage from "./OrganizationEditPage";
import GroupEditPage from "./GroupEdit";
import GroupListPage from "./GroupList";
import GroupTreePage from "./GroupTreePage";
import UserListPage from "./UserListPage";
import UserEditPage from "./UserEditPage";
import InvitationListPage from "./InvitationListPage";
import InvitationEditPage from "./InvitationEditPage";
import ApplicationListPage from "./ApplicationListPage";
import ApplicationEditPage from "./ApplicationEditPage";
import ProviderListPage from "./ProviderListPage";
import ProviderEditPage from "./ProviderEditPage";
import ResourceListPage from "./ResourceListPage";
import CertListPage from "./CertListPage";
import CertEditPage from "./CertEditPage";
import RoleListPage from "./RoleListPage";
import RoleEditPage from "./RoleEditPage";
import PermissionListPage from "./PermissionListPage";
import PermissionEditPage from "./PermissionEditPage";
import ModelListPage from "./ModelListPage";
import ModelEditPage from "./ModelEditPage";
import AdapterListPage from "./AdapterListPage";
import AdapterEditPage from "./AdapterEditPage";
import EnforcerEditPage from "./EnforcerEditPage";
import EnforcerListPage from "./EnforcerListPage";
import SessionListPage from "./SessionListPage";
import TokenListPage from "./TokenListPage";
import TokenEditPage from "./TokenEditPage";
import ProductListPage from "./ProductListPage";
import ProductEditPage from "./ProductEditPage";
import ProductBuyPage from "./ProductBuyPage";
import PaymentListPage from "./PaymentListPage";
import PaymentEditPage from "./PaymentEditPage";
import PaymentResultPage from "./PaymentResultPage";
import PricingListPage from "./PricingListPage";
import PricingEditPage from "./PricingEditPage";
import PlanListPage from "./PlanListPage";
import PlanEditPage from "./PlanEditPage";
import SubscriptionListPage from "./SubscriptionListPage";
import SubscriptionEditPage from "./SubscriptionEditPage";
import SystemInfo from "./SystemInfo";
import SyncerListPage from "./SyncerListPage";
import SyncerEditPage from "./SyncerEditPage";
import WebhookListPage from "./WebhookListPage";
import WebhookEditPage from "./WebhookEditPage";
import LdapEditPage from "./LdapEditPage";
import LdapSyncPage from "./LdapSyncPage";
import MfaSetupPage from "./auth/MfaSetupPage";
import CustomGithubCorner from "./common/CustomGithubCorner"; import CustomGithubCorner from "./common/CustomGithubCorner";
import * as Conf from "./Conf"; import * as Conf from "./Conf";
@ -80,19 +27,11 @@ import * as Auth from "./auth/Auth";
import EntryPage from "./EntryPage"; import EntryPage from "./EntryPage";
import * as AuthBackend from "./auth/AuthBackend"; import * as AuthBackend from "./auth/AuthBackend";
import AuthCallback from "./auth/AuthCallback"; import AuthCallback from "./auth/AuthCallback";
import OdicDiscoveryPage from "./auth/OidcDiscoveryPage";
import SamlCallback from "./auth/SamlCallback"; import SamlCallback from "./auth/SamlCallback";
import i18next from "i18next"; import i18next from "i18next";
import {withTranslation} from "react-i18next"; import {withTranslation} from "react-i18next";
import EnableMfaNotification from "./common/notifaction/EnableMfaNotification"; const ManagementPage = lazy(() => import("./ManagementPage"));
import LanguageSelect from "./common/select/LanguageSelect"; const {Footer, Content} = Layout;
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;
import {setTwoToneColor} from "@ant-design/icons"; import {setTwoToneColor} from "@ant-design/icons";
@ -101,19 +40,23 @@ setTwoToneColor("rgb(87,52,211)");
class App extends Component { class App extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
let storageThemeAlgorithm = [];
try {
storageThemeAlgorithm = localStorage.getItem("themeAlgorithm") ? JSON.parse(localStorage.getItem("themeAlgorithm")) : ["default"];
} catch {
storageThemeAlgorithm = ["default"];
}
this.state = { this.state = {
classes: props, classes: props,
selectedMenuKey: 0, selectedMenuKey: 0,
account: undefined, account: undefined,
uri: null, uri: null,
menuVisible: false, themeAlgorithm: storageThemeAlgorithm,
themeAlgorithm: ["default"],
themeData: Conf.ThemeDefault, themeData: Conf.ThemeDefault,
logo: this.getLogo(Setting.getAlgorithmNames(Conf.ThemeDefault)), logo: this.getLogo(storageThemeAlgorithm),
requiredEnableMfa: false, requiredEnableMfa: false,
isAiAssistantOpen: false, isAiAssistantOpen: false,
}; };
Setting.initServerUrl(); Setting.initServerUrl();
Auth.initAuthWithConfig({ Auth.initAuthWithConfig({
serverUrl: Setting.ServerUrl, serverUrl: Setting.ServerUrl,
@ -227,6 +170,19 @@ class App extends Component {
}); });
if (initThemeAlgorithm) { if (initThemeAlgorithm) {
if (localStorage.getItem("themeAlgorithm")) {
let storageThemeAlgorithm = [];
try {
storageThemeAlgorithm = JSON.parse(localStorage.getItem("themeAlgorithm"));
} catch {
storageThemeAlgorithm = ["default"];
}
this.setState({
logo: this.getLogo(storageThemeAlgorithm),
themeAlgorithm: storageThemeAlgorithm,
});
return;
}
this.setState({ this.setState({
logo: this.getLogo(Setting.getAlgorithmNames(theme)), logo: this.getLogo(Setting.getAlgorithmNames(theme)),
themeAlgorithm: Setting.getAlgorithmNames(theme), themeAlgorithm: Setting.getAlgorithmNames(theme),
@ -273,385 +229,12 @@ class App extends Component {
}); });
} }
logout() {
this.setState({
expired: false,
submitted: false,
});
AuthBackend.logout()
.then((res) => {
if (res.status === "ok") {
const owner = this.state.account.owner;
this.setState({
account: null,
themeAlgorithm: ["default"],
});
clearWeb3AuthToken();
Setting.showMessage("success", i18next.t("application:Logged out successfully"));
const redirectUri = res.data2;
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
Setting.goToLink(redirectUri);
} else if (owner !== "built-in") {
Setting.goToLink(`${window.location.origin}/login/${owner}`);
} else {
Setting.goToLinkSoft(this, "/");
}
} else {
Setting.showMessage("error", `Failed to log out: ${res.msg}`);
}
});
}
onUpdateAccount(account) { onUpdateAccount(account) {
this.setState({ this.setState({
account: account, account: account,
}); });
} }
renderAvatar() {
if (this.state.account.avatar === "") {
return (
<Avatar style={{backgroundColor: Setting.getAvatarColor(this.state.account.name), verticalAlign: "middle"}} size="large">
{Setting.getShortName(this.state.account.name)}
</Avatar>
);
} else {
return (
<Avatar src={this.state.account.avatar} style={{verticalAlign: "middle"}} size="large"
icon={<AccountAvatar src={this.state.account.avatar} style={{verticalAlign: "middle"}} size={40} />}
>
{Setting.getShortName(this.state.account.name)}
</Avatar>
);
}
}
renderRightDropdown() {
const items = [];
if (this.state.requiredEnableMfa === false) {
items.push(Setting.getItem(<><SettingOutlined />&nbsp;&nbsp;{i18next.t("account:My Account")}</>,
"/account"
));
}
items.push(Setting.getItem(<><LogoutOutlined />&nbsp;&nbsp;{i18next.t("account:Logout")}</>,
"/logout"));
const onClick = (e) => {
if (e.key === "/account") {
this.props.history.push("/account");
} else if (e.key === "/subscription") {
this.props.history.push("/subscription");
} else if (e.key === "/logout") {
this.logout();
}
};
return (
<Dropdown key="/rightDropDown" menu={{items, onClick}} >
<div className="rightDropDown">
{
this.renderAvatar()
}
&nbsp;
&nbsp;
{Setting.isMobile() ? null : Setting.getShortText(Setting.getNameAtLeast(this.state.account.displayName), 30)} &nbsp; <DownOutlined />
&nbsp;
&nbsp;
&nbsp;
</div>
</Dropdown>
);
}
renderAccountMenu() {
if (this.state.account === undefined) {
return null;
} else if (this.state.account === null) {
return (
<React.Fragment>
<LanguageSelect />
</React.Fragment>
);
} else {
return (
<React.Fragment>
{this.renderRightDropdown()}
<ThemeSelect
themeAlgorithm={this.state.themeAlgorithm}
onChange={(nextThemeAlgorithm) => {
this.setState({
themeAlgorithm: nextThemeAlgorithm,
logo: this.getLogo(nextThemeAlgorithm),
});
}} />
<LanguageSelect languages={this.state.account.organization.languages} />
<Tooltip title="Click to open AI assitant">
<div className="select-box" onClick={() => {
this.setState({
isAiAssistantOpen: true,
});
}}>
<DeploymentUnitOutlined style={{fontSize: "24px", color: "rgb(77,77,77)"}} />
</div>
</Tooltip>
<OpenTour />
{Setting.isAdminUser(this.state.account) && !Setting.isMobile() && (this.state.uri.indexOf("/trees") === -1) &&
<OrganizationSelect
initValue={Setting.getOrganization()}
withAll={true}
style={{marginRight: "20px", width: "180px", display: "flex"}}
onChange={(value) => {
Setting.setOrganization(value);
}}
className="select-box"
/>
}
</React.Fragment>
);
}
}
getMenuItems() {
const res = [];
if (this.state.account === null || this.state.account === undefined) {
return [];
}
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) {
res.push(Setting.getItem(<a href={"https://casdoor.com"}>
<span style={{fontWeight: "bold", backgroundColor: "rgba(87,52,211,0.4)", marginTop: "12px", paddingLeft: "5px", paddingRight: "5px", display: "flex", alignItems: "center", height: "40px", borderRadius: "5px"}}>
🚀 SaaS Hosting 🔥
</span>
</a>, "#"));
}
res.push(Setting.getItem(<Link style={{color: "black"}} to="/organizations">{i18next.t("general:User Management")}</Link>, "/orgs", <AppstoreTwoTone />, [
Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>, "/organizations"),
Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>, "/groups"),
Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>, "/users"),
Setting.getItem(<Link to="/invitations">{i18next.t("general:Invitations")}</Link>, "/invitations"),
]));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/applications">{i18next.t("general:Identity")}</Link>, "/identity", <LockTwoTone />, [
Setting.getItem(<Link to="/applications">{i18next.t("general:Applications")}</Link>, "/applications"),
Setting.getItem(<Link to="/providers">{i18next.t("general:Providers")}</Link>, "/providers"),
Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>, "/resources"),
Setting.getItem(<Link to="/certs">{i18next.t("general:Certs")}</Link>, "/certs"),
]));
res.push(Setting.getItem(<Link style={{color: "black"}} to="/roles">{i18next.t("general:Authorization")}</Link>, "/auth", <SafetyCertificateTwoTone />, [
Setting.getItem(<Link to="/roles">{i18next.t("general:Roles")}</Link>, "/roles"),
Setting.getItem(<Link to="/permissions">{i18next.t("general:Permissions")}</Link>, "/permissions"),
Setting.getItem(<Link to="/models">{i18next.t("general:Models")}</Link>, "/models"),
Setting.getItem(<Link to="/adapters">{i18next.t("general:Adapters")}</Link>, "/adapters"),
Setting.getItem(<Link to="/enforcers">{i18next.t("general:Enforcers")}</Link>, "/enforcers"),
].filter(item => {
if (!Setting.isLocalAdminUser(this.state.account) && ["/models", "/adapters", "/enforcers"].includes(item.key)) {
return false;
} else {
return true;
}
})));
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 />, [
Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>, "/products"),
Setting.getItem(<Link to="/payments">{i18next.t("general:Payments")}</Link>, "/payments"),
Setting.getItem(<Link to="/plans">{i18next.t("general:Plans")}</Link>, "/plans"),
Setting.getItem(<Link to="/pricings">{i18next.t("general:Pricings")}</Link>, "/pricings"),
Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>, "/subscriptions"),
]));
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;
}
renderLoginIfNotLoggedIn(component) {
if (this.state.account === null) {
sessionStorage.setItem("from", window.location.pathname);
return <Redirect to="/login" />;
} else if (this.state.account === undefined) {
return null;
} else {
return component;
}
}
renderRouter() {
return (
<Switch>
<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} />)} />
<Route exact path="/organizations/:organizationName/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
<Route exact path="/trees/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupTreePage account={this.state.account} {...props} />)} />
<Route exact path="/trees/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupTreePage account={this.state.account} {...props} />)} />
<Route exact path="/groups" render={(props) => this.renderLoginIfNotLoggedIn(<GroupListPage account={this.state.account} {...props} />)} />
<Route exact path="/groups/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupEditPage account={this.state.account} {...props} />)} />
<Route exact path="/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
<Route exact path="/users/:organizationName/:userName" render={(props) => <UserEditPage account={this.state.account} {...props} />} />
<Route exact path="/invitations" render={(props) => this.renderLoginIfNotLoggedIn(<InvitationListPage account={this.state.account} {...props} />)} />
<Route exact path="/invitations/:organizationName/:invitationName" render={(props) => this.renderLoginIfNotLoggedIn(<InvitationEditPage account={this.state.account} {...props} />)} />
<Route exact path="/applications" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationListPage account={this.state.account} {...props} />)} />
<Route exact path="/applications/:organizationName/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<ApplicationEditPage account={this.state.account} {...props} />)} />
<Route exact path="/providers" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderListPage account={this.state.account} {...props} />)} />
<Route exact path="/providers/:organizationName/:providerName" render={(props) => this.renderLoginIfNotLoggedIn(<ProviderEditPage account={this.state.account} {...props} />)} />
<Route exact path="/resources" render={(props) => this.renderLoginIfNotLoggedIn(<ResourceListPage account={this.state.account} {...props} />)} />
<Route exact path="/certs" render={(props) => this.renderLoginIfNotLoggedIn(<CertListPage account={this.state.account} {...props} />)} />
<Route exact path="/certs/:organizationName/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
<Route exact path="/roles" render={(props) => this.renderLoginIfNotLoggedIn(<RoleListPage account={this.state.account} {...props} />)} />
<Route exact path="/roles/:organizationName/:roleName" render={(props) => this.renderLoginIfNotLoggedIn(<RoleEditPage account={this.state.account} {...props} />)} />
<Route exact path="/permissions" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionListPage account={this.state.account} {...props} />)} />
<Route exact path="/permissions/:organizationName/:permissionName" render={(props) => this.renderLoginIfNotLoggedIn(<PermissionEditPage account={this.state.account} {...props} />)} />
<Route exact path="/models" render={(props) => this.renderLoginIfNotLoggedIn(<ModelListPage account={this.state.account} {...props} />)} />
<Route exact path="/models/:organizationName/:modelName" render={(props) => this.renderLoginIfNotLoggedIn(<ModelEditPage account={this.state.account} {...props} />)} />
<Route exact path="/adapters" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterListPage account={this.state.account} {...props} />)} />
<Route exact path="/adapters/:organizationName/:adapterName" render={(props) => this.renderLoginIfNotLoggedIn(<AdapterEditPage account={this.state.account} {...props} />)} />
<Route exact path="/enforcers" render={(props) => this.renderLoginIfNotLoggedIn(<EnforcerListPage account={this.state.account} {...props} />)} />
<Route exact path="/enforcers/:organizationName/:enforcerName" render={(props) => this.renderLoginIfNotLoggedIn(<EnforcerEditPage account={this.state.account} {...props} />)} />
<Route exact path="/sessions" render={(props) => this.renderLoginIfNotLoggedIn(<SessionListPage account={this.state.account} {...props} />)} />
<Route exact path="/tokens" render={(props) => this.renderLoginIfNotLoggedIn(<TokenListPage account={this.state.account} {...props} />)} />
<Route exact path="/tokens/:tokenName" render={(props) => this.renderLoginIfNotLoggedIn(<TokenEditPage account={this.state.account} {...props} />)} />
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} />
<Route exact path="/products/:organizationName/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)} />
<Route exact path="/products/:organizationName/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage account={this.state.account} {...props} />)} />
<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="/plans" render={(props) => this.renderLoginIfNotLoggedIn(<PlanListPage account={this.state.account} {...props} />)} />
<Route exact path="/plans/:organizationName/:planName" render={(props) => this.renderLoginIfNotLoggedIn(<PlanEditPage account={this.state.account} {...props} />)} />
<Route exact path="/pricings" render={(props) => this.renderLoginIfNotLoggedIn(<PricingListPage account={this.state.account} {...props} />)} />
<Route exact path="/pricings/:organizationName/:pricingName" render={(props) => this.renderLoginIfNotLoggedIn(<PricingEditPage account={this.state.account} {...props} />)} />
<Route exact path="/subscriptions" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionListPage account={this.state.account} {...props} />)} />
<Route exact path="/subscriptions/:organizationName/:subscriptionName" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionEditPage account={this.state.account} {...props} />)} />
<Route exact path="/sysinfo" render={(props) => this.renderLoginIfNotLoggedIn(<SystemInfo account={this.state.account} {...props} />)} />
<Route exact path="/syncers" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerListPage account={this.state.account} {...props} />)} />
<Route exact path="/syncers/:syncerName" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerEditPage account={this.state.account} {...props} />)} />
<Route exact path="/webhooks" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookListPage account={this.state.account} {...props} />)} />
<Route exact path="/webhooks/:webhookName" render={(props) => this.renderLoginIfNotLoggedIn(<WebhookEditPage account={this.state.account} {...props} />)} />
<Route exact path="/ldap/:organizationName/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapEditPage account={this.state.account} {...props} />)} />
<Route exact path="/ldap/sync/:organizationName/:ldapId" render={(props) => this.renderLoginIfNotLoggedIn(<LdapSyncPage 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 path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
</Switch>
);
}
onClose = () => {
this.setState({
menuVisible: false,
});
};
showMenu = () => {
this.setState({
menuVisible: true,
});
};
isWithoutCard() {
return Setting.isMobile() || window.location.pathname.startsWith("/trees");
}
renderContent() {
const onClick = ({key}) => {
if (key !== "/swagger" && key !== "/records") {
if (this.state.requiredEnableMfa) {
Setting.showMessage("info", "Please enable MFA first!");
} else {
this.props.history.push(key);
}
}
};
const menuStyleRight = Setting.isAdminUser(this.state.account) && !Setting.isMobile() ? "calc(180px + 280px)" : "280px";
return (
<Layout id="parent-area">
<EnableMfaNotification account={this.state.account} />
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: this.state.themeAlgorithm.includes("dark") ? "black" : "white"}} >
{Setting.isMobile() ? null : (
<Link to={"/"}>
<div className="logo" style={{background: `url(${this.state.logo})`}} />
</Link>
)}
{this.state.requiredEnableMfa || (Setting.isMobile() ?
<React.Fragment>
<Drawer title={i18next.t("general:Close")} placement="left" visible={this.state.menuVisible} onClose={this.onClose}>
<Menu
items={this.getMenuItems()}
mode={"inline"}
selectedKeys={[this.state.selectedMenuKey]}
style={{lineHeight: "64px"}}
onClick={this.onClose}
>
</Menu>
</Drawer>
<Button icon={<BarsOutlined />} onClick={this.showMenu} type="text">
{i18next.t("general:Menu")}
</Button>
</React.Fragment> :
<Menu
onClick={onClick}
items={this.getMenuItems()}
mode={"horizontal"}
selectedKeys={[this.state.selectedMenuKey]}
style={{position: "absolute", left: "145px", right: menuStyleRight}}
/>
)}
{
this.renderAccountMenu()
}
</Header>
<Content style={{display: "flex", flexDirection: "column"}} >
{this.isWithoutCard() ?
this.renderRouter() :
<Card className="content-warp-card">
{this.renderRouter()}
</Card>
}
</Content>
{
this.renderFooter()
}
{
this.renderAiAssistant()
}
</Layout>
);
}
renderFooter() { renderFooter() {
return ( return (
<React.Fragment> <React.Fragment>
@ -718,49 +301,63 @@ class App extends Component {
window.location.pathname.startsWith("/prompt") || window.location.pathname.startsWith("/prompt") ||
window.location.pathname.startsWith("/result") || window.location.pathname.startsWith("/result") ||
window.location.pathname.startsWith("/cas") || 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") || window.location.pathname.startsWith("/buy-plan") ||
window.location.pathname.startsWith("/qrcode") ; window.location.pathname.startsWith("/qrcode") ;
} }
onClick = ({key}) => {
if (key !== "/swagger" && key !== "/records") {
if (this.state.requiredEnableMfa) {
Setting.showMessage("info", "Please enable MFA first!");
} else {
this.props.history.push(key);
}
}
};
renderPage() { renderPage() {
if (this.isDoorPages()) { if (this.isDoorPages()) {
return ( return (
<Layout id="parent-area"> <ConfigProvider theme={{
<Content style={{display: "flex", justifyContent: "center"}}> algorithm: Setting.getAlgorithm(["default"]),
{ }}>
this.isEntryPages() ? <StyleProvider hashPriority="high" transformers={[legacyLogicalPropertiesTransformer]}>
<EntryPage <Layout id="parent-area">
account={this.state.account} <Content style={{display: "flex", justifyContent: "center"}}>
theme={this.state.themeData} {
onLoginSuccess={(redirectUrl) => { this.isEntryPages() ?
if (redirectUrl) { <EntryPage
localStorage.setItem("mfaRedirectUrl", redirectUrl); account={this.state.account}
} theme={this.state.themeData}
this.getAccount(); onLoginSuccess={(redirectUrl) => {
}} if (redirectUrl) {
onUpdateAccount={(account) => this.onUpdateAccount(account)} localStorage.setItem("mfaRedirectUrl", redirectUrl);
updataThemeData={this.setTheme} }
/> : this.getAccount();
<Switch> }}
<Route exact path="/callback" component={AuthCallback} /> onUpdateAccount={(account) => this.onUpdateAccount(account)}
<Route exact path="/callback/saml" component={SamlCallback} /> updataThemeData={this.setTheme}
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")} /> :
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} /> <Switch>
</Switch> <Route exact path="/callback" component={AuthCallback} />
} <Route exact path="/callback/saml" component={SamlCallback} />
</Content> <Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
{ extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
this.renderFooter() </Switch>
} }
{ </Content>
this.renderAiAssistant() {
} this.renderFooter()
</Layout> }
{
this.renderAiAssistant()
}
</Layout>
</StyleProvider>
</ConfigProvider>
); );
} }
return ( return (
<React.Fragment> <React.Fragment>
{/* { */} {/* { */}
@ -769,7 +366,49 @@ class App extends Component {
<FloatButton.BackTop /> <FloatButton.BackTop />
<CustomGithubCorner /> <CustomGithubCorner />
{ {
this.renderContent() <Suspense fallback={<div>loading</div>}>
<Layout id="parent-area">
<ManagementPage
account={this.state.account}
uri={this.state.uri}
themeData={this.state.themeData}
themeAlgorithm={this.state.themeAlgorithm}
selectedMenuKey={this.state.selectedMenuKey}
requiredEnableMfa={this.state.requiredEnableMfa}
menuVisible={this.state.menuVisible}
logo={this.state.logo}
onChangeTheme={this.setTheme}
onClick = {this.onClick}
onfinish={() => {
this.setState({requiredEnableMfa: false});
}}
openAiAssistant={() => {
this.setState({
isAiAssistantOpen: true,
});
}}
setLogoAndThemeAlgorithm={(nextThemeAlgorithm) => {
this.setState({
themeAlgorithm: nextThemeAlgorithm,
logo: this.getLogo(nextThemeAlgorithm),
});
localStorage.setItem("themeAlgorithm", JSON.stringify(nextThemeAlgorithm));
}}
setLogoutState={() => {
this.setState({
account: null,
themeAlgorithm: ["default"],
});
}}
/>
{
this.renderFooter()
}
{
this.renderAiAssistant()
}
</Layout>
</Suspense>
} }
</React.Fragment> </React.Fragment>
); );

View File

@ -54,7 +54,7 @@ img {
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background-color: #f5f5f5 !important; background-color: #f5f5f5a5 !important;
} }
} }
@ -67,8 +67,7 @@ img {
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background-color: #f5f5f5; background-color: #f5f5f5a5;
color: black;
} }
} }

View File

@ -36,6 +36,7 @@ import ThemeEditor from "./common/theme/ThemeEditor";
import {Controlled as CodeMirror} from "react-codemirror2"; import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css"; import "codemirror/lib/codemirror.css";
import SigninTable from "./table/SigninTable";
require("codemirror/theme/material-darker.css"); require("codemirror/theme/material-darker.css");
require("codemirror/mode/htmlmixed/htmlmixed"); require("codemirror/mode/htmlmixed/htmlmixed");
@ -759,7 +760,7 @@ class ApplicationEditPage extends React.Component {
</Row> </Row>
<Row> <Row>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Form CSS"), i18next.t("application:Form CSS - Tooltip"))} : {Setting.getLabel(i18next.t("application:Custom CSS"), i18next.t("application:Custom CSS - Tooltip"))} :
</Col> </Col>
<Col span={22}> <Col span={22}>
<Popover placement="right" content={ <Popover placement="right" content={
@ -771,7 +772,7 @@ class ApplicationEditPage extends React.Component {
}} }}
/> />
</div> </div>
} title={i18next.t("application:Form CSS - Edit")} trigger="click"> } title={i18next.t("application:Custom CSS - Edit")} trigger="click">
<Input value={this.state.application.formCss} style={{marginBottom: "10px"}} onChange={e => { <Input value={this.state.application.formCss} style={{marginBottom: "10px"}} onChange={e => {
this.updateApplicationField("formCss", e.target.value); this.updateApplicationField("formCss", e.target.value);
}} /> }} />
@ -780,7 +781,7 @@ class ApplicationEditPage extends React.Component {
</Row> </Row>
<Row> <Row>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Form CSS Mobile"), i18next.t("application:Form CSS Mobile - Tooltip"))} : {Setting.getLabel(i18next.t("application:Custom CSS Mobile"), i18next.t("application:Custom CSS Mobile - Tooltip"))} :
</Col> </Col>
<Col span={22}> <Col span={22}>
<Popover placement="right" content={ <Popover placement="right" content={
@ -792,7 +793,7 @@ class ApplicationEditPage extends React.Component {
}} }}
/> />
</div> </div>
} title={i18next.t("application:Form CSS Mobile - Edit")} trigger="click"> } title={i18next.t("application:Custom CSS Mobile - Edit")} trigger="click">
<Input value={this.state.application.formCssMobile} style={{marginBottom: "10px"}} onChange={e => { <Input value={this.state.application.formCssMobile} style={{marginBottom: "10px"}} onChange={e => {
this.updateApplicationField("formCssMobile", e.target.value); this.updateApplicationField("formCssMobile", e.target.value);
}} /> }} />
@ -864,6 +865,46 @@ class ApplicationEditPage extends React.Component {
} }
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Header HTML"), i18next.t("application:Header HTML - Tooltip"))} :
</Col>
<Col span={22} >
<Popover placement="right" content={
<div style={{width: "900px", height: "300px"}} >
<CodeMirror
value={this.state.application.headerHtml}
options={{mode: "htmlmixed", theme: "material-darker"}}
onBeforeChange={(editor, data, value) => {
this.updateApplicationField("headerHtml", value);
}}
/>
</div>
} title={i18next.t("application:Header HTML - Edit")} trigger="click">
<Input value={this.state.application.headerHtml} style={{marginBottom: "10px"}} onChange={e => {
this.updateApplicationField("headerHtml", e.target.value);
}} />
</Popover>
</Col>
</Row>
{
<React.Fragment>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Signin items"), i18next.t("application:Signin items - Tooltip"))} :
</Col>
<Col span={22} >
<SigninTable
title={i18next.t("application:Signin items")}
table={this.state.application.signinItems}
onUpdateTable={(value) => {
this.updateApplicationField("signinItems", value);
}}
/>
</Col>
</Row>
</React.Fragment>
}
{ {
!this.state.application.enableSignUp ? null : ( !this.state.application.enableSignUp ? null : (
<React.Fragment> <React.Fragment>

View File

@ -61,6 +61,7 @@ class ApplicationListPage extends BaseListPage {
{name: "Phone", visible: true, required: true, rule: "None"}, {name: "Phone", visible: true, required: true, rule: "None"},
{name: "Agreement", visible: true, required: true, rule: "None"}, {name: "Agreement", visible: true, required: true, rule: "None"},
], ],
grantTypes: ["authorization_code", "password", "client_credentials", "token", "id_token", "refresh_token"],
cert: "cert-built-in", cert: "cert-built-in",
redirectUris: ["http://localhost:9000/callback"], redirectUris: ["http://localhost:9000/callback"],
tokenFormat: "JWT", tokenFormat: "JWT",

View File

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

View File

@ -32,6 +32,7 @@ import {authConfig} from "./auth/Auth";
import ProductBuyPage from "./ProductBuyPage"; import ProductBuyPage from "./ProductBuyPage";
import PaymentResultPage from "./PaymentResultPage"; import PaymentResultPage from "./PaymentResultPage";
import QrCodePage from "./QrCodePage"; import QrCodePage from "./QrCodePage";
import CustomHead from "./basic/CustomHead";
class EntryPage extends React.Component { class EntryPage extends React.Component {
constructor(props) { constructor(props) {
@ -66,7 +67,6 @@ class EntryPage extends React.Component {
this.setState({ this.setState({
application: application, application: application,
}); });
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault; const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault;
this.props.updataThemeData(themeData); this.props.updataThemeData(themeData);
}; };
@ -82,7 +82,6 @@ class EntryPage extends React.Component {
Setting.showMessage("error", res.msg); Setting.showMessage("error", res.msg);
return; return;
} }
const application = res.data; const application = res.data;
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault; const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault;
this.props.updataThemeData(themeData); this.props.updataThemeData(themeData);
@ -90,33 +89,35 @@ class EntryPage extends React.Component {
}; };
return ( return (
<div className="loginBackground" <React.Fragment>
style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}> <CustomHead headerHtml={this.state.application?.headerHtml} />
<Spin size="large" spinning={this.state.application === undefined && this.state.pricing === undefined} tip={i18next.t("login:Loading")} <div className="loginBackground"
style={{margin: "0 auto"}} /> style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}>
<Switch> <Spin size="large" spinning={this.state.application === undefined && this.state.pricing === undefined} tip={i18next.t("login:Loading")}
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} applicationName={authConfig.appName} onUpdateApplication={onUpdateApplication} {...props} />)} /> style={{margin: "0 auto"}} />
<Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Switch>
<Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} applicationName={authConfig.appName} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/auto-signup/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signup"} onUpdateApplication={onUpdateApplication}{...props} />} /> <Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} /> <Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} /> <Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"saml"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} /> <Route exact path="/login/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"saml"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/result" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/result" 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="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...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="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/select-plan/:owner/:pricingName" render={(props) => <PricingPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...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="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...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/result" render={(props) => <PaymentResultPage {...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="/qrcode/:owner/:paymentName" render={(props) => <QrCodePage {...this.props} onUpdateApplication={onUpdateApplication} {...props} />} /> <Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
</Switch> <Route exact path="/qrcode/:owner/:paymentName" render={(props) => <QrCodePage {...this.props} onUpdateApplication={onUpdateApplication} {...props} />} />
</div> </Switch>
</div>
</React.Fragment>
); );
} }
} }

448
web/src/ManagementPage.js Normal file
View File

@ -0,0 +1,448 @@
// Copyright 2024 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.
import * as Setting from "./Setting";
import {Avatar, Button, Card, Drawer, Dropdown, Menu, Result, Tooltip} from "antd";
import EnableMfaNotification from "./common/notifaction/EnableMfaNotification";
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
import React, {useState} from "react";
import i18next from "i18next";
import {
AppstoreTwoTone,
BarsOutlined, DeploymentUnitOutlined, DollarTwoTone, DownOutlined,
HomeTwoTone,
LockTwoTone, LogoutOutlined,
SafetyCertificateTwoTone, SettingOutlined, SettingTwoTone,
WalletTwoTone
} from "@ant-design/icons";
import Dashboard from "./basic/Dashboard";
import AppListPage from "./basic/AppListPage";
import ShortcutsPage from "./basic/ShortcutsPage";
import AccountPage from "./account/AccountPage";
import OrganizationListPage from "./OrganizationListPage";
import OrganizationEditPage from "./OrganizationEditPage";
import UserListPage from "./UserListPage";
import GroupTreePage from "./GroupTreePage";
import GroupListPage from "./GroupList";
import GroupEditPage from "./GroupEdit";
import UserEditPage from "./UserEditPage";
import InvitationListPage from "./InvitationListPage";
import InvitationEditPage from "./InvitationEditPage";
import ApplicationListPage from "./ApplicationListPage";
import ApplicationEditPage from "./ApplicationEditPage";
import ProviderListPage from "./ProviderListPage";
import ProviderEditPage from "./ProviderEditPage";
import RecordListPage from "./RecordListPage";
import ResourceListPage from "./ResourceListPage";
import CertListPage from "./CertListPage";
import CertEditPage from "./CertEditPage";
import RoleListPage from "./RoleListPage";
import RoleEditPage from "./RoleEditPage";
import PermissionListPage from "./PermissionListPage";
import PermissionEditPage from "./PermissionEditPage";
import ModelListPage from "./ModelListPage";
import ModelEditPage from "./ModelEditPage";
import AdapterListPage from "./AdapterListPage";
import AdapterEditPage from "./AdapterEditPage";
import EnforcerListPage from "./EnforcerListPage";
import EnforcerEditPage from "./EnforcerEditPage";
import SessionListPage from "./SessionListPage";
import TokenListPage from "./TokenListPage";
import TokenEditPage from "./TokenEditPage";
import ProductListPage from "./ProductListPage";
import ProductEditPage from "./ProductEditPage";
import ProductBuyPage from "./ProductBuyPage";
import PaymentListPage from "./PaymentListPage";
import PaymentEditPage from "./PaymentEditPage";
import PaymentResultPage from "./PaymentResultPage";
import PlanListPage from "./PlanListPage";
import PlanEditPage from "./PlanEditPage";
import PricingListPage from "./PricingListPage";
import PricingEditPage from "./PricingEditPage";
import SubscriptionListPage from "./SubscriptionListPage";
import SubscriptionEditPage from "./SubscriptionEditPage";
import SystemInfo from "./SystemInfo";
import SyncerListPage from "./SyncerListPage";
import SyncerEditPage from "./SyncerEditPage";
import WebhookListPage from "./WebhookListPage";
import WebhookEditPage from "./WebhookEditPage";
import LdapEditPage from "./LdapEditPage";
import LdapSyncPage from "./LdapSyncPage";
import MfaSetupPage from "./auth/MfaSetupPage";
import OdicDiscoveryPage from "./auth/OidcDiscoveryPage";
import * as Conf from "./Conf";
import LanguageSelect from "./common/select/LanguageSelect";
import ThemeSelect from "./common/select/ThemeSelect";
import OpenTour from "./common/OpenTour";
import OrganizationSelect from "./common/select/OrganizationSelect";
import AccountAvatar from "./account/AccountAvatar";
import {Content, Header} from "antd/es/layout/layout";
import * as AuthBackend from "./auth/AuthBackend";
import {clearWeb3AuthToken} from "./auth/Web3Auth";
import TransactionListPage from "./TransactionListPage";
import TransactionEditPage from "./TransactionEditPage";
function ManagementPage(props) {
const [menuVisible, setMenuVisible] = useState(false);
function logout() {
AuthBackend.logout()
.then((res) => {
if (res.status === "ok") {
const owner = props.account.owner;
props.setLogoutState();
clearWeb3AuthToken();
Setting.showMessage("success", i18next.t("application:Logged out successfully"));
const redirectUri = res.data2;
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
Setting.goToLink(redirectUri);
} else if (owner !== "built-in") {
Setting.goToLink(`${window.location.origin}/login/${owner}`);
} else {
Setting.goToLinkSoft({props}, "/");
}
} else {
Setting.showMessage("error", `Failed to log out: ${res.msg}`);
}
});
}
function renderAvatar() {
if (props.account.avatar === "") {
return (
<Avatar style={{backgroundColor: Setting.getAvatarColor(props.account.name), verticalAlign: "middle"}} size="large">
{Setting.getShortName(props.account.name)}
</Avatar>
);
} else {
return (
<Avatar src={props.account.avatar} style={{verticalAlign: "middle"}} size="large"
icon={<AccountAvatar src={props.account.avatar} style={{verticalAlign: "middle"}} size={40} />}
>
{Setting.getShortName(props.account.name)}
</Avatar>
);
}
}
function renderRightDropdown() {
const items = [];
if (props.requiredEnableMfa === false) {
items.push(Setting.getItem(<><SettingOutlined />&nbsp;&nbsp;{i18next.t("account:My Account")}</>,
"/account"
));
}
items.push(Setting.getItem(<><LogoutOutlined />&nbsp;&nbsp;{i18next.t("account:Logout")}</>,
"/logout"));
const onClick = (e) => {
if (e.key === "/account") {
props.history.push("/account");
} else if (e.key === "/subscription") {
props.history.push("/subscription");
} else if (e.key === "/logout") {
logout();
}
};
return (
<Dropdown key="/rightDropDown" menu={{items, onClick}} >
<div className="rightDropDown">
{
renderAvatar()
}
&nbsp;
&nbsp;
{Setting.isMobile() ? null : Setting.getShortText(Setting.getNameAtLeast(props.account.displayName), 30)} &nbsp; <DownOutlined />
&nbsp;
&nbsp;
&nbsp;
</div>
</Dropdown>
);
}
function renderAccountMenu() {
if (props.account === undefined) {
return null;
} else if (props.account === null) {
return (
<React.Fragment>
<LanguageSelect />
</React.Fragment>
);
} else {
return (
<React.Fragment>
{renderRightDropdown()}
<ThemeSelect
themeAlgorithm={props.themeAlgorithm}
onChange={props.setLogoAndThemeAlgorithm} />
<LanguageSelect languages={props.account.organization.languages} />
<Tooltip title="Click to open AI assitant">
<div className="select-box" onClick={props.openAiAssistant}>
<DeploymentUnitOutlined style={{fontSize: "24px"}} />
</div>
</Tooltip>
<OpenTour />
{Setting.isAdminUser(props.account) && !Setting.isMobile() && (props.uri.indexOf("/trees") === -1) &&
<OrganizationSelect
initValue={Setting.getOrganization()}
withAll={true}
style={{marginRight: "20px", width: "180px", display: "flex"}}
onChange={(value) => {
Setting.setOrganization(value);
}}
className="select-box"
/>
}
</React.Fragment>
);
}
}
function getMenuItems() {
const res = [];
if (props.account === null || props.account === undefined) {
return [];
}
const textColor = props.themeAlgorithm.includes("dark") ? "white" : "black";
const twoToneColor = props.themeData.colorPrimary;
res.push(Setting.getItem(<Link style={{color: textColor}} to="/">{i18next.t("general:Home")}</Link>, "/home", <HomeTwoTone twoToneColor={twoToneColor} />, [
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(props.account);
})));
if (Setting.isLocalAdminUser(props.account)) {
if (Conf.ShowGithubCorner) {
res.push(Setting.getItem(<a href={"https://casdoor.com"}>
<span style={{fontWeight: "bold", backgroundColor: "rgba(87,52,211,0.4)", marginTop: "12px", paddingLeft: "5px", paddingRight: "5px", display: "flex", alignItems: "center", height: "40px", borderRadius: "5px"}}>
🚀 SaaS Hosting 🔥
</span>
</a>, "#"));
}
res.push(Setting.getItem(<Link style={{color: textColor}} to="/organizations">{i18next.t("general:User Management")}</Link>, "/orgs", <AppstoreTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>, "/organizations"),
Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>, "/groups"),
Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>, "/users"),
Setting.getItem(<Link to="/invitations">{i18next.t("general:Invitations")}</Link>, "/invitations"),
]));
res.push(Setting.getItem(<Link style={{color: textColor}} to="/applications">{i18next.t("general:Identity")}</Link>, "/identity", <LockTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/applications">{i18next.t("general:Applications")}</Link>, "/applications"),
Setting.getItem(<Link to="/providers">{i18next.t("general:Providers")}</Link>, "/providers"),
Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>, "/resources"),
Setting.getItem(<Link to="/certs">{i18next.t("general:Certs")}</Link>, "/certs"),
]));
res.push(Setting.getItem(<Link style={{color: textColor}} to="/roles">{i18next.t("general:Authorization")}</Link>, "/auth", <SafetyCertificateTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/roles">{i18next.t("general:Roles")}</Link>, "/roles"),
Setting.getItem(<Link to="/permissions">{i18next.t("general:Permissions")}</Link>, "/permissions"),
Setting.getItem(<Link to="/models">{i18next.t("general:Models")}</Link>, "/models"),
Setting.getItem(<Link to="/adapters">{i18next.t("general:Adapters")}</Link>, "/adapters"),
Setting.getItem(<Link to="/enforcers">{i18next.t("general:Enforcers")}</Link>, "/enforcers"),
].filter(item => {
if (!Setting.isLocalAdminUser(props.account) && ["/models", "/adapters", "/enforcers"].includes(item.key)) {
return false;
} else {
return true;
}
})));
res.push(Setting.getItem(<Link style={{color: textColor}} to="/sessions">{i18next.t("general:Logging & Auditing")}</Link>, "/logs", <WalletTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/sessions">{i18next.t("general:Sessions")}</Link>, "/sessions"),
Conf.CasvisorUrl ? Setting.getItem(<a target="_blank" rel="noreferrer" href={Conf.CasvisorUrl}>{i18next.t("general:Records")}</a>, "/records")
: 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: textColor}} to="/products">{i18next.t("general:Business & Payments")}</Link>, "/business", <DollarTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>, "/products"),
Setting.getItem(<Link to="/payments">{i18next.t("general:Payments")}</Link>, "/payments"),
Setting.getItem(<Link to="/plans">{i18next.t("general:Plans")}</Link>, "/plans"),
Setting.getItem(<Link to="/pricings">{i18next.t("general:Pricings")}</Link>, "/pricings"),
Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>, "/subscriptions"),
Setting.getItem(<Link to="/transactions">{i18next.t("general:Transactions")}</Link>, "/transactions"),
]));
if (Setting.isAdminUser(props.account)) {
res.push(Setting.getItem(<Link style={{color: textColor}} to="/sysinfo">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone twoToneColor={twoToneColor} />, [
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: textColor}} to="/syncers">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks")]));
}
}
return res;
}
function renderLoginIfNotLoggedIn(component) {
if (props.account === null) {
sessionStorage.setItem("from", window.location.pathname);
return <Redirect to="/login" />;
} else if (props.account === undefined) {
return null;
} else {
return component;
}
}
function renderRouter() {
const account = props.account;
const onChangeTheme = props.onChangeTheme;
const onfinish = props.onfinish;
return (
<Switch>
<Route exact path="/" render={(props) => renderLoginIfNotLoggedIn(<Dashboard account={account} {...props} />)} />
<Route exact path="/apps" render={(props) => renderLoginIfNotLoggedIn(<AppListPage account={account} {...props} />)} />
<Route exact path="/shortcuts" render={(props) => renderLoginIfNotLoggedIn(<ShortcutsPage account={account} {...props} />)} />
<Route exact path="/account" render={(props) => renderLoginIfNotLoggedIn(<AccountPage account={account} {...props} />)} />
<Route exact path="/organizations" render={(props) => renderLoginIfNotLoggedIn(<OrganizationListPage account={account} {...props} />)} />
<Route exact path="/organizations/:organizationName" render={(props) => renderLoginIfNotLoggedIn(<OrganizationEditPage account={account} onChangeTheme={onChangeTheme} {...props} />)} />
<Route exact path="/organizations/:organizationName/users" render={(props) => renderLoginIfNotLoggedIn(<UserListPage account={account} {...props} />)} />
<Route exact path="/trees/:organizationName" render={(props) => renderLoginIfNotLoggedIn(<GroupTreePage account={account} {...props} />)} />
<Route exact path="/trees/:organizationName/:groupName" render={(props) => renderLoginIfNotLoggedIn(<GroupTreePage account={account} {...props} />)} />
<Route exact path="/groups" render={(props) => renderLoginIfNotLoggedIn(<GroupListPage account={account} {...props} />)} />
<Route exact path="/groups/:organizationName/:groupName" render={(props) => renderLoginIfNotLoggedIn(<GroupEditPage account={account} {...props} />)} />
<Route exact path="/users" render={(props) => renderLoginIfNotLoggedIn(<UserListPage account={account} {...props} />)} />
<Route exact path="/users/:organizationName/:userName" render={(props) => <UserEditPage account={account} {...props} />} />
<Route exact path="/invitations" render={(props) => renderLoginIfNotLoggedIn(<InvitationListPage account={account} {...props} />)} />
<Route exact path="/invitations/:organizationName/:invitationName" render={(props) => renderLoginIfNotLoggedIn(<InvitationEditPage account={account} {...props} />)} />
<Route exact path="/applications" render={(props) => renderLoginIfNotLoggedIn(<ApplicationListPage account={account} {...props} />)} />
<Route exact path="/applications/:organizationName/:applicationName" render={(props) => renderLoginIfNotLoggedIn(<ApplicationEditPage account={account} {...props} />)} />
<Route exact path="/providers" render={(props) => renderLoginIfNotLoggedIn(<ProviderListPage account={account} {...props} />)} />
<Route exact path="/providers/:organizationName/:providerName" render={(props) => renderLoginIfNotLoggedIn(<ProviderEditPage account={account} {...props} />)} />
<Route exact path="/records" render={(props) => renderLoginIfNotLoggedIn(<RecordListPage account={account} {...props} />)} />
<Route exact path="/resources" render={(props) => renderLoginIfNotLoggedIn(<ResourceListPage account={account} {...props} />)} />
<Route exact path="/certs" render={(props) => renderLoginIfNotLoggedIn(<CertListPage account={account} {...props} />)} />
<Route exact path="/certs/:organizationName/:certName" render={(props) => renderLoginIfNotLoggedIn(<CertEditPage account={account} {...props} />)} />
<Route exact path="/roles" render={(props) => renderLoginIfNotLoggedIn(<RoleListPage account={account} {...props} />)} />
<Route exact path="/roles/:organizationName/:roleName" render={(props) => renderLoginIfNotLoggedIn(<RoleEditPage account={account} {...props} />)} />
<Route exact path="/permissions" render={(props) => renderLoginIfNotLoggedIn(<PermissionListPage account={account} {...props} />)} />
<Route exact path="/permissions/:organizationName/:permissionName" render={(props) => renderLoginIfNotLoggedIn(<PermissionEditPage account={account} {...props} />)} />
<Route exact path="/models" render={(props) => renderLoginIfNotLoggedIn(<ModelListPage account={account} {...props} />)} />
<Route exact path="/models/:organizationName/:modelName" render={(props) => renderLoginIfNotLoggedIn(<ModelEditPage account={account} {...props} />)} />
<Route exact path="/adapters" render={(props) => renderLoginIfNotLoggedIn(<AdapterListPage account={account} {...props} />)} />
<Route exact path="/adapters/:organizationName/:adapterName" render={(props) => renderLoginIfNotLoggedIn(<AdapterEditPage account={account} {...props} />)} />
<Route exact path="/enforcers" render={(props) => renderLoginIfNotLoggedIn(<EnforcerListPage account={account} {...props} />)} />
<Route exact path="/enforcers/:organizationName/:enforcerName" render={(props) => renderLoginIfNotLoggedIn(<EnforcerEditPage account={account} {...props} />)} />
<Route exact path="/sessions" render={(props) => renderLoginIfNotLoggedIn(<SessionListPage account={account} {...props} />)} />
<Route exact path="/tokens" render={(props) => renderLoginIfNotLoggedIn(<TokenListPage account={account} {...props} />)} />
<Route exact path="/tokens/:tokenName" render={(props) => renderLoginIfNotLoggedIn(<TokenEditPage account={account} {...props} />)} />
<Route exact path="/products" render={(props) => renderLoginIfNotLoggedIn(<ProductListPage account={account} {...props} />)} />
<Route exact path="/products/:organizationName/:productName" render={(props) => renderLoginIfNotLoggedIn(<ProductEditPage account={account} {...props} />)} />
<Route exact path="/products/:organizationName/:productName/buy" render={(props) => renderLoginIfNotLoggedIn(<ProductBuyPage account={account} {...props} />)} />
<Route exact path="/payments" render={(props) => renderLoginIfNotLoggedIn(<PaymentListPage account={account} {...props} />)} />
<Route exact path="/payments/:organizationName/:paymentName" render={(props) => renderLoginIfNotLoggedIn(<PaymentEditPage account={account} {...props} />)} />
<Route exact path="/payments/:organizationName/:paymentName/result" render={(props) => renderLoginIfNotLoggedIn(<PaymentResultPage account={account} {...props} />)} />
<Route exact path="/plans" render={(props) => renderLoginIfNotLoggedIn(<PlanListPage account={account} {...props} />)} />
<Route exact path="/plans/:organizationName/:planName" render={(props) => renderLoginIfNotLoggedIn(<PlanEditPage account={account} {...props} />)} />
<Route exact path="/pricings" render={(props) => renderLoginIfNotLoggedIn(<PricingListPage account={account} {...props} />)} />
<Route exact path="/pricings/:organizationName/:pricingName" render={(props) => renderLoginIfNotLoggedIn(<PricingEditPage account={account} {...props} />)} />
<Route exact path="/subscriptions" render={(props) => renderLoginIfNotLoggedIn(<SubscriptionListPage account={account} {...props} />)} />
<Route exact path="/subscriptions/:organizationName/:subscriptionName" render={(props) => renderLoginIfNotLoggedIn(<SubscriptionEditPage account={account} {...props} />)} />
<Route exact path="/sysinfo" render={(props) => renderLoginIfNotLoggedIn(<SystemInfo account={account} {...props} />)} />
<Route exact path="/syncers" render={(props) => renderLoginIfNotLoggedIn(<SyncerListPage account={account} {...props} />)} />
<Route exact path="/syncers/:syncerName" render={(props) => renderLoginIfNotLoggedIn(<SyncerEditPage account={account} {...props} />)} />
<Route exact path="/transactions" render={(props) => renderLoginIfNotLoggedIn(<TransactionListPage account={account} {...props} />)} />
<Route exact path="/transactions/:organizationName/:transactionName" render={(props) => renderLoginIfNotLoggedIn(<TransactionEditPage account={account} {...props} />)} />
<Route exact path="/webhooks" render={(props) => renderLoginIfNotLoggedIn(<WebhookListPage account={account} {...props} />)} />
<Route exact path="/webhooks/:webhookName" render={(props) => renderLoginIfNotLoggedIn(<WebhookEditPage account={account} {...props} />)} />
<Route exact path="/ldap/:organizationName/:ldapId" render={(props) => renderLoginIfNotLoggedIn(<LdapEditPage account={account} {...props} />)} />
<Route exact path="/ldap/sync/:organizationName/:ldapId" render={(props) => renderLoginIfNotLoggedIn(<LdapSyncPage account={account} {...props} />)} />
<Route exact path="/mfa/setup" render={(props) => renderLoginIfNotLoggedIn(<MfaSetupPage account={account} onfinish={onfinish} {...props} />)} />
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
<Route path="" render={() => <Result status="404" title="404 NOT FOUND" subTitle={i18next.t("general:Sorry, the page you visited does not exist.")}
extra={<a href="/"><Button type="primary">{i18next.t("general:Back Home")}</Button></a>} />} />
</Switch>
);
}
function isWithoutCard() {
return Setting.isMobile() || window.location.pathname.startsWith("/trees");
}
const menuStyleRight = Setting.isAdminUser(props.account) && !Setting.isMobile() ? "calc(180px + 280px)" : "280px";
const onClose = () => {
setMenuVisible(false);
};
const showMenu = () => {
setMenuVisible(true);
};
return (
<React.Fragment>
<EnableMfaNotification account={props.account} />
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: props.themeAlgorithm.includes("dark") ? "black" : "white"}} >
{Setting.isMobile() ? null : (
<Link to={"/"}>
<div className="logo" style={{background: `url(${props.logo})`}} />
</Link>
)}
{props.requiredEnableMfa || (Setting.isMobile() ?
<React.Fragment>
<Drawer title={i18next.t("general:Close")} placement="left" visible={menuVisible} onClose={onClose}>
<Menu
items={getMenuItems()}
mode={"inline"}
selectedKeys={[props.selectedMenuKey]}
style={{lineHeight: "64px"}}
onClick={onClose}
>
</Menu>
</Drawer>
<Button icon={<BarsOutlined />} onClick={showMenu} type="text">
{i18next.t("general:Menu")}
</Button>
</React.Fragment> :
<Menu
onClick={onClose}
items={getMenuItems()}
mode={"horizontal"}
selectedKeys={[props.selectedMenuKey]}
style={{position: "absolute", left: "145px", right: menuStyleRight, backgroundColor: props.themeAlgorithm.includes("dark") ? "black" : "white"}}
/>
)}
{
renderAccountMenu()
}
</Header>
<Content style={{display: "flex", flexDirection: "column"}} >
{isWithoutCard() ?
renderRouter() :
<Card className="content-warp-card">
{renderRouter()}
</Card>
}
</Content>
</React.Fragment>
);
}
export default withRouter(ManagementPage);

View File

@ -91,7 +91,7 @@ class PermissionListPage extends BaseListPage {
const {pagination} = this.state; const {pagination} = this.state;
this.fetch({pagination}); this.fetch({pagination});
} else { } else {
Setting.showMessage("error", `Users failed to upload: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to sync")}: ${res.msg}`);
} }
} else if (status === "error") { } else if (status === "error") {
Setting.showMessage("error", "File failed to upload"); Setting.showMessage("error", "File failed to upload");

View File

@ -191,7 +191,7 @@ class ProviderEditPage extends React.Component {
return Setting.getLabel(i18next.t("provider:App key"), i18next.t("provider:App key - Tooltip")); return Setting.getLabel(i18next.t("provider:App key"), i18next.t("provider:App key - Tooltip"));
} else if (provider.type === "UCloud SMS") { } else if (provider.type === "UCloud SMS") {
return Setting.getLabel(i18next.t("provider:Public key"), i18next.t("provider:Public key - Tooltip")); return Setting.getLabel(i18next.t("provider:Public key"), i18next.t("provider:Public key - Tooltip"));
} else if (provider.type === "Msg91 SMS" || provider.type === "Infobip SMS") { } else if (provider.type === "Msg91 SMS" || provider.type === "Infobip SMS" || provider.type === "OSON SMS") {
return Setting.getLabel(i18next.t("provider:Sender Id"), i18next.t("provider:Sender Id - Tooltip")); return Setting.getLabel(i18next.t("provider:Sender Id"), i18next.t("provider:Sender Id - Tooltip"));
} else { } else {
return Setting.getLabel(i18next.t("provider:Client ID"), i18next.t("provider:Client ID - Tooltip")); return Setting.getLabel(i18next.t("provider:Client ID"), i18next.t("provider:Client ID - Tooltip"));
@ -234,7 +234,7 @@ class ProviderEditPage extends React.Component {
return Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip")); return Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"));
} }
case "SMS": case "SMS":
if (provider.type === "Volc Engine SMS" || provider.type === "Amazon SNS" || provider.type === "Baidu Cloud SMS") { if (provider.type === "Volc Engine SMS" || provider.type === "Amazon SNS" || provider.type === "Baidu Cloud SMS" || provider.type === "OSON SMS") {
return Setting.getLabel(i18next.t("provider:Secret access key"), i18next.t("provider:Secret access key - Tooltip")); return Setting.getLabel(i18next.t("provider:Secret access key"), i18next.t("provider:Secret access key - Tooltip"));
} else if (provider.type === "Huawei Cloud SMS") { } else if (provider.type === "Huawei Cloud SMS") {
return Setting.getLabel(i18next.t("provider:App secret"), i18next.t("provider:AppSecret - Tooltip")); return Setting.getLabel(i18next.t("provider:App secret"), i18next.t("provider:AppSecret - Tooltip"));
@ -876,7 +876,7 @@ class ProviderEditPage extends React.Component {
</Col> </Col>
</Row> </Row>
)} )}
{["Custom HTTP SMS", "MinIO", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology"].includes(this.state.provider.type) ? null : ( {["Custom HTTP SMS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}> <Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :

239
web/src/RecordListPage.js Normal file
View File

@ -0,0 +1,239 @@
// 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.
import React from "react";
import {Link} from "react-router-dom";
import {Switch, Table} from "antd";
import * as Setting from "./Setting";
import * as RecordBackend from "./backend/RecordBackend";
import i18next from "i18next";
import moment from "moment";
import BaseListPage from "./BaseListPage";
class RecordListPage extends BaseListPage {
UNSAFE_componentWillMount() {
this.state.pagination.pageSize = 20;
const {pagination} = this.state;
this.fetch({pagination});
}
newRecord() {
return {
owner: "built-in",
name: "1234",
id: "1234",
clientIp: "::1",
timestamp: moment().format(),
organization: "built-in",
username: "admin",
requestUri: "/api/get-account",
action: "login",
isTriggered: false,
};
}
renderTable(records) {
let columns = [
{
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "320px",
sorter: true,
...this.getColumnSearchProps("name"),
},
{
title: i18next.t("general:ID"),
dataIndex: "id",
key: "id",
width: "90px",
sorter: true,
...this.getColumnSearchProps("id"),
},
{
title: i18next.t("general:Client IP"),
dataIndex: "clientIp",
key: "clientIp",
width: "150px",
sorter: true,
...this.getColumnSearchProps("clientIp"),
render: (text, record, index) => {
return (
<a target="_blank" rel="noreferrer" href={`https://db-ip.com/${text}`}>
{text}
</a>
);
},
},
{
title: i18next.t("general:Timestamp"),
dataIndex: "createdTime",
key: "createdTime",
width: "180px",
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
},
},
{
title: i18next.t("general:Organization"),
dataIndex: "organization",
key: "organization",
width: "110px",
sorter: true,
...this.getColumnSearchProps("organization"),
render: (text, record, index) => {
return (
<Link to={`/organizations/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:User"),
dataIndex: "user",
key: "user",
width: "120px",
sorter: true,
...this.getColumnSearchProps("user"),
render: (text, record, index) => {
return (
<Link to={`/users/${record.organization}/${record.user}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Method"),
dataIndex: "method",
key: "method",
width: "110px",
sorter: true,
filterMultiple: false,
filters: [
{text: "GET", value: "GET"},
{text: "HEAD", value: "HEAD"},
{text: "POST", value: "POST"},
{text: "PUT", value: "PUT"},
{text: "DELETE", value: "DELETE"},
{text: "CONNECT", value: "CONNECT"},
{text: "OPTIONS", value: "OPTIONS"},
{text: "TRACE", value: "TRACE"},
{text: "PATCH", value: "PATCH"},
],
},
{
title: i18next.t("general:Request URI"),
dataIndex: "requestUri",
key: "requestUri",
// width: '300px',
sorter: true,
...this.getColumnSearchProps("requestUri"),
},
{
title: i18next.t("general:Action"),
dataIndex: "action",
key: "action",
width: "200px",
sorter: true,
...this.getColumnSearchProps("action"),
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
return text;
},
},
{
title: i18next.t("record:Is triggered"),
dataIndex: "isTriggered",
key: "isTriggered",
width: "140px",
sorter: true,
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
if (!["signup", "login", "logout", "update-user"].includes(record.action)) {
return null;
}
return (
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />
);
},
},
];
if (Setting.isLocalAdminUser(this.props.account)) {
columns = columns.filter(column => column.key !== "name");
}
const paginationProps = {
total: this.state.pagination.total,
pageSize: this.state.pagination.pageSize,
showQuickJumper: true,
showSizeChanger: true,
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
};
return (
<div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={records} rowKey="id" size="middle" bordered pagination={paginationProps}
title={() => (
<div>
{i18next.t("general:Records")}&nbsp;&nbsp;&nbsp;&nbsp;
</div>
)}
loading={this.state.loading}
onChange={this.handleTableChange}
/>
</div>
);
}
fetch = (params = {}) => {
let field = params.searchedColumn, value = params.searchText;
const sortField = params.sortField, sortOrder = params.sortOrder;
if (params.method !== undefined && params.method !== null) {
field = "method";
value = params.method;
}
this.setState({loading: true});
RecordBackend.getRecords(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,
});
if (res.status === "ok") {
this.setState({
data: res.data,
pagination: {
...params.pagination,
total: res.data2,
},
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (res.data.includes("Please login first")) {
this.setState({
loading: false,
isAuthorized: false,
});
}
}
});
};
}
export default RecordListPage;

View File

@ -83,7 +83,7 @@ class RoleListPage extends BaseListPage {
const {pagination} = this.state; const {pagination} = this.state;
this.fetch({pagination}); this.fetch({pagination});
} else { } else {
Setting.showMessage("error", `Users failed to upload: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to sync")}: ${res.msg}`);
} }
} else if (status === "error") { } else if (status === "error") {
Setting.showMessage("error", "File failed to upload"); Setting.showMessage("error", "File failed to upload");

View File

@ -69,7 +69,7 @@ export function getThemeData(organization, application) {
} }
export function getAlgorithm(themeAlgorithmNames) { export function getAlgorithm(themeAlgorithmNames) {
return themeAlgorithmNames.map((algorithmName) => { return themeAlgorithmNames.sort().reverse().map((algorithmName) => {
if (algorithmName === "dark") { if (algorithmName === "dark") {
return theme.darkAlgorithm; return theme.darkAlgorithm;
} }
@ -143,6 +143,10 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/social_msg91.ico`, logo: `${StaticBaseUrl}/img/social_msg91.ico`,
url: "https://control.msg91.com/app/", url: "https://control.msg91.com/app/",
}, },
"OSON SMS": {
logo: "https://osonsms.com/images/osonsms-logo.svg",
url: "https://osonsms.com/",
},
"Custom HTTP SMS": { "Custom HTTP SMS": {
logo: `${StaticBaseUrl}/img/social_default.png`, logo: `${StaticBaseUrl}/img/social_default.png`,
url: "https://casdoor.org/docs/provider/sms/overview", url: "https://casdoor.org/docs/provider/sms/overview",
@ -703,6 +707,15 @@ export function goToLinkSoft(ths, link) {
ths.props.history.push(link); ths.props.history.push(link);
} }
export function goToLinkSoftOrJumpSelf(ths, link) {
if (link.startsWith("http")) {
goToLink(link);
return;
}
ths.props.history.push(link);
}
export function showMessage(type, text) { export function showMessage(type, text) {
if (type === "success") { if (type === "success") {
message.success(text); message.success(text);
@ -1005,6 +1018,7 @@ export function getProviderTypeOptions(category) {
{id: "Azure ACS", name: "Azure ACS"}, {id: "Azure ACS", name: "Azure ACS"},
{id: "Custom HTTP SMS", name: "Custom HTTP SMS"}, {id: "Custom HTTP SMS", name: "Custom HTTP SMS"},
{id: "Mock SMS", name: "Mock SMS"}, {id: "Mock SMS", name: "Mock SMS"},
{id: "OSON SMS", name: "OSON SMS"},
{id: "Infobip SMS", name: "Infobip SMS"}, {id: "Infobip SMS", name: "Infobip SMS"},
{id: "Tencent Cloud SMS", name: "Tencent Cloud SMS"}, {id: "Tencent Cloud SMS", name: "Tencent Cloud SMS"},
{id: "Baidu Cloud SMS", name: "Baidu Cloud SMS"}, {id: "Baidu Cloud SMS", name: "Baidu Cloud SMS"},
@ -1164,8 +1178,8 @@ export function getLoginLink(application) {
let url; let url;
if (application === null) { if (application === null) {
url = null; url = null;
} else if (!isPasswordEnabled(application) && window.location.pathname.includes("/auto-signup/oauth/authorize")) { } else if (window.location.pathname.includes("/signup/oauth/authorize")) {
url = window.location.href.replace("/auto-signup/oauth/authorize", "/login/oauth/authorize"); url = window.location.pathname.replace("/signup/oauth/authorize", "/login/oauth/authorize");
} else if (authConfig.appName === application.name) { } else if (authConfig.appName === application.name) {
url = "/login"; url = "/login";
} else if (application.signinUrl === "") { } else if (application.signinUrl === "") {
@ -1173,12 +1187,7 @@ export function getLoginLink(application) {
} else { } else {
url = application.signinUrl; url = application.signinUrl;
} }
return url; return url + window.location.search;
}
export function renderLoginLink(application, text) {
const url = getLoginLink(application);
return renderLink(url, text, null);
} }
export function redirectToLoginPage(application, history) { export function redirectToLoginPage(application, history) {
@ -1205,7 +1214,7 @@ function renderLink(url, text, onClick) {
); );
} else if (url.startsWith("http")) { } else if (url.startsWith("http")) {
return ( return (
<a target="_blank" rel="noopener noreferrer" style={{float: "right"}} href={url} onClick={() => { <a style={{float: "right"}} href={url} onClick={() => {
if (onClick !== null) { if (onClick !== null) {
onClick(); onClick();
} }
@ -1220,8 +1229,8 @@ export function renderSignupLink(application, text) {
let url; let url;
if (application === null) { if (application === null) {
url = null; url = null;
} else if (!isPasswordEnabled(application) && window.location.pathname.includes("/login/oauth/authorize")) { } else if (window.location.pathname.includes("/login/oauth/authorize")) {
url = window.location.href.replace("/login/oauth/authorize", "/auto-signup/oauth/authorize"); url = window.location.pathname.replace("/login/oauth/authorize", "/signup/oauth/authorize");
} else if (authConfig.appName === application.name) { } else if (authConfig.appName === application.name) {
url = "/signup"; url = "/signup";
} else { } else {
@ -1233,10 +1242,10 @@ export function renderSignupLink(application, text) {
} }
const storeSigninUrl = () => { const storeSigninUrl = () => {
sessionStorage.setItem("signinUrl", window.location.href); sessionStorage.setItem("signinUrl", window.location.pathname + window.location.search);
}; };
return renderLink(url, text, storeSigninUrl); return renderLink(url + window.location.search, text, storeSigninUrl);
} }
export function renderForgetLink(application, text) { export function renderForgetLink(application, text) {
@ -1253,7 +1262,11 @@ export function renderForgetLink(application, text) {
} }
} }
return renderLink(url, text, null); const storeSigninUrl = () => {
sessionStorage.setItem("signinUrl", window.location.pathname + window.location.search);
};
return renderLink(url, text, storeSigninUrl);
} }
export function renderHelmet(application) { export function renderHelmet(application) {

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd"; import {Button, Card, Col, Input, InputNumber, Radio, Row, Select, Switch} from "antd";
import {LinkOutlined} from "@ant-design/icons"; import {LinkOutlined} from "@ant-design/icons";
import * as SyncerBackend from "./backend/SyncerBackend"; import * as SyncerBackend from "./backend/SyncerBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
@ -23,6 +23,7 @@ import SyncerTableColumnTable from "./table/SyncerTableColumnTable";
import {Controlled as CodeMirror} from "react-codemirror2"; import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css"; import "codemirror/lib/codemirror.css";
import * as CertBackend from "./backend/CertBackend";
require("codemirror/theme/material-darker.css"); require("codemirror/theme/material-darker.css");
require("codemirror/mode/javascript/javascript"); require("codemirror/mode/javascript/javascript");
@ -32,11 +33,13 @@ class SyncerEditPage extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
certs: [],
classes: props, classes: props,
syncerName: props.match.params.syncerName, syncerName: props.match.params.syncerName,
syncer: null, syncer: null,
organizations: [], organizations: [],
mode: props.location.mode !== undefined ? props.location.mode : "edit", mode: props.location.mode !== undefined ? props.location.mode : "edit",
testDbLoading: false,
}; };
} }
@ -64,12 +67,24 @@ class SyncerEditPage extends React.Component {
}); });
} }
getCerts(owner) {
CertBackend.getCerts(owner)
.then((res) => {
this.setState({
certs: res.data || [],
});
});
}
getOrganizations() { getOrganizations() {
OrganizationBackend.getOrganizations("admin") OrganizationBackend.getOrganizations("admin")
.then((res) => { .then((res) => {
this.setState({ this.setState({
organizations: res.data || [], organizations: res.data || [],
}); });
if (res.data) {
this.getCerts(`${res.data.owner}/${res.data.name}`);
}
}); });
} }
@ -228,7 +243,7 @@ class SyncerEditPage extends React.Component {
}); });
})}> })}>
{ {
["Database", "LDAP", "Keycloak"] ["Database", "Keycloak"]
.map((item, index) => <Option key={index} value={item}>{item}</Option>) .map((item, index) => <Option key={index} value={item}>{item}</Option>)
} }
</Select> </Select>
@ -317,7 +332,7 @@ class SyncerEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"))} : {Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input value={this.state.syncer.password} onChange={e => { <Input.Password value={this.state.syncer.password} onChange={e => {
this.updateSyncerField("password", e.target.value); this.updateSyncerField("password", e.target.value);
}} /> }} />
</Col> </Col>
@ -332,6 +347,88 @@ class SyncerEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </Row>
{
this.state.syncer.databaseType === "mysql" || this.state.syncer.databaseType === "mssql" || this.state.syncer.databaseType === "postgres" ? (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:SSH type"), i18next.t("general:SSH type - Tooltip"))} :
</Col>
<Col span={22} >
<Radio.Group value={this.state.syncer.sshType} buttonStyle="solid" onChange={e => {
this.updateSyncerField("sshType", e.target.value);
}}>
<Radio.Button value="">{i18next.t("general:None")}</Radio.Button>
<Radio.Button value="password">{i18next.t("general:Password")}</Radio.Button>
<Radio.Button value="cert">{i18next.t("general:Cert")}</Radio.Button>
</Radio.Group>
</Col>
</Row>
) : null
}
{
this.state.syncer.sshType && this.state.syncer.databaseType === "mysql" || this.state.syncer.databaseType === "mssql" || this.state.syncer.databaseType === "postgres" ? (
<React.Fragment>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:SSH host"), i18next.t("provider:Host - Tooltip"))} :
</Col>
<Col span={22} >
<Input prefix={<LinkOutlined />} value={this.state.syncer.sshHost} onChange={e => {
this.updateSyncerField("sshHost", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:SSH port"), i18next.t("provider:Port - Tooltip"))} :
</Col>
<Col span={22} >
<InputNumber value={this.state.syncer.sshPort} onChange={value => {
this.updateSyncerField("sshPort", value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:SSH user"), i18next.t("general:User - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.syncer.sshUser} onChange={e => {
this.updateSyncerField("sshUser", e.target.value);
}} />
</Col>
</Row>
{
this.state.syncer.sshType === "password" && (this.state.syncer.databaseType === "mysql" || this.state.syncer.databaseType === "mssql" || this.state.syncer.databaseType === "postgres") ?
(
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:SSH password"), i18next.t("general:Password - Tooltip"))} :
</Col>
<Col span={22} >
<Input.Password value={this.state.syncer.sshPassword} onChange={e => {
this.updateSyncerField("ssh " + "sshPassword", e.target.value);
}} />
</Col>
</Row>
) : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:SSH cert"), i18next.t("general:Cert - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.syncer.cert} onChange={(value => {this.updateSyncerField("cert", value);})}>
{
this.state?.certs.map((cert, index) => <Option key={index} value={cert.name}>{cert.name}</Option>)
}
</Select>
</Col>
</Row>
)
}
</React.Fragment>
) : null
}
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:Table"), i18next.t("syncer:Table - Tooltip"))} : {Setting.getLabel(i18next.t("syncer:Table"), i18next.t("syncer:Table - Tooltip"))} :
@ -343,6 +440,31 @@ class SyncerEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:DB test"), i18next.t("provider:DB test - Tooltip"))} :
</Col>
<Col span={2} >
<Button type={"primary"} loading={this.state.testDbLoading} onClick={() => {
this.setState({testDbLoading: true});
SyncerBackend.testSyncerDb(this.state.syncer)
.then((res) => {
if (res.status === "ok") {
this.setState({testDbLoading: false});
Setting.showMessage("success", i18next.t("syncer:Connect successfully"));
} else {
this.setState({testDbLoading: false});
Setting.showMessage("error", `${i18next.t("syncer:Failed to connect")}: ${res.msg}`);
}
})
.catch(error => {
this.setState({testDbLoading: false});
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
}>{i18next.t("syncer:Test DB Connection")}</Button>
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("syncer:Table columns"), i18next.t("syncer:Table columns - Tooltip"))} : {Setting.getLabel(i18next.t("syncer:Table columns"), i18next.t("syncer:Table columns - Tooltip"))} :

View File

@ -0,0 +1,324 @@
// Copyright 2024 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.
import React from "react";
import * as TransactionBackend from "./backend/TransactionBackend";
import * as Setting from "./Setting";
import * as ApplicationBackend from "./backend/ApplicationBackend";
import {Button, Card, Col, Input, Row} from "antd";
import i18next from "i18next";
class TransactionEditPage extends React.Component {
constructor(props) {
super(props);
this.state = {
classes: props,
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
transactionName: props.match.params.transactionName,
application: null,
transaction: null,
providers: [],
mode: props.location.mode !== undefined ? props.location.mode : "edit",
};
}
UNSAFE_componentWillMount() {
this.getTransaction();
}
getTransaction() {
TransactionBackend.getTransaction(this.state.organizationName, this.state.transactionName)
.then((res) => {
if (res.data === null) {
this.props.history.push("/404");
return;
}
this.setState({
transaction: res.data,
});
Setting.scrollToDiv("invoice-area");
});
}
submitTransactionEdit(exitAfterSave) {
const transaction = Setting.deepCopy(this.state.transaction);
TransactionBackend.updateTransaction(this.state.transaction.owner, this.state.transactionName, transaction)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully saved"));
this.setState({
transactionName: this.state.transaction.name,
});
if (exitAfterSave) {
this.props.history.push("/transactions");
} else {
this.props.history.push(`/transactions/${this.state.organizationName}/${this.state.transaction.name}`);
}
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
this.updatePaymentField("name", this.state.transactionName);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
deleteTransaction() {
TransactionBackend.deleteTransaction(this.state.transaction)
.then((res) => {
if (res.status === "ok") {
this.props.history.push("/transactions");
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
parseTransactionField(key, value) {
if ([""].includes(key)) {
value = Setting.myParseInt(value);
}
return value;
}
getApplication() {
ApplicationBackend.getApplication("admin", this.state.applicationName)
.then((res) => {
if (res.data === null) {
this.props.history.push("/404");
return;
}
if (res.status === "error") {
Setting.showMessage("error", res.msg);
return;
}
const application = res.data;
if (application.grantTypes === null || application.grantTypes === undefined || application.grantTypes.length === 0) {
application.grantTypes = ["authorization_code"];
}
if (application.tags === null || application.tags === undefined) {
application.tags = [];
}
this.setState({
application: application,
});
this.getCerts(application.organization);
this.getSamlMetadata(application.enableSamlPostBinding);
});
}
renderTransaction() {
return (
<Card size="small" title={
<div>
{this.state.mode === "add" ? i18next.t("transaction:New Transaction") : i18next.t("transaction:Edit Transaction")}&nbsp;&nbsp;&nbsp;&nbsp;
<Button onClick={() => this.submitTransactionEdit(false)}>{i18next.t("general:Save")}</Button>
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitTransactionEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteTransaction()}>{i18next.t("general:Cancel")}</Button> : null}
</div>
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
<Row style={{marginTop: "10px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.owner} onChange={e => {
// this.updatePaymentField('organization', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.name} onChange={e => {
// this.updatePaymentField('name', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Display name"), i18next.t("general:Display name - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.displayName} onChange={e => {
this.updatePaymentField("displayName", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Provider"), i18next.t("general:Provider - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.provider} onChange={e => {
// this.updatePaymentField('provider', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Category"), i18next.t("provider:Category - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.category} onChange={e => {
this.updatePaymentField("displayName", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Type"), i18next.t("payment:Type - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.type} onChange={e => {
// this.updatePaymentField('type', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("payment:Product"), i18next.t("payment:Product - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.productName} onChange={e => {
// this.updatePaymentField('productName', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("product:Detail"), i18next.t("product:Detail - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.detail} onChange={e => {
// this.updatePaymentField('currency', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("user:Tag"), i18next.t("transaction:Tag - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.tag} onChange={e => {
// this.updatePaymentField('currency', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("payment:Currency"), i18next.t("payment:Currency - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.currency} onChange={e => {
// this.updatePaymentField('currency', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("transaction:Amount"), i18next.t("transaction:Amount - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.amount} onChange={e => {
// this.updatePaymentField('amount', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("product:Return URL"), i18next.t("product:Return URL - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.user} onChange={e => {
// this.updatePaymentField('amount', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:User"), i18next.t("general:User - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.user} onChange={e => {
// this.updatePaymentField('amount', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Application"), i18next.t("general:Application - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.application} onChange={e => {
// this.updatePaymentField('amount', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Payment"), i18next.t("general:Payment - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.payment} onChange={e => {
// this.updatePaymentField('amount', e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:State"), i18next.t("general:State - Tooltip"))} :
</Col>
<Col span={22} >
<Input disabled={true} value={this.state.transaction.state} onChange={e => {
// this.updatePaymentField('state', e.target.value);
}} />
</Col>
</Row>
</Card>
);
}
render() {
return (
<div>
{
this.state.transaction !== null ? this.renderTransaction() : null
}
<div style={{marginTop: "20px", marginLeft: "40px"}}>
<Button size="large" onClick={() => this.submitTransactionEdit(false)}>{i18next.t("general:Save")}</Button>
<Button style={{marginLeft: "20px"}} type="primary" size="large" onClick={() => this.submitTransactionEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} size="large" onClick={() => this.deleteTransaction()}>{i18next.t("general:Cancel")}</Button> : null}
</div>
</div>
);
}
}
export default TransactionEditPage;

View File

@ -0,0 +1,333 @@
// Copyright 2024 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.
import BaseListPage from "./BaseListPage";
import i18next from "i18next";
import {Link} from "react-router-dom";
import * as Setting from "./Setting";
import * as Provider from "./auth/Provider";
import {Button, Table} from "antd";
import PopconfirmModal from "./common/modal/PopconfirmModal";
import React from "react";
import * as TransactionBackend from "./backend/TransactionBackend";
import moment from "moment/moment";
class TransactionListPage extends BaseListPage {
newTransaction() {
const randomName = Setting.getRandomName();
const organizationName = Setting.getRequestOrganization(this.props.account);
return {
owner: organizationName,
name: `transaction_${randomName}`,
createdTime: moment().format(),
displayName: `New Transaction - ${randomName}`,
provider: "provider_pay_paypal",
category: "",
type: "PayPal",
productName: "computer-1",
productDisplayName: "A notebook computer",
detail: "This is a computer with excellent CPU, memory and disk",
tag: "Promotion-1",
currency: "USD",
amount: 0,
returnUrl: "https://door.casdoor.com/transactions",
user: "admin",
application: "",
payment: "payment_bhn1ra",
state: "Paid",
};
}
deleteTransaction(i) {
TransactionBackend.deleteTransaction(this.state.data[i])
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: {total: this.state.pagination.total - 1},
});
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
}
})
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
addTransaction() {
const newTransaction = this.newTransaction();
TransactionBackend.addTransaction(newTransaction)
.then((res) => {
if (res.status === "ok") {
this.props.history.push({pathname: `/transactions/${newTransaction.owner}/${newTransaction.name}`, mode: "add"});
Setting.showMessage("success", i18next.t("general:Successfully added"));
} else {
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
}
}
)
.catch(error => {
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
});
}
renderTable(transactions) {
const columns = [
{
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "180px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("name"),
render: (text, record, index) => {
return (
<Link to={`/transactions/${record.owner}/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Organization"),
dataIndex: "owner",
key: "owner",
width: "120px",
fixed: "left",
sorter: true,
...this.getColumnSearchProps("owner"),
render: (text, record, index) => {
return (
<Link to={`/organizations/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Provider"),
dataIndex: "provider",
key: "provider",
width: "150px",
sorter: true,
...this.getColumnSearchProps("provider"),
render: (text, record, index) => {
return (
<Link to={`/providers/${record.owner}/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:User"),
dataIndex: "user",
key: "user",
width: "120px",
sorter: true,
...this.getColumnSearchProps("user"),
render: (text, record, index) => {
return (
<Link to={`/users/${record.owner}/${text}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Created time"),
dataIndex: "createdTime",
key: "createdTime",
width: "160px",
sorter: true,
render: (text, record, index) => {
return Setting.getFormattedDate(text);
},
},
{
title: i18next.t("provider:Type"),
dataIndex: "type",
key: "type",
width: "140px",
align: "center",
filterMultiple: false,
filters: Setting.getProviderTypeOptions("Payment").map((o) => {return {text: o.id, value: o.name};}),
sorter: true,
render: (text, record, index) => {
record.category = "Payment";
return Provider.getProviderLogoWidget(record);
},
},
{
title: i18next.t("payment:Product"),
dataIndex: "productDisplayName",
key: "productDisplayName",
// width: '160px',
sorter: true,
...this.getColumnSearchProps("productDisplayName"),
render: (text, record, index) => {
return (
<Link to={`/products/${record.owner}/${record.productName}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("payment:Currency"),
dataIndex: "currency",
key: "currency",
width: "120px",
sorter: true,
...this.getColumnSearchProps("currency"),
},
{
title: i18next.t("transaction:Amount"),
dataIndex: "amount",
key: "amount",
width: "120px",
sorter: true,
...this.getColumnSearchProps("amount"),
},
{
title: i18next.t("general:User"),
dataIndex: "user",
key: "user",
width: "120px",
sorter: true,
...this.getColumnSearchProps("user"),
},
{
title: i18next.t("general:Application"),
dataIndex: "application",
key: "application",
width: "120px",
sorter: true,
...this.getColumnSearchProps("application"),
render: (text, record, index) => {
return (
<Link to={`/applications/${record.owner}/${record.application}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:Payment"),
dataIndex: "payment",
key: "payment",
width: "120px",
sorter: true,
...this.getColumnSearchProps("payment"),
render: (text, record, index) => {
return (
<Link to={`/payments/${record.owner}/${record.payment}`}>
{text}
</Link>
);
},
},
{
title: i18next.t("general:State"),
dataIndex: "state",
key: "state",
width: "120px",
sorter: true,
...this.getColumnSearchProps("state"),
},
{
title: i18next.t("general:Action"),
dataIndex: "",
key: "op",
width: "240px",
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => {
return (
<div>
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/transactions/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
<PopconfirmModal
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
onConfirm={() => this.deleteTransaction(index)}
>
</PopconfirmModal>
</div>
);
},
},
];
const paginationProps = {
total: this.state.pagination.total,
showQuickJumper: true,
showSizeChanger: true,
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
};
return (
<div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={transactions} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
title={() => (
<div>
{i18next.t("general:Transactions")}&nbsp;&nbsp;&nbsp;&nbsp;
<Button type="primary" size="small" onClick={this.addTransaction.bind(this)}>{i18next.t("general:Add")}</Button>
</div>
)}
loading={this.state.loading}
onChange={this.handleTableChange}
/>
</div>
);
}
fetch = (params = {}) => {
let field = params.searchedColumn, value = params.searchText;
const sortField = params.sortField, sortOrder = params.sortOrder;
if (params.type !== undefined && params.type !== null) {
field = "type";
value = params.type;
}
this.setState({loading: true});
TransactionBackend.getTransactions(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,
});
if (res.status === "ok") {
this.setState({
data: res.data,
pagination: {
...params.pagination,
total: res.data2,
},
searchText: params.searchText,
searchedColumn: params.searchedColumn,
});
} else {
if (Setting.isResponseDenied(res)) {
this.setState({
isAuthorized: false,
});
} else {
Setting.showMessage("error", res.msg);
}
}
});
};
}
export default TransactionListPage;

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Button, Card, Col, Input, InputNumber, List, Result, Row, Select, Space, Spin, Switch, Tag} from "antd"; import {Button, Card, Col, Form, Input, InputNumber, List, Result, Row, Select, Space, Spin, Switch, Tag} from "antd";
import {withRouter} from "react-router-dom"; import {withRouter} from "react-router-dom";
import {TotpMfaType} from "./auth/MfaSetupPage"; import {TotpMfaType} from "./auth/MfaSetupPage";
import * as GroupBackend from "./backend/GroupBackend"; import * as GroupBackend from "./backend/GroupBackend";
@ -64,7 +64,9 @@ class UserEditPage extends React.Component {
UNSAFE_componentWillMount() { UNSAFE_componentWillMount() {
this.getUser(); this.getUser();
this.getOrganizations(); if (Setting.isLocalAdminUser(this.props.account)) {
this.getOrganizations();
}
this.getApplicationsByOrganization(this.state.organizationName); this.getApplicationsByOrganization(this.state.organizationName);
this.getUserApplication(); this.getUserApplication();
this.setReturnUrl(); this.setReturnUrl();
@ -350,7 +352,9 @@ class UserEditPage extends React.Component {
{Setting.getLabel("ID", i18next.t("general:ID - Tooltip"))} : {Setting.getLabel("ID", i18next.t("general:ID - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input value={this.state.user.id} disabled={disabled} /> <Input value={this.state.user.id} disabled={disabled} onChange={e => {
this.updateUserField("id", e.target.value);
}} />
</Col> </Col>
</Row> </Row>
); );
@ -999,7 +1003,7 @@ class UserEditPage extends React.Component {
<div style={{verticalAlign: "middle", marginBottom: 10}}>{`(${i18next.t("general:empty")})`}</div> <div style={{verticalAlign: "middle", marginBottom: 10}}>{`(${i18next.t("general:empty")})`}</div>
</Col> </Col>
} }
<CropperDivModal disabled={disabled} tag={tag} setTitle={set} buttonText={`${title}...`} title={title} user={this.state.user} organization={this.state.organizations.find(organization => organization.name === this.state.organizationName)} /> <CropperDivModal disabled={disabled} tag={tag} setTitle={set} buttonText={`${title}...`} title={title} user={this.state.user} organization={this.getUserOrganization()} />
</Col> </Col>
); );
} }
@ -1016,17 +1020,27 @@ class UserEditPage extends React.Component {
</div> </div>
) )
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner"> } style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
{ <Form>
this.getUserOrganization()?.accountItems?.map(accountItem => { {
return ( this.getUserOrganization()?.accountItems?.map(accountItem => {
<React.Fragment key={accountItem.name}> return (
{ <React.Fragment key={accountItem.name}>
this.renderAccountItem(accountItem) <Form.Item name={accountItem.name}
} validateTrigger="onChange"
</React.Fragment> rules={[
); {
}) pattern: accountItem.regex ? new RegExp(accountItem.regex, "g") : null,
} message: i18next.t("user:This field value doesn't match the pattern rule"),
},
]}
style={{margin: 0}}>
{this.renderAccountItem(accountItem)}
</Form.Item>
</React.Fragment>
);
})
}
</Form>
</Card> </Card>
); );
} }

View File

@ -309,6 +309,16 @@ class WebhookEditPage extends React.Component {
</div> </div>
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("webhook:Single org only"), i18next.t("webhook:Single org only - Tooltip"))} :
</Col>
<Col span={1} >
<Switch checked={this.state.webhook.singleOrgOnly} onChange={checked => {
this.updateWebhookField("singleOrgOnly", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("general:Is enabled"), i18next.t("general:Is enabled - Tooltip"))} : {Setting.getLabel(i18next.t("general:Is enabled"), i18next.t("general:Is enabled - Tooltip"))} :

View File

@ -111,7 +111,7 @@ class WebhookListPage extends BaseListPage {
title: i18next.t("general:Created time"), title: i18next.t("general:Created time"),
dataIndex: "createdTime", dataIndex: "createdTime",
key: "createdTime", key: "createdTime",
width: "180px", width: "150px",
sorter: true, sorter: true,
render: (text, record, index) => { render: (text, record, index) => {
return Setting.getFormattedDate(text); return Setting.getFormattedDate(text);
@ -121,7 +121,7 @@ class WebhookListPage extends BaseListPage {
title: i18next.t("general:URL"), title: i18next.t("general:URL"),
dataIndex: "url", dataIndex: "url",
key: "url", key: "url",
width: "300px", width: "200px",
sorter: true, sorter: true,
...this.getColumnSearchProps("url"), ...this.getColumnSearchProps("url"),
render: (text, record, index) => { render: (text, record, index) => {
@ -138,7 +138,7 @@ class WebhookListPage extends BaseListPage {
title: i18next.t("general:Method"), title: i18next.t("general:Method"),
dataIndex: "method", dataIndex: "method",
key: "method", key: "method",
width: "120px", width: "100px",
sorter: true, sorter: true,
...this.getColumnSearchProps("method"), ...this.getColumnSearchProps("method"),
}, },
@ -146,7 +146,7 @@ class WebhookListPage extends BaseListPage {
title: i18next.t("webhook:Content type"), title: i18next.t("webhook:Content type"),
dataIndex: "contentType", dataIndex: "contentType",
key: "contentType", key: "contentType",
width: "200px", width: "140px",
sorter: true, sorter: true,
filterMultiple: false, filterMultiple: false,
filters: [ filters: [
@ -169,7 +169,19 @@ class WebhookListPage extends BaseListPage {
title: i18next.t("webhook:Is user extended"), title: i18next.t("webhook:Is user extended"),
dataIndex: "isUserExtended", dataIndex: "isUserExtended",
key: "isUserExtended", key: "isUserExtended",
width: "160px", width: "140px",
sorter: true,
render: (text, record, index) => {
return (
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />
);
},
},
{
title: i18next.t("webhook:Single org only"),
dataIndex: "singleOrgOnly",
key: "singleOrgOnly",
width: "140px",
sorter: true, sorter: true,
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
@ -183,6 +195,7 @@ class WebhookListPage extends BaseListPage {
key: "isEnabled", key: "isEnabled",
width: "120px", width: "120px",
sorter: true, sorter: true,
fixed: (Setting.isMobile()) ? "false" : "right",
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} /> <Switch disabled checkedChildren="ON" unCheckedChildren="OFF" checked={text} />

View File

@ -172,7 +172,7 @@ class AuthCallback extends React.Component {
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}${responseType}=${token}&state=${oAuthParams.state}&token_type=bearer`); Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}${responseType}=${token}&state=${oAuthParams.state}&token_type=bearer`);
} else if (responseType === "link") { } else if (responseType === "link") {
const from = innerParams.get("from"); const from = innerParams.get("from");
Setting.goToLinkSoft(this, from); Setting.goToLinkSoftOrJumpSelf(this, from);
} else if (responseType === "saml") { } else if (responseType === "saml") {
if (res.data2.method === "POST") { if (res.data2.method === "POST") {
this.setState({ this.setState({

View File

@ -47,6 +47,7 @@ class ForgetPage extends React.Component {
this.form = React.createRef(); this.form = React.createRef();
} }
componentDidMount() { componentDidMount() {
if (this.getApplicationObj() === undefined) { if (this.getApplicationObj() === undefined) {
if (this.state.applicationName !== undefined) { if (this.state.applicationName !== undefined) {
@ -153,7 +154,12 @@ class ForgetPage extends React.Component {
values.userOwner = this.getApplicationObj()?.organizationObj.name; values.userOwner = this.getApplicationObj()?.organizationObj.name;
UserBackend.setPassword(values.userOwner, values.username, "", values?.newPassword, this.state.code).then(res => { UserBackend.setPassword(values.userOwner, values.username, "", values?.newPassword, this.state.code).then(res => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.redirectToLoginPage(this.getApplicationObj(), this.props.history); const linkInStorage = sessionStorage.getItem("signinUrl");
if (linkInStorage !== null && linkInStorage !== "") {
Setting.goToLinkSoft(linkInStorage);
} else {
Setting.redirectToLoginPage(this.getApplicationObj(), this.props.history);
}
} else { } else {
Setting.showMessage("error", res.msg); Setting.showMessage("error", res.msg);
} }

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Button, Checkbox, Col, Form, Input, Result, Row, Spin, Tabs} from "antd"; import {Button, Checkbox, Col, Form, Input, Result, Spin, Tabs} from "antd";
import {ArrowLeftOutlined, LockOutlined, UserOutlined} from "@ant-design/icons"; import {ArrowLeftOutlined, LockOutlined, UserOutlined} from "@ant-design/icons";
import {withRouter} from "react-router-dom"; import {withRouter} from "react-router-dom";
import * as UserWebauthnBackend from "../backend/UserWebauthnBackend"; import * as UserWebauthnBackend from "../backend/UserWebauthnBackend";
@ -32,11 +32,11 @@ import i18next from "i18next";
import CustomGithubCorner from "../common/CustomGithubCorner"; import CustomGithubCorner from "../common/CustomGithubCorner";
import {SendCodeInput} from "../common/SendCodeInput"; import {SendCodeInput} from "../common/SendCodeInput";
import LanguageSelect from "../common/select/LanguageSelect"; import LanguageSelect from "../common/select/LanguageSelect";
import {CaptchaModal} from "../common/modal/CaptchaModal"; import {CaptchaModal, CaptchaRule} from "../common/modal/CaptchaModal";
import {CaptchaRule} from "../common/modal/CaptchaModal";
import RedirectForm from "../common/RedirectForm"; import RedirectForm from "../common/RedirectForm";
import {MfaAuthVerifyForm, NextMfa, RequiredMfa} from "./mfa/MfaAuthVerifyForm"; import {MfaAuthVerifyForm, NextMfa, RequiredMfa} from "./mfa/MfaAuthVerifyForm";
import {GoogleOneTapLoginVirtualButton} from "./GoogleLoginButton"; import {GoogleOneTapLoginVirtualButton} from "./GoogleLoginButton";
class LoginPage extends React.Component { class LoginPage extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -494,6 +494,200 @@ class LoginPage extends React.Component {
return null; return null;
} }
renderFormItem(application, signinItem) {
if (!signinItem.visible && signinItem.name !== "Forgot password?") {
return null;
}
if (signinItem.name === "Logo") {
return (
<div className="login-logo-box">
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
{
Setting.renderHelmet(application)
}
{
Setting.renderLogo(application)
}
</div>
);
} else if (signinItem.name === "Back button") {
return (
<div>
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
{
this.renderBackButton()
}
</div>
);
} else if (signinItem.name === "Languages") {
return (
<div className="login-languages">
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
<LanguageSelect languages={application.organizationObj.languages} />
</div>
);
} else if (signinItem.name === "Signin methods") {
return (
<div>
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
{this.renderMethodChoiceBox()}
</div>
)
;
} else if (signinItem.name === "Username") {
return (
<div className="login-username">
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
<Form.Item
name="username"
rules={[
{
required: true,
message: () => {
switch (this.state.loginMethod) {
case "verificationCodeEmail":
return i18next.t("login:Please input your Email!");
case "verificationCodePhone":
return i18next.t("login:Please input your Phone!");
case "ldap":
return i18next.t("login:Please input your LDAP username!");
default:
return i18next.t("login:Please input your Email or Phone!");
}
},
},
{
validator: (_, value) => {
if (value === "") {
return Promise.resolve();
}
if (this.state.loginMethod === "verificationCode") {
if (!Setting.isValidEmail(value) && !Setting.isValidPhone(value)) {
this.setState({validEmailOrPhone: false});
return Promise.reject(i18next.t("login:The input is not valid Email or phone number!"));
}
if (Setting.isValidEmail(value)) {
this.setState({validEmail: true});
} else {
this.setState({validEmail: false});
}
} else if (this.state.loginMethod === "verificationCodeEmail") {
if (!Setting.isValidEmail(value)) {
this.setState({validEmail: false});
this.setState({validEmailOrPhone: false});
return Promise.reject(i18next.t("login:The input is not valid Email!"));
} else {
this.setState({validEmail: true});
}
} else if (this.state.loginMethod === "verificationCodePhone") {
if (!Setting.isValidPhone(value)) {
this.setState({validEmailOrPhone: false});
return Promise.reject(i18next.t("login:The input is not valid phone number!"));
}
}
this.setState({validEmailOrPhone: true});
return Promise.resolve();
},
},
]}
>
<Input
id="input"
prefix={<UserOutlined className="site-form-item-icon" />}
placeholder={this.getPlaceholder()}
onChange={e => {
this.setState({
username: e.target.value,
});
}}
/>
</Form.Item>
</div>
);
} else if (signinItem.name === "Password") {
return (
<div>
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
{this.renderPasswordOrCodeInput()}
</div>
);
} else if (signinItem.name === "Forgot password?") {
return (
<div>
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
<div className="login-forget-password">
<Form.Item name="autoSignin" valuePropName="checked" noStyle>
<Checkbox style={{float: "left"}}>
{i18next.t("login:Auto sign in")}
</Checkbox>
</Form.Item>
{
signinItem.visible ? Setting.renderForgetLink(application, i18next.t("login:Forgot password?")) : null
}
</div>
</div>
);
} else if (signinItem.name === "Agreement") {
return AgreementModal.isAgreementRequired(application) ? AgreementModal.renderAgreementFormItem(application, true, {}, this) : null;
} else if (signinItem.name === "Login button") {
return (
<Form.Item className="login-button-box">
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
<Button
type="primary"
htmlType="submit"
className="login-button"
>
{
this.state.loginMethod === "webAuthn" ? i18next.t("login:Sign in with WebAuthn") :
i18next.t("login:Sign In")
}
</Button>
{
this.renderCaptchaModal(application)
}
</Form.Item>
);
} else if (signinItem.name === "Providers") {
const showForm = Setting.isPasswordEnabled(application) || Setting.isCodeSigninEnabled(application) || Setting.isWebAuthnEnabled(application) || Setting.isLdapEnabled(application);
if (signinItem.rule === "None" || signinItem.rule === "") {
signinItem.rule = showForm ? "small" : "big";
}
return (
<div>
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
<Form.Item>
{
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map(providerItem => {
return ProviderButton.renderProviderLogo(providerItem.provider, application, null, null, signinItem.rule, this.props.location);
})
}
{
this.renderOtherFormProvider(application)
}
</Form.Item>
</div>
);
} else if (signinItem.name.startsWith("Text ") || signinItem?.isCustom) {
return (
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
);
} else if (signinItem.name === "Signup link") {
return (
<div style={{width: "100%"}} className="login-signup-link">
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
{this.renderFooter(application)}
</div>
);
}
}
renderForm(application) { renderForm(application) {
if (this.state.msg !== null) { if (this.state.msg !== null) {
return Util.renderMessage(this.state.msg); return Util.renderMessage(this.state.msg);
@ -569,116 +763,10 @@ class LoginPage extends React.Component {
]} ]}
> >
</Form.Item> </Form.Item>
{this.renderMethodChoiceBox()}
<Row style={{minHeight: 130, alignItems: "center"}}>
<Col span={24}>
<Form.Item
name="username"
rules={[
{
required: true,
message: () => {
switch (this.state.loginMethod) {
case "verificationCodeEmail": return i18next.t("login:Please input your Email!");
case "verificationCodePhone": return i18next.t("login:Please input your Phone!");
case "ldap": return i18next.t("login:Please input your LDAP username!");
default: return i18next.t("login:Please input your Email or Phone!");
}
},
},
{
validator: (_, value) => {
if (value === "") {
return Promise.resolve();
}
if (this.state.loginMethod === "verificationCode") { {
if (!Setting.isValidEmail(value) && !Setting.isValidPhone(value)) { application.signinItems?.map(signinItem => this.renderFormItem(application, signinItem))
this.setState({validEmailOrPhone: false}); }
return Promise.reject(i18next.t("login:The input is not valid Email or phone number!"));
}
if (Setting.isValidEmail(value)) {
this.setState({validEmail: true});
} else {
this.setState({validEmail: false});
}
} else if (this.state.loginMethod === "verificationCodeEmail") {
if (!Setting.isValidEmail(value)) {
this.setState({validEmail: false});
this.setState({validEmailOrPhone: false});
return Promise.reject(i18next.t("login:The input is not valid Email!"));
} else {
this.setState({validEmail: true});
}
} else if (this.state.loginMethod === "verificationCodePhone") {
if (!Setting.isValidPhone(value)) {
this.setState({validEmailOrPhone: false});
return Promise.reject(i18next.t("login:The input is not valid phone number!"));
}
}
this.setState({validEmailOrPhone: true});
return Promise.resolve();
},
},
]}
>
<Input
id="input"
prefix={<UserOutlined className="site-form-item-icon" />}
placeholder={this.getPlaceholder()}
onChange={e => {
this.setState({
username: e.target.value,
});
}}
/>
</Form.Item>
</Col>
{
this.renderPasswordOrCodeInput()
}
</Row>
<div style={{display: "inline-flex", justifyContent: "space-between", width: "320px", marginBottom: AgreementModal.isAgreementRequired(application) ? "5px" : "25px"}}>
<Form.Item name="autoSignin" valuePropName="checked" noStyle>
<Checkbox style={{float: "left"}}>
{i18next.t("login:Auto sign in")}
</Checkbox>
</Form.Item>
{
Setting.renderForgetLink(application, i18next.t("login:Forgot password?"))
}
</div>
{AgreementModal.isAgreementRequired(application) ? AgreementModal.renderAgreementFormItem(application, true, {}, this) : null}
<Form.Item>
<Button
type="primary"
htmlType="submit"
style={{width: "100%", marginBottom: "5px"}}
>
{
this.state.loginMethod === "webAuthn" ? i18next.t("login:Sign in with WebAuthn") :
i18next.t("login:Sign In")
}
</Button>
{
this.renderCaptchaModal(application)
}
{
this.renderFooter(application)
}
</Form.Item>
<Form.Item>
{
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map(providerItem => {
return ProviderButton.renderProviderLogo(providerItem.provider, application, 30, 5, "small", this.props.location);
})
}
{
this.renderOtherFormProvider(application)
}
</Form.Item>
</Form> </Form>
); );
} else { } else {
@ -695,19 +783,8 @@ class LoginPage extends React.Component {
</div> </div>
<br /> <br />
{ {
application.providers?.filter(providerItem => this.isProviderVisible(providerItem)).map(providerItem => { application?.signinItems.map(signinItem => signinItem.name === "Providers" || signinItem.name === "Signup link" ? this.renderFormItem(application, signinItem) : null)
return ProviderButton.renderProviderLogo(providerItem.provider, application, 40, 10, "big", this.props.location);
})
} }
{
this.renderOtherFormProvider(application)
}
<div>
<br />
{
this.renderFooter(application)
}
</div>
</div> </div>
); );
} }
@ -760,7 +837,7 @@ class LoginPage extends React.Component {
renderFooter(application) { renderFooter(application) {
return ( return (
<span style={{float: "right"}}> <div>
{ {
!application.enableSignUp ? null : ( !application.enableSignUp ? null : (
<React.Fragment> <React.Fragment>
@ -771,7 +848,7 @@ class LoginPage extends React.Component {
</React.Fragment> </React.Fragment>
) )
} }
</span> </div>
); );
} }
@ -889,33 +966,37 @@ class LoginPage extends React.Component {
if (this.state.loginMethod === "password" || this.state.loginMethod === "ldap") { if (this.state.loginMethod === "password" || this.state.loginMethod === "ldap") {
return ( return (
<Col span={24}> <Col span={24}>
<Form.Item <div className="login-password">
name="password" <Form.Item
rules={[{required: true, message: i18next.t("login:Please input your password!")}]} name="password"
> rules={[{required: true, message: i18next.t("login:Please input your password!")}]}
<Input.Password >
prefix={<LockOutlined className="site-form-item-icon" />} <Input.Password
type="password" prefix={<LockOutlined className="site-form-item-icon" />}
placeholder={i18next.t("general:Password")} type="password"
disabled={this.state.loginMethod === "password" ? !Setting.isPasswordEnabled(application) : !Setting.isLdapEnabled(application)} placeholder={i18next.t("general:Password")}
/> disabled={this.state.loginMethod === "password" ? !Setting.isPasswordEnabled(application) : !Setting.isLdapEnabled(application)}
</Form.Item> />
</Form.Item>
</div>
</Col> </Col>
); );
} else if (this.state.loginMethod?.includes("verificationCode")) { } else if (this.state.loginMethod?.includes("verificationCode")) {
return ( return (
<Col span={24}> <Col span={24}>
<Form.Item <div className="login-password">
name="code" <Form.Item
rules={[{required: true, message: i18next.t("login:Please input your code!")}]} name="code"
> rules={[{required: true, message: i18next.t("login:Please input your code!")}]}
<SendCodeInput >
disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone} <SendCodeInput
method={"login"} disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone}
onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]} method={"login"}
application={application} onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]}
/> application={application}
</Form.Item> />
</Form.Item>
</div>
</Col> </Col>
); );
} else { } else {
@ -952,7 +1033,7 @@ class LoginPage extends React.Component {
if (items.length > 1) { if (items.length > 1) {
return ( return (
<div> <div>
<Tabs items={items} size={"small"} defaultActiveKey={this.getDefaultLoginMethod(application)} onChange={(key) => { <Tabs className="signin-methods" items={items} size={"small"} defaultActiveKey={this.getDefaultLoginMethod(application)} onChange={(key) => {
this.setState({loginMethod: key}); this.setState({loginMethod: key});
}} centered> }} centered>
</Tabs> </Tabs>
@ -1047,10 +1128,10 @@ class LoginPage extends React.Component {
} }
renderBackButton() { renderBackButton() {
if (this.state.orgChoiceMode === "None") { if (this.state.orgChoiceMode === "None" || this.props.preview === "auto") {
return ( return (
<Button type="text" size="large" icon={<ArrowLeftOutlined />} <Button type="text" size="large" icon={<ArrowLeftOutlined />}
style={{top: "65px", left: "15px", position: "absolute"}} className="back-button"
onClick={() => history.back()}> onClick={() => history.back()}>
</Button> </Button>
); );
@ -1099,16 +1180,6 @@ class LoginPage extends React.Component {
<div className="login-form"> <div className="login-form">
<div> <div>
<div> <div>
{
Setting.renderHelmet(application)
}
{
Setting.renderLogo(application)
}
{
this.renderBackButton()
}
<LanguageSelect languages={application.organizationObj.languages} style={{top: "55px", right: "5px", position: "absolute"}} />
{ {
this.renderLoginPanel(application) this.renderLoginPanel(application)
} }

View File

@ -17,7 +17,6 @@ import i18next from "i18next";
import * as Provider from "./Provider"; import * as Provider from "./Provider";
import {getProviderLogoURL} from "../Setting"; import {getProviderLogoURL} from "../Setting";
import {GithubLoginButton, GoogleLoginButton} from "react-social-login-buttons"; import {GithubLoginButton, GoogleLoginButton} from "react-social-login-buttons";
import {authViaMetaMask, authViaWeb3Onboard} from "./Web3Auth";
import QqLoginButton from "./QqLoginButton"; import QqLoginButton from "./QqLoginButton";
import FacebookLoginButton from "./FacebookLoginButton"; import FacebookLoginButton from "./FacebookLoginButton";
import WeiboLoginButton from "./WeiboLoginButton"; import WeiboLoginButton from "./WeiboLoginButton";
@ -43,9 +42,7 @@ import OktaLoginButton from "./OktaLoginButton";
import DouyinLoginButton from "./DouyinLoginButton"; import DouyinLoginButton from "./DouyinLoginButton";
import LoginButton from "./LoginButton"; import LoginButton from "./LoginButton";
import * as AuthBackend from "./AuthBackend"; import * as AuthBackend from "./AuthBackend";
import * as Setting from "../Setting"; import {WechatOfficialAccountModal} from "./Util";
import {getEvent} from "./Util";
import {Modal} from "antd";
function getSigninButton(provider) { function getSigninButton(provider) {
const text = i18next.t("login:Sign in with {type}").replace("{type}", provider.displayName !== "" ? provider.displayName : provider.type); const text = i18next.t("login:Sign in with {type}").replace("{type}", provider.displayName !== "" ? provider.displayName : provider.type);
@ -124,9 +121,17 @@ function goToSamlUrl(provider, location) {
export function goToWeb3Url(application, provider, method) { export function goToWeb3Url(application, provider, method) {
if (provider.type === "MetaMask") { if (provider.type === "MetaMask") {
authViaMetaMask(application, provider, method); import("./Web3Auth")
.then(module => {
const authViaMetaMask = module.authViaMetaMask;
authViaMetaMask(application, provider, method);
});
} else if (provider.type === "Web3Onboard") { } else if (provider.type === "Web3Onboard") {
authViaWeb3Onboard(application, provider, method); import("./Web3Auth")
.then(module => {
const authViaWeb3Onboard = module.authViaWeb3Onboard;
authViaWeb3Onboard(application, provider, method);
});
} }
} }
@ -134,51 +139,30 @@ export function renderProviderLogo(provider, application, width, margin, size, l
if (size === "small") { if (size === "small") {
if (provider.category === "OAuth") { if (provider.category === "OAuth") {
if (provider.type === "WeChat" && provider.clientId2 !== "" && provider.clientSecret2 !== "" && provider.disableSsl === true && !navigator.userAgent.includes("MicroMessenger")) { if (provider.type === "WeChat" && provider.clientId2 !== "" && provider.clientSecret2 !== "" && provider.disableSsl === true && !navigator.userAgent.includes("MicroMessenger")) {
const info = async() => {
AuthBackend.getWechatQRCode(`${provider.owner}/${provider.name}`).then(
async res => {
if (res.status !== "ok") {
Setting.showMessage("error", res?.msg);
return;
}
const t1 = setInterval(await getEvent, 1000, application, provider, res.data2);
{Modal.info({
title: i18next.t("provider:Please use WeChat to scan the QR code and follow the official account for sign in"),
content: (
<div style={{marginRight: "34px"}}>
<img src = {"data:image/png;base64," + res.data} alt="Wechat QR code" style={{width: "100%"}} />
</div>
),
onOk() {
window.clearInterval(t1);
},
});}
}
);
};
return ( return (
<a key={provider.displayName} > <a key={provider.displayName} >
<img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} style={{margin: margin}} onClick={info} /> <img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} className="provider-img" style={{margin: margin}} onClick={() => {
WechatOfficialAccountModal(application, provider, "signup");
}} />
</a> </a>
); );
} else { } else {
return ( return (
<a key={provider.displayName} href={Provider.getAuthUrl(application, provider, "signup")}> <a key={provider.displayName} href={Provider.getAuthUrl(application, provider, "signup")}>
<img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} style={{margin: margin}} /> <img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} className="provider-img" style={{margin: margin}} />
</a> </a>
); );
} }
} else if (provider.category === "SAML") { } else if (provider.category === "SAML") {
return ( return (
<a key={provider.displayName} onClick={() => goToSamlUrl(provider, location)}> <a key={provider.displayName} onClick={() => goToSamlUrl(provider, location)}>
<img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} style={{margin: margin}} /> <img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} className="provider-img" style={{margin: margin}} />
</a> </a>
); );
} else if (provider.category === "Web3") { } else if (provider.category === "Web3") {
return ( return (
<a key={provider.displayName} onClick={() => goToWeb3Url(application, provider, "signup")}> <a key={provider.displayName} onClick={() => goToWeb3Url(application, provider, "signup")}>
<img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} style={{margin: margin}} /> <img width={width} height={width} src={getProviderLogoURL(provider)} alt={provider.displayName} className="provider-img" style={{margin: margin}} />
</a> </a>
); );
} }
@ -193,7 +177,7 @@ export function renderProviderLogo(provider, application, width, margin, size, l
return ( return (
<a key={provider.displayName} href={Provider.getAuthUrl(application, provider, "signup")} style={customAStyle}> <a key={provider.displayName} href={Provider.getAuthUrl(application, provider, "signup")} style={customAStyle}>
<button style={customButtonStyle}> <button style={customButtonStyle}>
<img width={26} src={getProviderLogoURL(provider)} alt={provider.displayName} style={customImgStyle} /> <img width={26} src={getProviderLogoURL(provider)} alt={provider.displayName} className="provider-img" style={customImgStyle} />
<span style={customSpanStyle}>{text}</span> <span style={customSpanStyle}>{text}</span>
</button> </button>
</a> </a>
@ -202,7 +186,7 @@ export function renderProviderLogo(provider, application, width, margin, size, l
return ( return (
<a key={provider.displayName} onClick={() => goToSamlUrl(provider, location)} style={customAStyle}> <a key={provider.displayName} onClick={() => goToSamlUrl(provider, location)} style={customAStyle}>
<button style={customButtonStyle}> <button style={customButtonStyle}>
<img width={26} src={getProviderLogoURL(provider)} alt={provider.displayName} style={customImgStyle} /> <img width={26} src={getProviderLogoURL(provider)} alt={provider.displayName} className="provider-img" style={customImgStyle} />
<span style={customSpanStyle}>{text}</span> <span style={customSpanStyle}>{text}</span>
</button> </button>
</a> </a>
@ -212,7 +196,7 @@ export function renderProviderLogo(provider, application, width, margin, size, l
// big button, for disable password signin // big button, for disable password signin
if (provider.category === "SAML") { if (provider.category === "SAML") {
return ( return (
<div key={provider.displayName} style={{marginBottom: "10px"}}> <div key={provider.displayName} className="provider-big-img">
<a onClick={() => goToSamlUrl(provider, location)}> <a onClick={() => goToSamlUrl(provider, location)}>
{ {
getSigninButton(provider) getSigninButton(provider)
@ -222,7 +206,7 @@ export function renderProviderLogo(provider, application, width, margin, size, l
); );
} else if (provider.category === "Web3") { } else if (provider.category === "Web3") {
return ( return (
<div key={provider.displayName} style={{marginBottom: "10px"}}> <div key={provider.displayName} className="provider-big-img">
<a onClick={() => goToWeb3Url(application, provider, "signup")}> <a onClick={() => goToWeb3Url(application, provider, "signup")}>
{ {
getSigninButton(provider) getSigninButton(provider)
@ -232,7 +216,7 @@ export function renderProviderLogo(provider, application, width, margin, size, l
); );
} else { } else {
return ( return (
<div key={provider.displayName} style={{marginBottom: "10px"}}> <div key={provider.displayName} className="provider-big-img">
<a href={Provider.getAuthUrl(application, provider, "signup")}> <a href={Provider.getAuthUrl(application, provider, "signup")}>
{ {
getSigninButton(provider) getSigninButton(provider)

View File

@ -92,7 +92,7 @@ class ResultPage extends React.Component {
<Button type="primary" key="login" onClick={() => { <Button type="primary" key="login" onClick={() => {
const linkInStorage = sessionStorage.getItem("signinUrl"); const linkInStorage = sessionStorage.getItem("signinUrl");
if (linkInStorage !== null && linkInStorage !== "") { if (linkInStorage !== null && linkInStorage !== "") {
Setting.goToLink(linkInStorage); Setting.goToLinkSoft(this, linkInStorage);
} else { } else {
Setting.redirectToLoginPage(application, this.props.history); Setting.redirectToLoginPage(application, this.props.history);
} }

View File

@ -17,6 +17,10 @@ import LoginPage from "./LoginPage";
import {authConfig} from "./Auth"; import {authConfig} from "./Auth";
class SelfLoginPage extends React.Component { class SelfLoginPage extends React.Component {
constructor(props) {
super(props);
import("../ManagementPage");
}
render() { render() {
return ( return (
<LoginPage type={"login"} mode={"signin"} applicationName={authConfig.appName} {...this.props} /> <LoginPage type={"login"} mode={"signin"} applicationName={authConfig.appName} {...this.props} />

View File

@ -87,8 +87,8 @@ class SignupPage extends React.Component {
componentDidMount() { componentDidMount() {
const oAuthParams = Util.getOAuthGetParameters(); const oAuthParams = Util.getOAuthGetParameters();
if (oAuthParams !== null) { if (oAuthParams !== null) {
const signinUrl = window.location.href.replace("/signup/oauth/authorize", "/login/oauth/authorize"); const signinUrl = window.location.pathname.replace("/signup/oauth/authorize", "/login/oauth/authorize");
sessionStorage.setItem("signinUrl", signinUrl); sessionStorage.setItem("signinUrl", signinUrl + window.location.search);
} }
if (this.getApplicationObj() === undefined) { if (this.getApplicationObj() === undefined) {
@ -223,7 +223,7 @@ class SignupPage extends React.Component {
Setting.goToLinkSoft(this, this.getResultPath(application, values)); Setting.goToLinkSoft(this, this.getResultPath(application, values));
} }
} else { } else {
Setting.showMessage("error", i18next.t(`signup:${res.msg}`)); Setting.showMessage("error", res.msg);
} }
}); });
} }
@ -639,7 +639,7 @@ class SignupPage extends React.Component {
<a onClick={() => { <a onClick={() => {
const linkInStorage = sessionStorage.getItem("signinUrl"); const linkInStorage = sessionStorage.getItem("signinUrl");
if (linkInStorage !== null && linkInStorage !== "") { if (linkInStorage !== null && linkInStorage !== "") {
Setting.goToLink(linkInStorage); Setting.goToLinkSoft(this, linkInStorage);
} else { } else {
Setting.redirectToLoginPage(application, this.props.history); Setting.redirectToLoginPage(application, this.props.history);
} }

View File

@ -13,11 +13,12 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Alert, Button, Result} from "antd"; import {Alert, Button, Modal, Result} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import {getWechatMessageEvent} from "./AuthBackend"; import {getWechatMessageEvent} from "./AuthBackend";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import * as Provider from "./Provider"; import * as Provider from "./Provider";
import * as AuthBackend from "./AuthBackend";
export function renderMessage(msg) { export function renderMessage(msg) {
if (msg !== null) { if (msg !== null) {
@ -188,12 +189,36 @@ export function getQueryParamsFromState(state) {
} }
} }
export function getEvent(application, provider, ticket) { export function getEvent(application, provider, ticket, method) {
getWechatMessageEvent(ticket) getWechatMessageEvent(ticket)
.then(res => { .then(res => {
if (res.data === "SCAN" || res.data === "subscribe") { if (res.data === "SCAN" || res.data === "subscribe") {
const code = res?.data2; const code = res?.data2;
Setting.goToLink(Provider.getAuthUrl(application, provider, "signup", code)); Setting.goToLink(Provider.getAuthUrl(application, provider, method ?? "signup", code));
} }
}); });
} }
export async function WechatOfficialAccountModal(application, provider, method) {
AuthBackend.getWechatQRCode(`${provider.owner}/${provider.name}`).then(
async res => {
if (res.status !== "ok") {
Setting.showMessage("error", res?.msg);
return;
}
const t1 = setInterval(await getEvent, 1000, application, provider, res.data2, method);
{Modal.info({
title: i18next.t("provider:Please use WeChat to scan the QR code and follow the official account for sign in"),
content: (
<div style={{marginRight: "34px"}}>
<img src = {"data:image/png;base64," + res.data} alt="Wechat QR code" style={{width: "100%"}} />
</div>
),
onOk() {
window.clearInterval(t1);
},
});}
}
);
}

View File

@ -0,0 +1,25 @@
// 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.
import * as Setting from "../Setting";
export function getRecords(organizationName, page, pageSize, field = "", value = "", sortField = "", sortOrder = "") {
return fetch(`${Setting.ServerUrl}/api/get-records?organizationName=${organizationName}&pageSize=${pageSize}&p=${page}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}

View File

@ -58,6 +58,18 @@ export function addSyncer(syncer) {
}).then(res => res.json()); }).then(res => res.json());
} }
export function testSyncerDb(syncer) {
const newSyncer = Setting.deepCopy(syncer);
return fetch(`${Setting.ServerUrl}/api/test-syncer-db`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newSyncer),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function deleteSyncer(syncer) { export function deleteSyncer(syncer) {
const newSyncer = Setting.deepCopy(syncer); const newSyncer = Setting.deepCopy(syncer);
return fetch(`${Setting.ServerUrl}/api/delete-syncer`, { return fetch(`${Setting.ServerUrl}/api/delete-syncer`, {

View File

@ -0,0 +1,71 @@
// Copyright 2024 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.
import * as Setting from "../Setting";
export function getTransactions(owner, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") {
return fetch(`${Setting.ServerUrl}/api/get-transactions?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function getTransaction(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-transaction?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET",
credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function updateTransaction(owner, name, transaction) {
const newTransaction = Setting.deepCopy(transaction);
return fetch(`${Setting.ServerUrl}/api/update-transaction?id=${owner}/${encodeURIComponent(name)}`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newTransaction),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function addTransaction(transaction) {
const newTransaction = Setting.deepCopy(transaction);
return fetch(`${Setting.ServerUrl}/api/add-transaction`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newTransaction),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}
export function deleteTransaction(transaction) {
const newTransaction = Setting.deepCopy(transaction);
return fetch(`${Setting.ServerUrl}/api/delete-transaction`, {
method: "POST",
credentials: "include",
body: JSON.stringify(newTransaction),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json());
}

View File

@ -147,7 +147,7 @@ export function sendCode(captchaType, captchaToken, clientSecret, method, countr
Setting.showMessage("success", i18next.t("user:Verification code sent")); Setting.showMessage("success", i18next.t("user:Verification code sent"));
return true; return true;
} else { } else {
Setting.showMessage("error", i18next.t("user:" + res.msg)); Setting.showMessage("error", res.msg);
return false; return false;
} }
}); });

View File

@ -0,0 +1,50 @@
// Copyright 2024 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.
import {useEffect} from "react";
let customHeadLoaded = false;
function CustomHead(props) {
useEffect(() => {
if (!customHeadLoaded) {
const suffix = new Date().getTime().toString();
if (!props.headerHtml) {return;}
const node = document.createElement("div");
node.innerHTML = props.headerHtml;
node.childNodes.forEach(el => {
if (el.nodeName === "#text") {
return;
}
let innerNode = el;
innerNode.setAttribute("app-custom-head" + suffix, "");
if (innerNode.localName === "script") {
const scriptNode = document.createElement("script");
Array.from(innerNode.attributes).forEach(attr => {
scriptNode.setAttribute(attr.name, attr.value);
});
scriptNode.text = innerNode.textContent;
innerNode = scriptNode;
}
document.head.appendChild(innerNode);
});
customHeadLoaded = true;
}
});
}
export default CustomHead;

View File

@ -20,8 +20,8 @@ import * as Setting from "../Setting";
import * as Provider from "../auth/Provider"; import * as Provider from "../auth/Provider";
import * as AuthBackend from "../auth/AuthBackend"; import * as AuthBackend from "../auth/AuthBackend";
import {goToWeb3Url} from "../auth/ProviderButton"; import {goToWeb3Url} from "../auth/ProviderButton";
import {delWeb3AuthToken} from "../auth/Web3Auth";
import AccountAvatar from "../account/AccountAvatar"; import AccountAvatar from "../account/AccountAvatar";
import {WechatOfficialAccountModal} from "../auth/Util";
class OAuthWidget extends React.Component { class OAuthWidget extends React.Component {
constructor(props) { constructor(props) {
@ -98,7 +98,22 @@ class OAuthWidget extends React.Component {
user: this.props.user, user: this.props.user,
}; };
if (providerType === "MetaMask" || providerType === "Web3Onboard") { if (providerType === "MetaMask" || providerType === "Web3Onboard") {
delWeb3AuthToken(linkedValue); import("../auth/Web3Auth")
.then(module => {
const delWeb3AuthToken = module.delWeb3AuthToken;
delWeb3AuthToken(linkedValue);
AuthBackend.unlink(body)
.then((res) => {
if (res.status === "ok") {
Setting.showMessage("success", "Unlinked successfully");
this.unlinked();
} else {
Setting.showMessage("error", `Failed to unlink: ${res.msg}`);
}
});
});
return;
} }
AuthBackend.unlink(body) AuthBackend.unlink(body)
.then((res) => { .then((res) => {
@ -183,9 +198,19 @@ class OAuthWidget extends React.Component {
provider.category === "Web3" ? ( provider.category === "Web3" ? (
<Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id} onClick={() => goToWeb3Url(application, provider, "link")}>{i18next.t("user:Link")}</Button> <Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id} onClick={() => goToWeb3Url(application, provider, "link")}>{i18next.t("user:Link")}</Button>
) : ( ) : (
<a key={provider.displayName} href={user.id !== account.id ? null : Provider.getAuthUrl(application, provider, "link")}> provider.type === "WeChat" && provider.clientId2 !== "" && provider.clientSecret2 !== "" && provider.disableSsl === true && !navigator.userAgent.includes("MicroMessenger") ? (
<Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id}>{i18next.t("user:Link")}</Button> <a key={provider.displayName}>
</a> <Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id} onClick={
() => {
WechatOfficialAccountModal(application, provider, "link");
}
}>{i18next.t("user:Link")}</Button>
</a>
) : (
<a key={provider.displayName} href={user.id !== account.id ? null : Provider.getAuthUrl(application, provider, "link")}>
<Button style={{marginLeft: "20px", width: linkButtonWidth}} type="primary" disabled={user.id !== account.id}>{i18next.t("user:Link")}</Button>
</a>
)
) )
) : ( ) : (
<Button disabled={!providerItem.canUnlink && !Setting.isAdminUser(account)} style={{marginLeft: "20px", width: linkButtonWidth}} onClick={() => this.unlinkUser(provider.type, linkedValue)}>{i18next.t("user:Unlink")}</Button> <Button disabled={!providerItem.canUnlink && !Setting.isAdminUser(account)} style={{marginLeft: "20px", width: linkButtonWidth}} onClick={() => this.unlinkUser(provider.type, linkedValue)}>{i18next.t("user:Unlink")}</Button>

View File

@ -36,7 +36,7 @@ class OpenTour extends React.Component {
this.canTour() ? this.canTour() ?
<Tooltip title="Click to open tour"> <Tooltip title="Click to open tour">
<div className="select-box" style={{display: Setting.isMobile() ? "none" : null, ...this.props.style}} onClick={() => TourConfig.setIsTourVisible(true)} > <div className="select-box" style={{display: Setting.isMobile() ? "none" : null, ...this.props.style}} onClick={() => TourConfig.setIsTourVisible(true)} >
<QuestionCircleOutlined style={{fontSize: "24px", color: "#4d4d4d"}} /> <QuestionCircleOutlined style={{fontSize: "24px"}} />
</div> </div>
</Tooltip> </Tooltip>
: :

View File

@ -81,6 +81,7 @@ export function renderAgreementFormItem(application, required, layout, ths) {
name="agreement" name="agreement"
key="agreement" key="agreement"
valuePropName="checked" valuePropName="checked"
className="login-agreement"
rules={[ rules={[
{ {
required: required, required: required,

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