forked from casdoor/casdoor
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55257d6190 | ||
|
|
b9046bec01 | ||
|
|
40d4e3a1a9 | ||
|
|
60bfc8891a | ||
|
|
126879533b | ||
|
|
469b6036fd | ||
|
|
6c750867b0 | ||
|
|
625b3e2c63 | ||
|
|
28dff8083a | ||
|
|
02c4bddb5f | ||
|
|
df65fb3525 | ||
|
|
d3bbf954f8 | ||
|
|
f3755d925c | ||
|
|
ca819e7e83 | ||
|
|
d619e91d9e | ||
|
|
5079c37818 | ||
|
|
d5f29d716a | ||
|
|
00b278a00f | ||
|
|
d883db907b | ||
|
|
8e7efe5c23 | ||
|
|
bf75508d95 | ||
|
|
986b94cc90 | ||
|
|
890f528556 | ||
|
|
b46e779235 | ||
|
|
5c80948a06 | ||
|
|
1467199159 | ||
|
|
64c2b8f0c2 | ||
|
|
8f7ea7f0a0 | ||
|
|
2ab85c0c44 | ||
|
|
bf67be2af6 | ||
|
|
bc94735a8d | ||
|
|
89c6ef5aae | ||
|
|
21da9f5ff2 | ||
|
|
3b11e778e7 | ||
|
|
ad240a373f | ||
|
|
01000f7022 | ||
|
|
f93aeb5350 | ||
|
|
8fa681f883 | ||
|
|
3b16406442 | ||
|
|
fbc16ef124 | ||
|
|
f26f56e88b | ||
|
|
9cb633c9e2 | ||
|
|
d0d059d42f | ||
|
|
c184dc7f3a | ||
|
|
2fa0890c11 | ||
|
|
a0e2be7ba8 | ||
|
|
09b389b1f7 | ||
|
|
a23033758f | ||
|
|
f7bc822087 | ||
|
|
e533ff1ee1 | ||
|
|
9f187f690e | ||
|
|
fe5aa1f214 | ||
|
|
eda742a848 | ||
|
|
83df077a02 | ||
|
|
ad6080e763 | ||
|
|
c179324de4 | ||
|
|
645716e485 | ||
|
|
955e73ddd1 | ||
|
|
2493ae9cfe | ||
|
|
b5c80513fb | ||
|
|
0653353be1 | ||
|
|
d6778fb4e6 | ||
|
|
fee7773839 |
@@ -61,6 +61,7 @@ p, *, *, GET, /api/get-application, *, *
|
||||
p, *, *, GET, /api/get-organization-applications, *, *
|
||||
p, *, *, GET, /api/get-user, *, *
|
||||
p, *, *, GET, /api/get-user-application, *, *
|
||||
p, *, *, POST, /api/upload-users, *, *
|
||||
p, *, *, GET, /api/get-resources, *, *
|
||||
p, *, *, GET, /api/get-records, *, *
|
||||
p, *, *, GET, /api/get-product, *, *
|
||||
@@ -143,6 +144,10 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
|
||||
return false
|
||||
}
|
||||
|
||||
if user.IsGlobalAdmin() {
|
||||
return true
|
||||
}
|
||||
|
||||
if user.IsAdmin && (subOwner == objOwner || (objOwner == "admin")) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ func (c *ApiController) UpdateApplication() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateApplication(id, &application))
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateApplication(id, &application, c.IsGlobalAdmin()))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
c.ResponseError(c.T("auth:Challenge method should be S256"))
|
||||
return
|
||||
}
|
||||
code, err := object.GetOAuthCode(userId, clientId, form.Provider, responseType, redirectUri, scope, state, nonce, codeChallenge, c.Ctx.Request.Host, c.GetAcceptLanguage())
|
||||
code, err := object.GetOAuthCode(userId, clientId, form.Provider, form.SigninMethod, responseType, redirectUri, scope, state, nonce, codeChallenge, c.Ctx.Request.Host, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
@@ -719,7 +719,8 @@ func (c *ApiController) Login() {
|
||||
|
||||
setHttpClient(idProvider, provider.Type)
|
||||
|
||||
if authForm.State != conf.GetConfigString("authState") && authForm.State != application.Name {
|
||||
stateApplicationName := strings.Split(authForm.State, "-org-")[0]
|
||||
if authForm.State != conf.GetConfigString("authState") && stateApplicationName != application.Name {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:State expected: %s, but got: %s"), conf.GetConfigString("authState"), authForm.State))
|
||||
return
|
||||
}
|
||||
@@ -788,7 +789,7 @@ func (c *ApiController) Login() {
|
||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||
|
||||
c.Ctx.Input.SetParam("recordUserId", user.GetId())
|
||||
} else if provider.Category == "OAuth" || provider.Category == "Web3" {
|
||||
} else if provider.Category == "OAuth" || provider.Category == "Web3" || provider.Category == "SAML" {
|
||||
// Sign up via OAuth
|
||||
if application.EnableLinkWithEmail {
|
||||
if userInfo.Email != "" {
|
||||
@@ -817,7 +818,7 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
if !providerItem.CanSignUp {
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up"), provider.Type, userInfo.Username, userInfo.DisplayName, provider.Type))
|
||||
c.ResponseError(fmt.Sprintf(c.T("auth:The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up"), provider.Type, userInfo.Username, userInfo.DisplayName, provider.Type))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ const (
|
||||
goCliRepo = "https://api.github.com/repos/casbin/casbin-go-cli/releases/latest"
|
||||
rustCliRepo = "https://api.github.com/repos/casbin-rs/casbin-rust-cli/releases/latest"
|
||||
pythonCliRepo = "https://api.github.com/repos/casbin/casbin-python-cli/releases/latest"
|
||||
dotnetCliRepo = "https://api.github.com/repos/casbin-net/casbin-dotnet-cli/releases/latest"
|
||||
downloadFolder = "bin"
|
||||
)
|
||||
|
||||
@@ -45,6 +46,7 @@ func getBinaryNames() map[string]string {
|
||||
java = "java"
|
||||
rust = "rust"
|
||||
python = "python"
|
||||
dotnet = "dotnet"
|
||||
)
|
||||
|
||||
arch := runtime.GOARCH
|
||||
@@ -65,6 +67,7 @@ func getBinaryNames() map[string]string {
|
||||
java: "casbin-java-cli.jar",
|
||||
rust: fmt.Sprintf("casbin-rust-cli-%s-pc-windows-gnu", archNames.rustArch),
|
||||
python: fmt.Sprintf("casbin-python-cli-windows-%s.exe", archNames.goArch),
|
||||
dotnet: fmt.Sprintf("casbin-dotnet-cli-windows-%s.exe", archNames.goArch),
|
||||
}
|
||||
case "darwin":
|
||||
return map[string]string{
|
||||
@@ -72,6 +75,7 @@ func getBinaryNames() map[string]string {
|
||||
java: "casbin-java-cli.jar",
|
||||
rust: fmt.Sprintf("casbin-rust-cli-%s-apple-darwin", archNames.rustArch),
|
||||
python: fmt.Sprintf("casbin-python-cli-darwin-%s", archNames.goArch),
|
||||
dotnet: fmt.Sprintf("casbin-dotnet-cli-darwin-%s", archNames.goArch),
|
||||
}
|
||||
case "linux":
|
||||
return map[string]string{
|
||||
@@ -79,6 +83,7 @@ func getBinaryNames() map[string]string {
|
||||
java: "casbin-java-cli.jar",
|
||||
rust: fmt.Sprintf("casbin-rust-cli-%s-unknown-linux-gnu", archNames.rustArch),
|
||||
python: fmt.Sprintf("casbin-python-cli-linux-%s", archNames.goArch),
|
||||
dotnet: fmt.Sprintf("casbin-dotnet-cli-linux-%s", archNames.goArch),
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
@@ -108,6 +113,11 @@ func getFinalBinaryName(lang string) string {
|
||||
return "casbin-python-cli.exe"
|
||||
}
|
||||
return "casbin-python-cli"
|
||||
case "dotnet":
|
||||
if runtime.GOOS == "windows" {
|
||||
return "casbin-dotnet-cli.exe"
|
||||
}
|
||||
return "casbin-dotnet-cli"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
@@ -347,6 +357,7 @@ func downloadCLI() error {
|
||||
"go": goCliRepo,
|
||||
"rust": rustCliRepo,
|
||||
"python": pythonCliRepo,
|
||||
"dotnet": dotnetCliRepo,
|
||||
}
|
||||
|
||||
for lang, repo := range repos {
|
||||
|
||||
175
controllers/form.go
Normal file
175
controllers/form.go
Normal file
@@ -0,0 +1,175 @@
|
||||
// Copyright 2025 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"
|
||||
)
|
||||
|
||||
// GetGlobalForms
|
||||
// @Title GetGlobalForms
|
||||
// @Tag Form API
|
||||
// @Description get global forms
|
||||
// @Success 200 {array} object.Form The Response object
|
||||
// @router /get-global-forms [get]
|
||||
func (c *ApiController) GetGlobalForms() {
|
||||
forms, err := object.GetGlobalForms()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedForms(forms, true))
|
||||
}
|
||||
|
||||
// GetForms
|
||||
// @Title GetForms
|
||||
// @Tag Form API
|
||||
// @Description get forms
|
||||
// @Param owner query string true "The owner of form"
|
||||
// @Success 200 {array} object.Form The Response object
|
||||
// @router /get-forms [get]
|
||||
func (c *ApiController) GetForms() {
|
||||
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 == "" {
|
||||
forms, err := object.GetForms(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedForms(forms, true))
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetFormCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
forms, err := object.GetPaginationForms(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.ResponseOk(forms, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// GetForm
|
||||
// @Title GetForm
|
||||
// @Tag Form API
|
||||
// @Description get form
|
||||
// @Param id query string true "The id (owner/name) of form"
|
||||
// @Success 200 {object} object.Form The Response object
|
||||
// @router /get-form [get]
|
||||
func (c *ApiController) GetForm() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
form, err := object.GetForm(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedForm(form, true))
|
||||
}
|
||||
|
||||
// UpdateForm
|
||||
// @Title UpdateForm
|
||||
// @Tag Form API
|
||||
// @Description update form
|
||||
// @Param id query string true "The id (owner/name) of the form"
|
||||
// @Param body body object.Form true "The details of the form"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-form [post]
|
||||
func (c *ApiController) UpdateForm() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var form object.Form
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &form)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
success, err := object.UpdateForm(id, &form)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(success)
|
||||
}
|
||||
|
||||
// AddForm
|
||||
// @Title AddForm
|
||||
// @Tag Form API
|
||||
// @Description add form
|
||||
// @Param body body object.Form true "The details of the form"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-form [post]
|
||||
func (c *ApiController) AddForm() {
|
||||
var form object.Form
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &form)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
success, err := object.AddForm(&form)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(success)
|
||||
}
|
||||
|
||||
// DeleteForm
|
||||
// @Title DeleteForm
|
||||
// @Tag Form API
|
||||
// @Description delete form
|
||||
// @Param body body object.Form true "The details of the form"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-form [post]
|
||||
func (c *ApiController) DeleteForm() {
|
||||
var form object.Form
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &form)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
success, err := object.DeleteForm(&form)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(success)
|
||||
}
|
||||
@@ -16,6 +16,8 @@ package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/beego/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
@@ -188,3 +190,73 @@ func (c *ApiController) VerifyInvitation() {
|
||||
|
||||
c.ResponseOk(payment, attachInfo)
|
||||
}
|
||||
|
||||
// SendInvitation
|
||||
// @Title VerifyInvitation
|
||||
// @Tag Invitation API
|
||||
// @Description verify invitation
|
||||
// @Param id query string true "The id ( owner/name ) of the invitation"
|
||||
// @Param body body []string true "The details of the invitation"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /send-invitation [post]
|
||||
func (c *ApiController) SendInvitation() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var destinations []string
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &destinations)
|
||||
|
||||
if !c.IsAdmin() {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
return
|
||||
}
|
||||
|
||||
invitation, err := object.GetInvitation(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if invitation == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("invitation:Invitation %s does not exist"), id))
|
||||
return
|
||||
}
|
||||
|
||||
organization, err := object.GetOrganization(fmt.Sprintf("admin/%s", invitation.Owner))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
application, err := object.GetApplicationByOrganizationName(invitation.Owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if application == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The organization: %s should have one application at least"), invitation.Owner))
|
||||
return
|
||||
}
|
||||
|
||||
provider, err := application.GetEmailProvider("Invitation")
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if provider == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("verification:please add an Email provider to the \"Providers\" list for the application: %s"), invitation.Owner))
|
||||
return
|
||||
}
|
||||
|
||||
content := provider.Metadata
|
||||
|
||||
content = strings.ReplaceAll(content, "%code", invitation.Code)
|
||||
content = strings.ReplaceAll(content, "%link", invitation.GetInvitationLink(c.Ctx.Request.Host, application.Name))
|
||||
|
||||
err = object.SendEmail(provider, provider.Title, content, destinations, organization.DisplayName)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk()
|
||||
}
|
||||
|
||||
@@ -364,7 +364,7 @@ func (c *ApiController) UploadResource() {
|
||||
}
|
||||
|
||||
applicationObj.TermsOfUse = fileUrl
|
||||
_, err = object.UpdateApplication(applicationId, applicationObj)
|
||||
_, err = object.UpdateApplication(applicationId, applicationObj, true)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
||||
@@ -59,8 +59,10 @@ func (c *ApiController) HandleSamlRedirect() {
|
||||
|
||||
relayState := c.Input().Get("RelayState")
|
||||
samlRequest := c.Input().Get("SAMLRequest")
|
||||
username := c.Input().Get("username")
|
||||
loginHint := c.Input().Get("login_hint")
|
||||
|
||||
targetURL := object.GetSamlRedirectAddress(owner, application, relayState, samlRequest, host)
|
||||
targetURL := object.GetSamlRedirectAddress(owner, application, relayState, samlRequest, host, username, loginHint)
|
||||
|
||||
c.Redirect(targetURL, http.StatusSeeOther)
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ func (c *ApiController) SendEmail() {
|
||||
content = strings.Replace(content, string(matchContent), "", -1)
|
||||
|
||||
for _, receiver := range emailForm.Receivers {
|
||||
err = object.SendEmail(provider, emailForm.Title, content, receiver, emailForm.Sender)
|
||||
err = object.SendEmail(provider, emailForm.Title, content, []string{receiver}, emailForm.Sender)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
||||
@@ -68,7 +68,7 @@ func (c *ApiController) GetSessions() {
|
||||
// @Title GetSingleSession
|
||||
// @Tag Session API
|
||||
// @Description Get session for one user in one application.
|
||||
// @Param id query string true "The id(organization/application/user) of session"
|
||||
// @Param sessionPkId query string true "The id(organization/user/application) of session"
|
||||
// @Success 200 {array} string The Response object
|
||||
// @router /get-session [get]
|
||||
func (c *ApiController) GetSingleSession() {
|
||||
@@ -87,7 +87,7 @@ func (c *ApiController) GetSingleSession() {
|
||||
// @Title UpdateSession
|
||||
// @Tag Session API
|
||||
// @Description Update session for one user in one application.
|
||||
// @Param id query string true "The id(organization/application/user) of session"
|
||||
// @Param id query string true "The id(organization/user/application) of session"
|
||||
// @Success 200 {array} string The Response object
|
||||
// @router /update-session [post]
|
||||
func (c *ApiController) UpdateSession() {
|
||||
@@ -106,7 +106,7 @@ func (c *ApiController) UpdateSession() {
|
||||
// @Title AddSession
|
||||
// @Tag Session API
|
||||
// @Description Add session for one user in one application. If there are other existing sessions, join the session into the list.
|
||||
// @Param id query string true "The id(organization/application/user) of session"
|
||||
// @Param id query string true "The id(organization/user/application) of session"
|
||||
// @Param sessionId query string true "sessionId to be added"
|
||||
// @Success 200 {array} string The Response object
|
||||
// @router /add-session [post]
|
||||
@@ -126,7 +126,7 @@ func (c *ApiController) AddSession() {
|
||||
// @Title DeleteSession
|
||||
// @Tag Session API
|
||||
// @Description Delete session for one user in one application.
|
||||
// @Param id query string true "The id(organization/application/user) of session"
|
||||
// @Param id query string true "The id(organization/user/application) of session"
|
||||
// @Success 200 {array} string The Response object
|
||||
// @router /delete-session [post]
|
||||
func (c *ApiController) DeleteSession() {
|
||||
@@ -145,7 +145,7 @@ func (c *ApiController) DeleteSession() {
|
||||
// @Title IsSessionDuplicated
|
||||
// @Tag Session API
|
||||
// @Description Check if there are other different sessions for one user in one application.
|
||||
// @Param id query string true "The id(organization/application/user) of session"
|
||||
// @Param sessionPkId query string true "The id(organization/user/application) of session"
|
||||
// @Param sessionId query string true "sessionId to be checked"
|
||||
// @Success 200 {array} string The Response object
|
||||
// @router /is-session-duplicated [get]
|
||||
|
||||
@@ -103,7 +103,7 @@ func (c *ApiController) UpdateSyncer() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateSyncer(id, &syncer))
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateSyncer(id, &syncer, c.IsGlobalAdmin()))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ func (c *ApiController) UpdateToken() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateToken(id, &token))
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateToken(id, &token, c.IsGlobalAdmin()))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
|
||||
@@ -252,12 +252,16 @@ func (c *ApiController) GetUser() {
|
||||
// @Title UpdateUser
|
||||
// @Tag User API
|
||||
// @Description update user
|
||||
// @Param id query string true "The id ( owner/name ) of the user"
|
||||
// @Param id query string false "The id ( owner/name ) of the user"
|
||||
// @Param userId query string false "The userId (UUID) of the user"
|
||||
// @Param owner query string false "The owner of the user (required when using userId)"
|
||||
// @Param body body object.User true "The details of the user"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-user [post]
|
||||
func (c *ApiController) UpdateUser() {
|
||||
id := c.Input().Get("id")
|
||||
userId := c.Input().Get("userId")
|
||||
owner := c.Input().Get("owner")
|
||||
columnsStr := c.Input().Get("columns")
|
||||
|
||||
var user object.User
|
||||
@@ -267,17 +271,38 @@ func (c *ApiController) UpdateUser() {
|
||||
return
|
||||
}
|
||||
|
||||
if id == "" {
|
||||
if id == "" && userId == "" {
|
||||
id = c.GetSessionUsername()
|
||||
if id == "" {
|
||||
c.ResponseError(c.T("general:Missing parameter"))
|
||||
return
|
||||
}
|
||||
}
|
||||
oldUser, err := object.GetUser(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
||||
var userFromUserId *object.User
|
||||
if userId != "" && owner != "" {
|
||||
userFromUserId, err = object.GetUserByUserId(owner, userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if userFromUserId == nil {
|
||||
c.ResponseError(fmt.Sprintf(c.T("general:The user with userId: %s doesn't exist"), userId))
|
||||
return
|
||||
}
|
||||
|
||||
id = util.GetId(userFromUserId.Owner, userFromUserId.Name)
|
||||
}
|
||||
|
||||
var oldUser *object.User
|
||||
if userId != "" {
|
||||
oldUser = userFromUserId
|
||||
} else {
|
||||
oldUser, err = object.GetUser(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if oldUser == nil {
|
||||
@@ -524,14 +549,16 @@ func (c *ApiController) SetPassword() {
|
||||
}
|
||||
}
|
||||
} else if code == "" {
|
||||
if user.Ldap == "" {
|
||||
err = object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||
} else {
|
||||
err = object.CheckLdapUserPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||
}
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
if targetUser.Password != "" || user.Ldap != "" {
|
||||
if user.Ldap == "" {
|
||||
err = object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||
} else {
|
||||
err = object.CheckLdapUserPassword(targetUser, oldPassword, c.GetAcceptLanguage())
|
||||
}
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,6 +578,12 @@ func (c *ApiController) SetPassword() {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the new password is the same as the current password
|
||||
if !object.CheckPasswordNotSameAsCurrent(targetUser, newPassword, organization) {
|
||||
c.ResponseError(c.T("user:The new password must be different from your current password"))
|
||||
return
|
||||
}
|
||||
|
||||
application, err := object.GetApplicationByUser(targetUser)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
|
||||
@@ -40,6 +40,17 @@ func saveFile(path string, file *multipart.File) (err error) {
|
||||
}
|
||||
|
||||
func (c *ApiController) UploadUsers() {
|
||||
if !c.IsAdmin() {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
return
|
||||
}
|
||||
|
||||
userObj := c.getCurrentUser()
|
||||
if userObj == nil {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
return
|
||||
}
|
||||
|
||||
userId := c.GetSessionUsername()
|
||||
owner, user := util.GetOwnerAndNameFromId(userId)
|
||||
|
||||
@@ -58,7 +69,7 @@ func (c *ApiController) UploadUsers() {
|
||||
return
|
||||
}
|
||||
|
||||
affected, err := object.UploadUsers(owner, path)
|
||||
affected, err := object.UploadUsers(owner, path, userObj, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
|
||||
@@ -435,9 +435,15 @@ func (c *ApiController) ResetEmailOrPhone() {
|
||||
|
||||
switch destType {
|
||||
case object.VerifyTypeEmail:
|
||||
id := user.GetId()
|
||||
user.Email = dest
|
||||
user.EmailVerified = true
|
||||
_, err = object.UpdateUser(user.GetId(), user, []string{"email", "email_verified"}, false)
|
||||
columns := []string{"email", "email_verified"}
|
||||
if organization.UseEmailAsUsername {
|
||||
user.Name = user.Email
|
||||
columns = append(columns, "name")
|
||||
}
|
||||
_, err = object.UpdateUser(id, user, columns, false)
|
||||
case object.VerifyTypePhone:
|
||||
user.Phone = dest
|
||||
_, err = object.SetUserField(user, "phone", user.Phone)
|
||||
@@ -449,6 +455,9 @@ func (c *ApiController) ResetEmailOrPhone() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
if organization.UseEmailAsUsername {
|
||||
c.SetSessionUsername(user.GetId())
|
||||
}
|
||||
|
||||
err = object.DisableVerificationCode(checkDest)
|
||||
if err != nil {
|
||||
|
||||
@@ -105,7 +105,7 @@ func (c *ApiController) UpdateWebhook() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateWebhook(id, &webhook))
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateWebhook(id, &webhook, c.IsGlobalAdmin()))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
|
||||
@@ -96,15 +96,17 @@ func NewAzureACSEmailProvider(accessKey string, endpoint string) *AzureACSEmailP
|
||||
}
|
||||
}
|
||||
|
||||
func newEmail(fromAddress string, toAddress string, subject string, content string) *Email {
|
||||
func newEmail(fromAddress string, toAddress []string, subject string, content string) *Email {
|
||||
var to []EmailAddress
|
||||
for _, addr := range toAddress {
|
||||
to = append(to, EmailAddress{
|
||||
DisplayName: addr,
|
||||
Address: addr,
|
||||
})
|
||||
}
|
||||
return &Email{
|
||||
Recipients: Recipients{
|
||||
To: []EmailAddress{
|
||||
{
|
||||
DisplayName: toAddress,
|
||||
Address: toAddress,
|
||||
},
|
||||
},
|
||||
To: to,
|
||||
},
|
||||
SenderAddress: fromAddress,
|
||||
Content: Content{
|
||||
@@ -116,7 +118,7 @@ func newEmail(fromAddress string, toAddress string, subject string, content stri
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AzureACSEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
|
||||
func (a *AzureACSEmailProvider) Send(fromAddress string, fromName string, toAddress []string, subject string, content string) error {
|
||||
email := newEmail(fromAddress, toAddress, subject, content)
|
||||
|
||||
postBody, err := json.Marshal(email)
|
||||
|
||||
@@ -48,21 +48,23 @@ func NewHttpEmailProvider(endpoint string, method string, httpHeaders map[string
|
||||
return client
|
||||
}
|
||||
|
||||
func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
|
||||
func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress []string, subject string, content string) error {
|
||||
var req *http.Request
|
||||
var err error
|
||||
|
||||
fromNameField := "fromName"
|
||||
toAddressField := "toAddress"
|
||||
toAddressesField := "toAddresses"
|
||||
subjectField := "subject"
|
||||
contentField := "content"
|
||||
|
||||
for k, v := range c.bodyMapping {
|
||||
switch k {
|
||||
case "fromName":
|
||||
fromNameField = v
|
||||
case "toAddress":
|
||||
toAddressField = v
|
||||
case "toAddresses":
|
||||
toAddressesField = v
|
||||
case "subject":
|
||||
subjectField = v
|
||||
case "content":
|
||||
@@ -73,7 +75,6 @@ func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress
|
||||
if c.method == "POST" || c.method == "PUT" || c.method == "DELETE" {
|
||||
bodyMap := make(map[string]string)
|
||||
bodyMap[fromNameField] = fromName
|
||||
bodyMap[toAddressField] = toAddress
|
||||
bodyMap[subjectField] = subject
|
||||
bodyMap[contentField] = content
|
||||
|
||||
@@ -89,6 +90,13 @@ func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress
|
||||
for k, v := range bodyMap {
|
||||
formValues.Add(k, v)
|
||||
}
|
||||
if len(toAddress) == 1 {
|
||||
formValues.Add(toAddressField, toAddress[0])
|
||||
} else {
|
||||
for _, addr := range toAddress {
|
||||
formValues.Add(toAddressesField, addr)
|
||||
}
|
||||
}
|
||||
req, err = http.NewRequest(c.method, c.endpoint, strings.NewReader(formValues.Encode()))
|
||||
}
|
||||
|
||||
@@ -105,7 +113,13 @@ func (c *HttpEmailProvider) Send(fromAddress string, fromName string, toAddress
|
||||
|
||||
q := req.URL.Query()
|
||||
q.Add(fromNameField, fromName)
|
||||
q.Add(toAddressField, toAddress)
|
||||
if len(toAddress) == 1 {
|
||||
q.Add(toAddressField, toAddress[0])
|
||||
} else {
|
||||
for _, addr := range toAddress {
|
||||
q.Add(toAddressesField, addr)
|
||||
}
|
||||
}
|
||||
q.Add(subjectField, subject)
|
||||
q.Add(contentField, content)
|
||||
req.URL.RawQuery = q.Encode()
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
package email
|
||||
|
||||
type EmailProvider interface {
|
||||
Send(fromAddress string, fromName, toAddress string, subject string, content string) error
|
||||
Send(fromAddress string, fromName string, toAddress []string, subject string, content string) error
|
||||
}
|
||||
|
||||
func GetEmailProvider(typ string, clientId string, clientSecret string, host string, port int, disableSsl bool, endpoint string, method string, httpHeaders map[string]string, bodyMapping map[string]string, contentType string) EmailProvider {
|
||||
|
||||
@@ -41,13 +41,24 @@ func NewSendgridEmailProvider(apiKey string, host string, endpoint string) *Send
|
||||
return &SendgridEmailProvider{ApiKey: apiKey, Host: host, Endpoint: endpoint}
|
||||
}
|
||||
|
||||
func (s *SendgridEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
|
||||
client := s.initSendgridClient()
|
||||
|
||||
func (s *SendgridEmailProvider) Send(fromAddress string, fromName string, toAddresses []string, subject string, content string) error {
|
||||
from := mail.NewEmail(fromName, fromAddress)
|
||||
to := mail.NewEmail("", toAddress)
|
||||
message := mail.NewSingleEmail(from, subject, to, "", content)
|
||||
message := mail.NewV3Mail()
|
||||
message.SetFrom(from)
|
||||
message.AddContent(mail.NewContent("text/html", content))
|
||||
|
||||
personalization := mail.NewPersonalization()
|
||||
|
||||
for _, toAddress := range toAddresses {
|
||||
to := mail.NewEmail(toAddress, toAddress)
|
||||
personalization.AddTos(to)
|
||||
}
|
||||
|
||||
personalization.Subject = subject
|
||||
|
||||
message.AddPersonalizations(personalization)
|
||||
|
||||
client := s.initSendgridClient()
|
||||
resp, err := client.Send(message)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -44,11 +44,15 @@ func NewSmtpEmailProvider(userName string, password string, host string, port in
|
||||
return &SmtpEmailProvider{Dialer: dialer}
|
||||
}
|
||||
|
||||
func (s *SmtpEmailProvider) Send(fromAddress string, fromName string, toAddress string, subject string, content string) error {
|
||||
func (s *SmtpEmailProvider) Send(fromAddress string, fromName string, toAddresses []string, subject string, content string) error {
|
||||
message := gomail.NewMessage()
|
||||
|
||||
message.SetAddressHeader("From", fromAddress, fromName)
|
||||
message.SetHeader("To", toAddress)
|
||||
var addresses []string
|
||||
for _, address := range toAddresses {
|
||||
addresses = append(addresses, message.FormatAddress(address, ""))
|
||||
}
|
||||
message.SetHeader("To", addresses...)
|
||||
message.SetHeader("Subject", subject)
|
||||
message.SetBody("text/html", content)
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "فشل تسجيل الدخول: %s",
|
||||
"Invalid token": "الرمز غير صالح",
|
||||
"State expected: %s, but got: %s": "كان من المتوقع الحالة: %s، لكن حصلنا على: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "الحساب الخاص بالمزود: %s واسم المستخدم: %s (%s) غير موجود ولا يُسمح بالتسجيل كحساب جديد عبر %%s، يرجى استخدام طريقة أخرى للتسجيل",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "الحساب الخاص بالمزود: %s واسم المستخدم: %s (%s) غير موجود ولا يُسمح بالتسجيل كحساب جديد عبر %s، يرجى استخدام طريقة أخرى للتسجيل",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "الحساب الخاص بالمزود: %s واسم المستخدم: %s (%s) غير موجود ولا يُسمح بالتسجيل كحساب جديد، يرجى الاتصال بدعم تكنولوجيا المعلومات",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "الحساب الخاص بالمزود: %s واسم المستخدم: %s (%s) مرتبط بالفعل بحساب آخر: %s (%s)",
|
||||
"The application: %s does not exist": "التطبيق: %s غير موجود",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "عنوان IP الخاص بك: %s تم حظره وفقًا لتكوين: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Şifrenizin süresi doldu. Lütfen \\\"Şifremi unuttum\\\"a tıklayarak şifrenizi sıfırlayın",
|
||||
"Your region is not allow to signup by phone": "منطقتك لا تسمح بالتسجيل عبر الهاتف",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "كلمة المرور أو الرمز غير صحيح",
|
||||
"password or code is incorrect, you have %s remaining chances": "كلمة المرور أو الرمز غير صحيح، لديك %s فرصة متبقية",
|
||||
"unsupported password type: %s": "نوع كلمة المرور غير مدعوم: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "هذه العملية غير مسموح بها في وضع العرض التوضيحي",
|
||||
"this operation requires administrator to perform": "هذه العملية تتطلب مسؤولاً لتنفيذها"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "خادم LDAP موجود"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "تم تمكين MFA للبريد الإلكتروني لكن البريد الإلكتروني فارغ",
|
||||
"MFA phone is enabled but phone number is empty": "تم تمكين MFA للهاتف لكن رقم الهاتف فارغ",
|
||||
"New password cannot contain blank space.": "كلمة المرور الجديدة لا يمكن أن تحتوي على مسافات.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "مالك المستخدم واسمه لا يجب أن يكونا فارغين"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Giriş uğursuz oldu: %s",
|
||||
"Invalid token": "Etibarsız token",
|
||||
"State expected: %s, but got: %s": "Gözlənilən vəziyyət: %s, lakin alınan: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Provayder üçün hesab: %s və istifadəçi adı: %s (%s) mövcud deyil və %%s vasitəsilə yeni hesab olaraq qeydiyyatdan keçməyə icazə verilmir, xahiş edirik qeydiyyat üçün başqa üsul istifadə edin",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Provayder üçün hesab: %s və istifadəçi adı: %s (%s) mövcud deyil və %s vasitəsilə yeni hesab olaraq qeydiyyatdan keçməyə icazə verilmir, xahiş edirik qeydiyyat üçün başqa üsul istifadə edin",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Provayder üçün hesab: %s və istifadəçi adı: %s (%s) mövcud deyil və yeni hesab olaraq qeydiyyatdan keçməyə icazə verilmir, xahiş edirik IT dəstəyinizlə əlaqə saxlayın",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Provayder üçün hesab: %s və istifadəçi adı: %s (%s) artıq başqa hesabla əlaqələndirilmişdir: %s (%s)",
|
||||
"The application: %s does not exist": "Tətbiq: %s mövcud deyil",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "IP ünvanınız: %s konfiqurasiyaya görə qadağan edilib: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Şifrənizin vaxtı keçib. Xahiş edirik \\\"Şifrəni unutdum\\\" düyməsinə basaraq şifrənizi sıfırlayın",
|
||||
"Your region is not allow to signup by phone": "Regionunuzda telefonla qeydiyyata icazə verilmir",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "şifrə və ya kod yanlışdır",
|
||||
"password or code is incorrect, you have %s remaining chances": "şifrə və ya kod yanlışdır, %s şansınız qalıb",
|
||||
"unsupported password type: %s": "dəstəklənməyən şifrə növü: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "bu əməliyyat demo rejimində icazə verilmir",
|
||||
"this operation requires administrator to perform": "bu əməliyyat administrator tərəfindən yerinə yetirilməsini tələb edir"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP serveri mövcuddur"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA email aktiv edilib, lakin email boşdur",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefon aktiv edilib, lakin telefon nömrəsi boşdur",
|
||||
"New password cannot contain blank space.": "Yeni şifrə boş yer ehtiva edə bilməz.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "istifadəçinin sahibi və adı boş olmamalıdır"
|
||||
},
|
||||
"util": {
|
||||
@@ -190,4 +195,4 @@
|
||||
"Found no credentials for this user": "Bu istifadəçi üçün heç bir etimadnamə tapılmadı",
|
||||
"Please call WebAuthnSigninBegin first": "Xahiş edirik əvvəlcə WebAuthnSigninBegin çağırın"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Nepodařilo se přihlásit: %s",
|
||||
"Invalid token": "Neplatný token",
|
||||
"State expected: %s, but got: %s": "Očekávaný stav: %s, ale získán: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) neexistuje a není povoleno se registrovat jako nový účet přes %%s, prosím použijte jiný způsob registrace",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) neexistuje a není povoleno se registrovat jako nový účet přes %s, prosím použijte jiný způsob registrace",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) neexistuje a není povoleno se registrovat jako nový účet, prosím kontaktujte svou IT podporu",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) je již propojen s jiným účtem: %s (%s)",
|
||||
"The application: %s does not exist": "Aplikace: %s neexistuje",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Vaše IP adresa: %s byla zablokována podle konfigurace: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Vaše heslo vypršelo. Prosím resetujte si heslo kliknutím na \\\"Zapomněl jsem heslo\\\"",
|
||||
"Your region is not allow to signup by phone": "Vaše oblast neumožňuje registraci pomocí telefonu",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "heslo nebo kód je nesprávný",
|
||||
"password or code is incorrect, you have %s remaining chances": "heslo nebo kód je nesprávné, máte %s zbývajících pokusů",
|
||||
"unsupported password type: %s": "nepodporovaný typ hesla: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "tato operace není povolena v demo režimu",
|
||||
"this operation requires administrator to perform": "tato operace vyžaduje administrátora k provedení"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Ldap server existuje"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA e-mail je povolen, ale e-mail je prázdný",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefon je povolen, ale telefonní číslo je prázdné",
|
||||
"New password cannot contain blank space.": "Nové heslo nemůže obsahovat prázdné místo.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "vlastník a jméno uživatele by neměly být prázdné"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Konnte nicht anmelden: %s",
|
||||
"Invalid token": "Ungültiges Token",
|
||||
"State expected: %s, but got: %s": "Erwarteter Zustand: %s, aber erhalten: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Das Konto für den Anbieter: %s und Benutzernamen: %s (%s) existiert nicht und darf nicht über %%s als neues Konto erstellt werden. Bitte nutzen Sie einen anderen Weg, um sich anzumelden",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Das Konto für den Anbieter: %s und Benutzernamen: %s (%s) existiert nicht und darf nicht über %s als neues Konto erstellt werden. Bitte nutzen Sie einen anderen Weg, um sich anzumelden",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Das Konto für den Anbieter %s und Benutzernamen %s (%s) existiert nicht und es ist nicht erlaubt, ein neues Konto anzumelden. Bitte wenden Sie sich an Ihren IT-Support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Das Konto für den Anbieter %s und Benutzernamen %s (%s) ist bereits mit einem anderen Konto verknüpft: %s (%s)",
|
||||
"The application: %s does not exist": "Die Anwendung: %s existiert nicht",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Ihre IP-Adresse: %s wurde laut Konfiguration gesperrt von: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Ihr Passwort ist abgelaufen. Bitte setzen Sie Ihr Passwort zurück, indem Sie auf \\\"Passwort vergessen\\\" klicken",
|
||||
"Your region is not allow to signup by phone": "Ihre Region ist nicht berechtigt, sich telefonisch anzumelden",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "Passwort oder Code ist falsch",
|
||||
"password or code is incorrect, you have %s remaining chances": "Das Passwort oder der Code ist falsch. Du hast noch %s Versuche übrig",
|
||||
"unsupported password type: %s": "Nicht unterstützter Passworttyp: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "Dieser Vorgang ist im Demo-Modus nicht erlaubt",
|
||||
"this operation requires administrator to perform": "Dieser Vorgang erfordert einen Administrator zur Ausführung"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Es gibt einen LDAP-Server"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA-E-Mail ist aktiviert, aber E-Mail ist leer",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-Telefon ist aktiviert, aber Telefonnummer ist leer",
|
||||
"New password cannot contain blank space.": "Das neue Passwort darf keine Leerzeichen enthalten.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "Eigentümer und Name des Benutzers dürfen nicht leer sein"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Failed to login in: %s",
|
||||
"Invalid token": "Invalid token",
|
||||
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Your IP address: %s has been banned according to the configuration of: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"",
|
||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "password or code is incorrect",
|
||||
"password or code is incorrect, you have %s remaining chances": "password or code is incorrect, you have %s remaining chances",
|
||||
"unsupported password type: %s": "unsupported password type: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode",
|
||||
"this operation requires administrator to perform": "this operation requires administrator to perform"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Ldap server exist"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA email is enabled but email is empty",
|
||||
"MFA phone is enabled but phone number is empty": "MFA phone is enabled but phone number is empty",
|
||||
"New password cannot contain blank space.": "New password cannot contain blank space.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "the user's owner and name should not be empty"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "No se ha podido iniciar sesión en: %s",
|
||||
"Invalid token": "Token inválido",
|
||||
"State expected: %s, but got: %s": "Estado esperado: %s, pero se obtuvo: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "La cuenta para el proveedor: %s y nombre de usuario: %s (%s) no existe y no está permitido registrarse como una cuenta nueva a través de %%s, por favor use otro método para registrarse",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "La cuenta para el proveedor: %s y nombre de usuario: %s (%s) no existe y no está permitido registrarse como una cuenta nueva a través de %s, por favor use otro método para registrarse",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "La cuenta para el proveedor: %s y el nombre de usuario: %s (%s) no existe y no se permite registrarse como una nueva cuenta, por favor contacte a su soporte de TI",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "La cuenta para proveedor: %s y nombre de usuario: %s (%s) ya está vinculada a otra cuenta: %s (%s)",
|
||||
"The application: %s does not exist": "La aplicación: %s no existe",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Su dirección IP: %s ha sido bloqueada según la configuración de: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Su contraseña ha expirado. Restablezca su contraseña haciendo clic en \\\"Olvidé mi contraseña\\\"",
|
||||
"Your region is not allow to signup by phone": "Tu región no está permitida para registrarse por teléfono",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "contraseña o código incorrecto",
|
||||
"password or code is incorrect, you have %s remaining chances": "Contraseña o código incorrecto, tienes %s intentos restantes",
|
||||
"unsupported password type: %s": "Tipo de contraseña no compatible: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "esta operación no está permitida en modo de demostración",
|
||||
"this operation requires administrator to perform": "esta operación requiere que el administrador la realice"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "El servidor LDAP existe"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "El correo electrónico MFA está habilitado pero el correo está vacío",
|
||||
"MFA phone is enabled but phone number is empty": "El teléfono MFA está habilitado pero el número de teléfono está vacío",
|
||||
"New password cannot contain blank space.": "La nueva contraseña no puede contener espacios en blanco.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "el propietario y el nombre del usuario no deben estar vacíos"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "عدم موفقیت در ورود: %s",
|
||||
"Invalid token": "توکن نامعتبر",
|
||||
"State expected: %s, but got: %s": "وضعیت مورد انتظار: %s، اما دریافت شد: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "حساب برای ارائهدهنده: %s و نام کاربری: %s (%s) وجود ندارد و مجاز به ثبتنام بهعنوان حساب جدید از طریق %%s نیست، لطفاً از روش دیگری برای ثبتنام استفاده کنید",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "حساب برای ارائهدهنده: %s و نام کاربری: %s (%s) وجود ندارد و مجاز به ثبتنام بهعنوان حساب جدید از طریق %s نیست، لطفاً از روش دیگری برای ثبتنام استفاده کنید",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "حساب برای ارائهدهنده: %s و نام کاربری: %s (%s) وجود ندارد و مجاز به ثبتنام بهعنوان حساب جدید نیست، لطفاً با پشتیبانی IT خود تماس بگیرید",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "حساب برای ارائهدهنده: %s و نام کاربری: %s (%s) در حال حاضر به حساب دیگری مرتبط است: %s (%s)",
|
||||
"The application: %s does not exist": "برنامه: %s وجود ندارد",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "آدرس IP شما: %s طبق تنظیمات بلاک شده است: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "رمز عبور شما منقضی شده است. لطفاً با کلیک بر روی \\\"فراموشی رمز عبور\\\" رمز عبور خود را بازنشانی کنید",
|
||||
"Your region is not allow to signup by phone": "منطقه شما اجازه ثبتنام با تلفن را ندارد",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "رمز عبور یا کد نادرست است",
|
||||
"password or code is incorrect, you have %s remaining chances": "رمز عبور یا کد نادرست است، شما %s فرصت باقیمانده دارید",
|
||||
"unsupported password type: %s": "نوع رمز عبور پشتیبانی نشده: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "این عملیات در حالت دمو مجاز نیست",
|
||||
"this operation requires administrator to perform": "این عملیات نیاز به مدیر برای انجام دارد"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "سرور LDAP وجود دارد"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "ایمیل MFA فعال است اما ایمیل خالی است",
|
||||
"MFA phone is enabled but phone number is empty": "تلفن MFA فعال است اما شماره تلفن خالی است",
|
||||
"New password cannot contain blank space.": "رمز عبور جدید نمیتواند حاوی فاصله خالی باشد.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "مالک و نام کاربر نباید خالی باشند"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Sisäänkirjautuminen epäonnistui: %s",
|
||||
"Invalid token": "Virheellinen token",
|
||||
"State expected: %s, but got: %s": "Odotettiin tilaa: %s, mutta saatiin: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Tiliä providerille: %s ja käyttäjälle: %s (%s) ei ole olemassa, eikä sitä voi rekisteröidä uutena tilinä kautta %%s, käytä toista tapaa rekisteröityä",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Tiliä providerille: %s ja käyttäjälle: %s (%s) ei ole olemassa, eikä sitä voi rekisteröidä uutena tilinä kautta %s, käytä toista tapaa rekisteröityä",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Tiliä providerille: %s ja käyttäjälle: %s (%s) ei ole olemassa, eikä sitä voi rekisteröidä uutena tilinä, ota yhteyttä IT-tukeen",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Tili providerille: %s ja käyttäjälle: %s (%s) on jo linkitetty toiseen tiliin: %s (%s)",
|
||||
"The application: %s does not exist": "Sovellus: %s ei ole olemassa",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "IP-osoitteesi: %s on estetty asetuksen mukaan: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Salasanasi on vanhentunut. Nollaa salasanasi klikkaamalla \\\"Unohdin salasanan\\\"",
|
||||
"Your region is not allow to signup by phone": "Alueellasi ei ole sallittua rekisteröityä puhelimella",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "salasana tai koodi on virheellinen",
|
||||
"password or code is incorrect, you have %s remaining chances": "salasana tai koodi on virheellinen, sinulla on %s yritystä jäljellä",
|
||||
"unsupported password type: %s": "ei-tuettu salasanatyyppi: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "tämä toiminto ei ole sallittu demo-tilassa",
|
||||
"this operation requires administrator to perform": "tämä toiminto vaatii ylläpitäjän suorittamista"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP-palvelin on olemassa"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA-sähköposti on käytössä, mutta sähköposti on tyhjä",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-puhelin on käytössä, mutta puhelinnumero on tyhjä",
|
||||
"New password cannot contain blank space.": "Uusi salasana ei voi sisältää välilyöntejä.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "käyttäjän omistaja ja nimi eivät saa olla tyhjiä"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Échec de la connexion : %s",
|
||||
"Invalid token": "Jeton invalide",
|
||||
"State expected: %s, but got: %s": "État attendu : %s, mais obtenu : %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Le compte pour le fournisseur : %s et le nom d'utilisateur : %s (%s) n'existe pas et n'est pas autorisé à s'inscrire en tant que nouveau compte via %%s, veuillez utiliser une autre méthode pour vous inscrire",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Le compte pour le fournisseur : %s et le nom d'utilisateur : %s (%s) n'existe pas et n'est pas autorisé à s'inscrire en tant que nouveau compte via %s, veuillez utiliser une autre méthode pour vous inscrire",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Le compte pour le fournisseur : %s et le nom d'utilisateur : %s (%s) n'existe pas et n'est pas autorisé à s'inscrire comme nouveau compte, veuillez contacter votre support informatique",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Le compte du fournisseur : %s et le nom d'utilisateur : %s (%s) sont déjà liés à un autre compte : %s (%s)",
|
||||
"The application: %s does not exist": "L'application : %s n'existe pas",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Votre adresse IP : %s a été bannie selon la configuration de : ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Votre mot de passe a expiré. Veuillez le réinitialiser en cliquant sur \\\"Mot de passe oublié\\\"",
|
||||
"Your region is not allow to signup by phone": "Votre région n'est pas autorisée à s'inscrire par téléphone",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "mot de passe ou code incorrect",
|
||||
"password or code is incorrect, you have %s remaining chances": "Le mot de passe ou le code est incorrect, il vous reste %s chances",
|
||||
"unsupported password type: %s": "Type de mot de passe non pris en charge : %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "cette opération n'est pas autorisée en mode démo",
|
||||
"this operation requires administrator to perform": "cette opération nécessite un administrateur pour être effectuée"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Le serveur LDAP existe"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "L'authentification MFA par e-mail est activée mais l'e-mail est vide",
|
||||
"MFA phone is enabled but phone number is empty": "L'authentification MFA par téléphone est activée mais le numéro de téléphone est vide",
|
||||
"New password cannot contain blank space.": "Le nouveau mot de passe ne peut pas contenir d'espace.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "le propriétaire et le nom de l'utilisateur ne doivent pas être vides"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "כניסה נכשלה: %s",
|
||||
"Invalid token": "אסימון שגוי",
|
||||
"State expected: %s, but got: %s": "מצב צפוי: %s, אך התקבל: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "החשבון עבור ספק: %s ושם משתמש: %s (%s) אינו קיים ואינו מאופשר להרשמה כחשבון חדש דרך %%s, אנא השתמש בדרך אחרת להרשמה",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "החשבון עבור ספק: %s ושם משתמש: %s (%s) אינו קיים ואינו מאופשר להרשמה כחשבון חדש דרך %s, אנא השתמש בדרך אחרת להרשמה",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "החשבון עבור ספק: %s ושם משתמש: %s (%s) אינו קיים ואינו מאופשר להרשמה כחשבון חדש, אנא צור קשר עם התמיכה הטכנית",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "החשבון עבור ספק: %s ושם משתמש: %s (%s) כבר מקושר לחשבון אחר: %s (%s)",
|
||||
"The application: %s does not exist": "האפליקציה: %s אינה קיימת",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "כתובת ה-IP שלך: %s נחסמה לפי התצורה של: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "הסיסמה שלך פגה. אנא אפס את הסיסמה שלך על ידי לחיצה על \\\"שכחתי סיסמה\\\"",
|
||||
"Your region is not allow to signup by phone": "האזור שלך אינו מאפשר הרשמה באמצעות טלפון",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "סיסמה או קוד שגויים",
|
||||
"password or code is incorrect, you have %s remaining chances": "סיסמה או קוד שגויים, יש לך %s ניסיונות נותרים",
|
||||
"unsupported password type: %s": "סוג סיסמה לא נתמך: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "פעולה זו אינה מותרת במצב הדגמה",
|
||||
"this operation requires administrator to perform": "פעולה זו דורשת מנהל לביצוע"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "שרת LDAP קיים"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA דוא\"ל מופעל אך הדוא\"ל ריק",
|
||||
"MFA phone is enabled but phone number is empty": "MFA טלפון מופעל אך מספר הטלפון ריק",
|
||||
"New password cannot contain blank space.": "הסיסמה החדשה אינה יכולה להכיל רווחים.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "הבעלים והשם של המשתמש אינם יכולים להיות ריקים"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Gagal masuk: %s",
|
||||
"Invalid token": "Token tidak valid",
|
||||
"State expected: %s, but got: %s": "Diharapkan: %s, tapi diperoleh: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Akun untuk penyedia: %s dan nama pengguna: %s (%s) tidak ada dan tidak diizinkan untuk mendaftar sebagai akun baru melalui %%s, silakan gunakan cara lain untuk mendaftar",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Akun untuk penyedia: %s dan nama pengguna: %s (%s) tidak ada dan tidak diizinkan untuk mendaftar sebagai akun baru melalui %s, silakan gunakan cara lain untuk mendaftar",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Akun untuk penyedia: %s dan nama pengguna: %s (%s) tidak ada dan tidak diizinkan untuk mendaftar sebagai akun baru, silakan hubungi dukungan IT Anda",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Akun untuk penyedia: %s dan username: %s (%s) sudah terhubung dengan akun lain: %s (%s)",
|
||||
"The application: %s does not exist": "Aplikasi: %s tidak ada",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Alamat IP Anda: %s telah diblokir sesuai konfigurasi: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Kata sandi Anda telah kedaluwarsa. Silakan atur ulang kata sandi dengan mengklik \\\"Lupa kata sandi\\\"",
|
||||
"Your region is not allow to signup by phone": "Wilayah Anda tidak diizinkan untuk mendaftar melalui telepon",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "kata sandi atau kode salah",
|
||||
"password or code is incorrect, you have %s remaining chances": "Sandi atau kode salah, Anda memiliki %s kesempatan tersisa",
|
||||
"unsupported password type: %s": "jenis sandi tidak didukung: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "operasi ini tidak diizinkan dalam mode demo",
|
||||
"this operation requires administrator to perform": "operasi ini memerlukan administrator untuk melakukannya"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Server ldap ada"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "Email MFA diaktifkan tetapi email kosong",
|
||||
"MFA phone is enabled but phone number is empty": "Telepon MFA diaktifkan tetapi nomor telepon kosong",
|
||||
"New password cannot contain blank space.": "Sandi baru tidak boleh mengandung spasi kosong.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "pemilik dan nama pengguna tidak boleh kosong"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Login fallito: %s",
|
||||
"Invalid token": "Token non valido",
|
||||
"State expected: %s, but got: %s": "Stato atteso: %s, ricevuto: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Account per provider: %s e utente: %s (%s) non esiste, registrazione tramite %%s non consentita",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Account per provider: %s e utente: %s (%s) non esiste, registrazione tramite %s non consentita",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Account per provider: %s e utente: %s (%s) non esiste, registrazione non consentita: contatta l'assistenza IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Account per provider: %s e utente: %s (%s) già collegato a un altro account: %s (%s)",
|
||||
"The application: %s does not exist": "L'app: %s non esiste",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Il tuo indirizzo IP: %s è stato bannato secondo la configurazione di: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "La tua password è scaduta. Reimposta la password cliccando \\\"Password dimenticata\\\"",
|
||||
"Your region is not allow to signup by phone": "Registrazione via telefono non consentita nella tua regione",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "password o codice errati",
|
||||
"password or code is incorrect, you have %s remaining chances": "password o codice errati, %s tentativi rimasti",
|
||||
"unsupported password type: %s": "tipo password non supportato: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "questa operazione non è consentita in modalità demo",
|
||||
"this operation requires administrator to perform": "questa operazione richiede un amministratore"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Server LDAP esistente"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "L'email MFA è abilitata ma l'email è vuota",
|
||||
"MFA phone is enabled but phone number is empty": "Il telefono MFA è abilitato ma il numero di telefono è vuoto",
|
||||
"New password cannot contain blank space.": "Nuova password non può contenere spazi",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "il proprietario e il nome dell'utente non devono essere vuoti"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "ログインできませんでした:%s",
|
||||
"Invalid token": "無効なトークン",
|
||||
"State expected: %s, but got: %s": "期待される状態: %s、実際には:%s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "プロバイダーのアカウント:%s とユーザー名:%s(%s)が存在せず、新しいアカウントを %%s 経由でサインアップすることはできません。他の方法でサインアップしてください",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "プロバイダーのアカウント:%s とユーザー名:%s(%s)が存在せず、新しいアカウントを %s 経由でサインアップすることはできません。他の方法でサインアップしてください",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "プロバイダー名:%sとユーザー名:%s(%s)のアカウントは存在しません。新しいアカウントとしてサインアップすることはできません。 ITサポートに連絡してください",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "プロバイダのアカウント:%s とユーザー名:%s (%s) は既に別のアカウント:%s (%s) にリンクされています",
|
||||
"The application: %s does not exist": "アプリケーション: %sは存在しません",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "あなたの IP アドレス「%s」は設定によりアクセスが禁止されています: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "パスワードの有効期限が切れています。「パスワードを忘れた方はこちら」をクリックしてリセットしてください",
|
||||
"Your region is not allow to signup by phone": "あなたの地域は電話でサインアップすることができません",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "パスワードまたはコードが正しくありません",
|
||||
"password or code is incorrect, you have %s remaining chances": "パスワードまたはコードが間違っています。あと %s 回の試行機会があります",
|
||||
"unsupported password type: %s": "サポートされていないパスワードタイプ:%s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "この操作はデモモードでは許可されていません",
|
||||
"this operation requires administrator to perform": "この操作は管理者権限が必要です"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAPサーバーは存在します"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA メールが有効になっていますが、メールアドレスが空です",
|
||||
"MFA phone is enabled but phone number is empty": "MFA 電話番号が有効になっていますが、電話番号が空です",
|
||||
"New password cannot contain blank space.": "新しいパスワードにはスペースを含めることはできません。",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "ユーザーのオーナーと名前は空にできません"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Inloggen mislukt: %s",
|
||||
"Invalid token": "Ongeldige token",
|
||||
"State expected: %s, but got: %s": "Verwachte state: %s, maar kreeg: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Account voor provider: %s en gebruikersnaam: %s (%s) bestaat niet en mag niet registreren via %%s, gebruik een andere methode",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Account voor provider: %s en gebruikersnaam: %s (%s) bestaat niet en mag niet registreren via %s, gebruik een andere methode",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Account voor provider: %s en gebruikersnaam: %s (%s) bestaat niet en mag niet registreren, contacteer IT-support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Account voor provider: %s en gebruikersnaam: %s (%s) is al gelinkt aan ander account: %s (%s)",
|
||||
"The application: %s does not exist": "Applicatie: %s bestaat niet",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Je IP-adres: %s is verbannen volgens configuratie van: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Сіздің пароліңіз мерзімі аяқталды. Кликтап \\\"Парольді ұмытып қалдым\\\" нұсқасын қалпына келтіріңіз",
|
||||
"Your region is not allow to signup by phone": "Registratie per telefoon niet toegestaan in jouw regio",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "wachtwoord of code onjuist",
|
||||
"password or code is incorrect, you have %s remaining chances": "wachtwoord of code onjuist, nog %s pogingen over",
|
||||
"unsupported password type: %s": "niet-ondersteund wachtwoordtype: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "deze handeling is niet toegestaan in demo-modus",
|
||||
"this operation requires administrator to perform": "deze handeling vereist beheerder"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP-server bestaat al"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA-e-mail is ingeschakeld maar e-mailadres is leeg",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-telefoon is ingeschakeld maar telefoonnummer is leeg",
|
||||
"New password cannot contain blank space.": "Nieuw wachtwoord mag geen spaties bevatten.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "eigenaar en naam van gebruiker mogen niet leeg zijn"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "로그인에 실패했습니다.: %s",
|
||||
"Invalid token": "유효하지 않은 토큰",
|
||||
"State expected: %s, but got: %s": "예상한 상태: %s, 실제 상태: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "제공자 계정: %s와 사용자 이름: %s (%s)은(는) 존재하지 않으며 %%s를 통해 새 계정으로 가입하는 것이 허용되지 않습니다. 다른 방법으로 가입하십시오",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "제공자 계정: %s와 사용자 이름: %s (%s)은(는) 존재하지 않으며 %s를 통해 새 계정으로 가입하는 것이 허용되지 않습니다. 다른 방법으로 가입하십시오",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "공급자 계정 %s과 사용자 이름 %s (%s)는 존재하지 않으며 새 계정으로 등록할 수 없습니다. IT 지원팀에 문의하십시오",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "공급자 계정 %s과 사용자 이름 %s(%s)는 이미 다른 계정 %s(%s)에 연결되어 있습니다",
|
||||
"The application: %s does not exist": "해당 애플리케이션(%s)이 존재하지 않습니다",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "IP 주소 %s이(가) 설정에 따라 차단되었습니다: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "비밀번호가 만료되었습니다. \\\"비밀번호 찾기\\\"를 클릭하여 비밀번호를 재설정하세요",
|
||||
"Your region is not allow to signup by phone": "당신의 지역은 전화로 가입할 수 없습니다",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "비밀번호 또는 코드가 올바르지 않습니다",
|
||||
"password or code is incorrect, you have %s remaining chances": "암호 또는 코드가 올바르지 않습니다. %s 번의 기회가 남아 있습니다",
|
||||
"unsupported password type: %s": "지원되지 않는 암호 유형: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "이 작업은 데모 모드에서 허용되지 않습니다",
|
||||
"this operation requires administrator to perform": "이 작업은 관리자 권한이 필요합니다"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP 서버가 존재합니다"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA 이메일이 활성화되었지만 이메일이 비어 있습니다",
|
||||
"MFA phone is enabled but phone number is empty": "MFA 전화번호가 활성화되었지만 전화번호가 비어 있습니다",
|
||||
"New password cannot contain blank space.": "새 비밀번호에는 공백이 포함될 수 없습니다.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "사용자의 소유자와 이름은 비워둘 수 없습니다"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Gagal log masuk: %s",
|
||||
"Invalid token": "Token tidak sah",
|
||||
"State expected: %s, but got: %s": "Jangkaan keadaan: %s, tetapi dapat: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Akaun untuk pembekal: %s dan nama pengguna: %s (%s) tidak wujud dan tidak dibenarkan daftar melalui %%s, sila guna cara lain",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Akaun untuk pembekal: %s dan nama pengguna: %s (%s) tidak wujud dan tidak dibenarkan daftar melalui %s, sila guna cara lain",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Akaun untuk pembekal: %s dan nama pengguna: %s (%s) tidak wujud dan tidak dibenarkan daftar, sila hubungi sokongan IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Akaun untuk pembekal: %s dan nama pengguna: %s (%s) sudah dipautkan kepada akaun lain: %s (%s)",
|
||||
"The application: %s does not exist": "Aplikasi: %s tidak wujud",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Alamat IP anda: %s telah disekat mengikut konfigurasi: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Kata laluan anda tamat. Sila tetapkan semula dengan klik \\\"Lupa kata laluan\\\"",
|
||||
"Your region is not allow to signup by phone": "Wilayah anda tidak dibenarkan daftar melalui telefon",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "kata laluan atau kod salah",
|
||||
"password or code is incorrect, you have %s remaining chances": "kata laluan atau kod salah, anda ada %s peluang lagi",
|
||||
"unsupported password type: %s": "jenis kata laluan tidak disokong: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "operasi ini tidak dibenarkan dalam mod demo",
|
||||
"this operation requires administrator to perform": "operasi ini perlukan pentadbir untuk jalankan"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Pelayan LDAP sudah wujud"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA emel dibenarkan tetapi emel kosong",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefon dibenarkan tetapi nombor telefon kosong",
|
||||
"New password cannot contain blank space.": "Kata laluan baharu tidak boleh ada ruang kosong.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "pemilik dan nama pengguna tidak boleh kosong"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Inloggen mislukt: %s",
|
||||
"Invalid token": "Ongeldige token",
|
||||
"State expected: %s, but got: %s": "Verwachtte state: %s, gekregen: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Gebruiker bestaat niet; aanmelden via %%s niet toegestaan, kies andere methode",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Gebruiker bestaat niet; aanmelden via %s niet toegestaan, kies andere methode",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Gebruiker bestaat niet; aanmelden niet toegestaan, neem contact op met IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Account al gekoppeld aan andere gebruiker: %s (%s)",
|
||||
"The application: %s does not exist": "Applicatie %s bestaat niet",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "IP-adres %s geblokkeerd volgens configuratie: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Wachtwoord verlopen; klik op \"Wachtwoord vergeten\"",
|
||||
"Your region is not allow to signup by phone": "Registratie per telefoon niet toegestaan in jouw regio",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "Verkeerd wachtwoord of code",
|
||||
"password or code is incorrect, you have %s remaining chances": "Verkeerd wachtwoord/code, nog %s pogingen",
|
||||
"unsupported password type: %s": "Niet-ondersteund wachtwoordtype: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "Handeling niet toegestaan in demo-modus",
|
||||
"this operation requires administrator to perform": "Alleen beheerder kan deze handeling uitvoeren"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP-server bestaat al"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA-e-mail ingeschakeld maar e-mailadres leeg",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-telefoon ingeschakeld maar nummer leeg",
|
||||
"New password cannot contain blank space.": "Nieuw wachtwoord mag geen spaties bevatten",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "Eigenaar en naam van gebruiker mogen niet leeg zijn"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Logowanie nie powiodło się: %s",
|
||||
"Invalid token": "Nieprawidłowy token",
|
||||
"State expected: %s, but got: %s": "Oczekiwano stanu: %s, ale otrzymano: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Konto dla dostawcy: %s i nazwy użytkownika: %s (%s) nie istnieje i nie można się zarejestrować jako nowe konto przez %%s, użyj innej metody rejestracji",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Konto dla dostawcy: %s i nazwy użytkownika: %s (%s) nie istnieje i nie można się zarejestrować jako nowe konto przez %s, użyj innej metody rejestracji",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Konto dla dostawcy: %s i nazwy użytkownika: %s (%s) nie istnieje i nie można się zarejestrować jako nowe konto, skontaktuj się z pomocą IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Konto dla dostawcy: %s i nazwy użytkownika: %s (%s) jest już powiązane z innym kontem: %s (%s)",
|
||||
"The application: %s does not exist": "Aplikacja: %s nie istnieje",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Twój adres IP: %s został zablokowany zgodnie z konfiguracją: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Twoje hasło wygasło. Zresetuj hasło klikając \\\"Zapomniałem hasła\\\"",
|
||||
"Your region is not allow to signup by phone": "Twój region nie pozwala na rejestrację przez telefon",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "hasło lub kod jest nieprawidłowe",
|
||||
"password or code is incorrect, you have %s remaining chances": "hasło lub kod jest nieprawidłowe, masz jeszcze %s prób",
|
||||
"unsupported password type: %s": "nieobsługiwany typ hasła: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "ta operacja nie jest dozwolona w trybie demo",
|
||||
"this operation requires administrator to perform": "ta operacja wymaga administratora do wykonania"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Serwer LDAP istnieje"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA email jest włączone, ale email jest pusty",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefon jest włączony, ale numer telefonu jest pusty",
|
||||
"New password cannot contain blank space.": "Nowe hasło nie może zawierać spacji.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "właściciel i nazwa użytkownika nie powinny być puste"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
{
|
||||
"account": {
|
||||
"Failed to add user": "Falha ao adicionar usuário",
|
||||
"Get init score failed, error: %w": "Obter pontuação inicial falhou, erro: %w",
|
||||
"Please sign out first": "Por favor, saia da sessão primeiro",
|
||||
"The application does not allow to sign up new account": "O aplicativo não permite a criação de uma nova conta"
|
||||
"Get init score failed, error: %w": "Falha ao obter pontuação inicial, erro: %w",
|
||||
"Please sign out first": "Por favor, saia primeiro",
|
||||
"The application does not allow to sign up new account": "O aplicativo não permite a criação de novas contas"
|
||||
},
|
||||
"auth": {
|
||||
"Challenge method should be S256": "Método de desafio deve ser S256",
|
||||
"Challenge method should be S256": "O método de desafio deve ser S256",
|
||||
"DeviceCode Invalid": "Código de dispositivo inválido",
|
||||
"Failed to create user, user information is invalid: %s": "Falha ao criar usuário, informação do usuário inválida: %s",
|
||||
"Failed to login in: %s": "Falha ao entrar em: %s",
|
||||
"Failed to create user, user information is invalid: %s": "Falha ao criar usuário, as informações são inválidas: %s",
|
||||
"Failed to login in: %s": "Falha ao fazer login em: %s",
|
||||
"Invalid token": "Token inválido",
|
||||
"State expected: %s, but got: %s": "Estado esperado: %s, mas recebeu: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "A conta para o provedor: %s e nome de usuário: %s (%s) não existe e não é permitido inscrever-se como uma nova conta via %%s, por favor, use outra forma de se inscrever",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "A conta para o provedor: %s e nome de usuário: %s (%s) não existe e não é permitido inscrever-se como uma nova conta entre em contato com seu suporte de TI",
|
||||
"State expected: %s, but got: %s": "Estado esperado: %s, mas recebido: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "A conta do provedor: %s e nome de usuário: %s (%s) não existe e não é permitido criar nova conta via %s, por favor, use outra forma de cadastro",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "A conta do provedor: %s e nome de usuário: %s (%s) não existe e não é permitido criar nova conta, entre em contato com o suporte de TI",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "A conta do provedor: %s e nome de usuário: %s (%s) já está vinculada a outra conta: %s (%s)",
|
||||
"The application: %s does not exist": "O aplicativo: %s não existe",
|
||||
"The application: %s has disabled users to signin": "The application: %s has disabled users to signin",
|
||||
"The login method: login with LDAP is not enabled for the application": "O método de login: login com LDAP não está ativado para a aplicação",
|
||||
"The login method: login with SMS is not enabled for the application": "O método de login: login com SMS não está ativado para a aplicação",
|
||||
"The login method: login with email is not enabled for the application": "O método de login: login com e-mail não está ativado para a aplicação",
|
||||
"The login method: login with face is not enabled for the application": "O método de login: login com reconhecimento facial não está ativado para a aplicação",
|
||||
"The login method: login with password is not enabled for the application": "O método de login: login com senha não está habilitado para o aplicativo",
|
||||
"The application: %s has disabled users to signin": "O aplicativo: %s desativou o login de usuários",
|
||||
"The login method: login with LDAP is not enabled for the application": "O método de login com LDAP não está habilitado para o aplicativo",
|
||||
"The login method: login with SMS is not enabled for the application": "O método de login com SMS não está habilitado para o aplicativo",
|
||||
"The login method: login with email is not enabled for the application": "O método de login com e-mail não está habilitado para o aplicativo",
|
||||
"The login method: login with face is not enabled for the application": "O método de login com reconhecimento facial não está habilitado para o aplicativo",
|
||||
"The login method: login with password is not enabled for the application": "O método de login com senha não está habilitado para o aplicativo",
|
||||
"The organization: %s does not exist": "A organização: %s não existe",
|
||||
"The organization: %s has disabled users to signin": "The organization: %s has disabled users to signin",
|
||||
"The organization: %s has disabled users to signin": "A organização: %s desativou o login de usuários",
|
||||
"The provider: %s does not exist": "O provedor: %s não existe",
|
||||
"The provider: %s is not enabled for the application": "O provedor: %s não está habilitado para o aplicativo",
|
||||
"Unauthorized operation": "Operação não autorizada",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Tipo de autenticação desconhecido (não é senha ou provedor), formulário = %s",
|
||||
"User's tag: %s is not listed in the application's tags": "A tag do usuário: %s não está listada nas tags da aplicação",
|
||||
"User's tag: %s is not listed in the application's tags": "A tag do usuário: %s não está listada nas tags do aplicativo",
|
||||
"UserCode Expired": "Código de usuário expirado",
|
||||
"UserCode Invalid": "Código de usuário inválido",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "usuário pago %s não possui assinatura ativa ou pendente e a aplicação: %s não possui preço padrão",
|
||||
"the application for user %s is not found": "a aplicação para o usuário %s não foi encontrada",
|
||||
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "O usuário pago %s não possui assinatura ativa ou pendente e o aplicativo: %s não possui preço padrão",
|
||||
"the application for user %s is not found": "o aplicativo para o usuário %s não foi encontrado",
|
||||
"the organization: %s is not found": "a organização: %s não foi encontrada"
|
||||
},
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "O serviço %s e %s não correspondem"
|
||||
},
|
||||
"check": {
|
||||
"%s does not meet the CIDR format requirements: %s": "%s não atende aos requisitos de formato CIDR: %s",
|
||||
"Affiliation cannot be blank": "A filiação não pode estar em branco",
|
||||
"CIDR for IP: %s should not be empty": "CIDR para IP: %s não deve estar vazio",
|
||||
"Default code does not match the code's matching rules": "O código padrão não corresponde às regras de correspondência do código",
|
||||
"%s does not meet the CIDR format requirements: %s": "%s não atende aos requisitos do formato CIDR: %s",
|
||||
"Affiliation cannot be blank": "A afiliação não pode estar em branco",
|
||||
"CIDR for IP: %s should not be empty": "O CIDR para o IP: %s não pode estar vazio",
|
||||
"Default code does not match the code's matching rules": "O código padrão não corresponde às regras de validação do código",
|
||||
"DisplayName cannot be blank": "O nome de exibição não pode estar em branco",
|
||||
"DisplayName is not valid real name": "O nome de exibição não é um nome real válido",
|
||||
"Email already exists": "O e-mail já existe",
|
||||
"Email cannot be empty": "O e-mail não pode estar vazio",
|
||||
"Email is invalid": "O e-mail é inválido",
|
||||
"Email is invalid": "E-mail inválido",
|
||||
"Empty username.": "Nome de usuário vazio.",
|
||||
"Face data does not exist, cannot log in": "Dados faciais não existem, não é possível fazer login",
|
||||
"Face data mismatch": "Incompatibilidade de dados faciais",
|
||||
"Failed to parse client IP: %s": "Falha ao analisar IP do cliente: %s",
|
||||
"Face data mismatch": "Dados faciais não correspondem",
|
||||
"Failed to parse client IP: %s": "Falha ao analisar o IP do cliente: %s",
|
||||
"FirstName cannot be blank": "O primeiro nome não pode estar em branco",
|
||||
"Invitation code cannot be blank": "O código de convite não pode estar em branco",
|
||||
"Invitation code exhausted": "Código de convite esgotado",
|
||||
"Invitation code exhausted": "O código de convite foi esgotado",
|
||||
"Invitation code is invalid": "Código de convite inválido",
|
||||
"Invitation code suspended": "Código de convite suspenso",
|
||||
"LDAP user name or password incorrect": "Nome de usuário ou senha LDAP incorretos",
|
||||
@@ -64,7 +64,7 @@
|
||||
"Password cannot be empty": "A senha não pode estar vazia",
|
||||
"Phone already exists": "O telefone já existe",
|
||||
"Phone cannot be empty": "O telefone não pode estar vazio",
|
||||
"Phone number is invalid": "O número de telefone é inválido",
|
||||
"Phone number is invalid": "Número de telefone inválido",
|
||||
"Please register using the email corresponding to the invitation code": "Por favor, registre-se usando o e-mail correspondente ao código de convite",
|
||||
"Please register using the phone corresponding to the invitation code": "Por favor, registre-se usando o telefone correspondente ao código de convite",
|
||||
"Please register using the username corresponding to the invitation code": "Por favor, registre-se usando o nome de usuário correspondente ao código de convite",
|
||||
@@ -73,66 +73,70 @@
|
||||
"The user is forbidden to sign in, please contact the administrator": "O usuário está proibido de entrar, entre em contato com o administrador",
|
||||
"The user: %s doesn't exist in LDAP server": "O usuário: %s não existe no servidor 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.": "O nome de usuário pode conter apenas caracteres alfanuméricos, sublinhados ou hífens, não pode ter hífens ou sublinhados consecutivos e não pode começar ou terminar com hífen ou sublinhado.",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "O valor \\\"%s\\\" para o campo de conta \\\"%s\\\" não corresponde à expressão regular do item de conta",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "O valor \\\"%s\\\" para o campo de registro \\\"%s\\\" não corresponde à expressão regular do item de registro da aplicação \\\"%s\\\"",
|
||||
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "O valor \\\"%s\\\" para o campo de conta \\\"%s\\\" não corresponde à expressão regular definida",
|
||||
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "O valor \\\"%s\\\" para o campo de registro \\\"%s\\\" não corresponde à expressão regular definida na aplicação \\\"%s\\\"",
|
||||
"Username already exists": "O nome de usuário já existe",
|
||||
"Username cannot be an email address": "O nome de usuário não pode ser um endereço de e-mail",
|
||||
"Username cannot contain white spaces": "O nome de usuário não pode conter espaços em branco",
|
||||
"Username cannot start with a digit": "O nome de usuário não pode começar com um dígito",
|
||||
"Username is too long (maximum is 255 characters).": "Nome de usuário é muito longo (máximo é 255 caracteres).",
|
||||
"Username must have at least 2 characters": "Nome de usuário deve ter pelo menos 2 caracteres",
|
||||
"Username supports email format. Also 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. Also pay attention to the email format.": "O nome de usuário suporta formato de e-mail. Além disso, o nome de usuário pode conter apenas caracteres alfanuméricos, sublinhados ou hífens, não pode ter hífens ou sublinhados consecutivos e não pode começar ou terminar com hífen ou sublinhado. Preste atenção também ao formato de e-mail.",
|
||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Você digitou a senha ou o código incorretos muitas vezes, aguarde %d minutos e tente novamente",
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Seu endereço IP: %s foi banido de acordo com a configuração de: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Sua senha expirou. Por favor, redefina sua senha clicando em \\\"Esqueci a senha\\\"",
|
||||
"Username cannot start with a digit": "O nome de usuário não pode começar com um número",
|
||||
"Username is too long (maximum is 255 characters).": "O nome de usuário é muito longo (máximo de 255 caracteres).",
|
||||
"Username must have at least 2 characters": "O nome de usuário deve ter pelo menos 2 caracteres",
|
||||
"Username supports email format. Also 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. Also pay attention to the email format.": "O nome de usuário suporta o formato de e-mail. Além disso, pode conter apenas caracteres alfanuméricos, sublinhados ou hífens, não pode ter hífens ou sublinhados consecutivos e não pode começar ou terminar com hífen ou sublinhado. Também preste atenção ao formato do e-mail.",
|
||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Você digitou a senha ou o código incorretos muitas vezes. Aguarde %d minutos e tente novamente",
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Seu endereço IP: %s foi bloqueado de acordo com a configuração de: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Sua senha expirou. Por favor, redefina-a clicando em \\\"Esqueci a senha\\\"",
|
||||
"Your region is not allow to signup by phone": "Sua região não permite cadastro por telefone",
|
||||
"password or code is incorrect": "senha ou código incorreto",
|
||||
"password or code is incorrect, you have %s remaining chances": "senha ou código está incorreto, você tem %s chances restantes",
|
||||
"unsupported password type: %s": "tipo de senha não suportado: %s"
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "Senha ou código incorreto",
|
||||
"password or code is incorrect, you have %s remaining chances": "Senha ou código incorreto, você tem %s tentativas restantes",
|
||||
"unsupported password type: %s": "Tipo de senha não suportado: %s"
|
||||
},
|
||||
"enforcer": {
|
||||
"the adapter: %s is not found": "o adaptador: %s não foi encontrado"
|
||||
"the adapter: %s is not found": "O adaptador: %s não foi encontrado"
|
||||
},
|
||||
"general": {
|
||||
"Failed to import groups": "Falha ao importar grupos",
|
||||
"Failed to import users": "Falha ao importar usuários",
|
||||
"Missing parameter": "Parâmetro faltante",
|
||||
"Only admin user can specify user": "Apenas usuário administrador pode especificar usuário",
|
||||
"Please login first": "Faça login primeiro",
|
||||
"The organization: %s should have one application at least": "A organização: %s deve ter pelo menos uma aplicação",
|
||||
"Missing parameter": "Parâmetro ausente",
|
||||
"Only admin user can specify user": "Apenas um administrador pode especificar um usuário",
|
||||
"Please login first": "Por favor, faça login primeiro",
|
||||
"The organization: %s should have one application at least": "A organização: %s deve ter pelo menos um aplicativo",
|
||||
"The user: %s doesn't exist": "O usuário: %s não existe",
|
||||
"Wrong userId": "ID de usuário incorreto",
|
||||
"don't support captchaProvider: ": "não suporta captchaProvider: ",
|
||||
"don't support captchaProvider: ": "captchaProvider não suportado: ",
|
||||
"this operation is not allowed in demo mode": "esta operação não é permitida no modo de demonstração",
|
||||
"this operation requires administrator to perform": "esta operação requer um administrador para executar"
|
||||
"this operation requires administrator to perform": "esta operação requer privilégios de administrador"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "O convite %s não existe"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Servidor LDAP existe"
|
||||
"Ldap server exist": "O servidor LDAP existe"
|
||||
},
|
||||
"link": {
|
||||
"Please link first": "Vincule primeiro",
|
||||
"This application has no providers": "Este aplicativo não tem provedores",
|
||||
"This application has no providers of type": "Este aplicativo não tem provedores do tipo",
|
||||
"Please link first": "Por favor, vincule primeiro",
|
||||
"This application has no providers": "Este aplicativo não possui provedores",
|
||||
"This application has no providers of type": "Este aplicativo não possui provedores do tipo especificado",
|
||||
"This provider can't be unlinked": "Este provedor não pode ser desvinculado",
|
||||
"You are not the global admin, you can't unlink other users": "Você não é o administrador global, não pode desvincular outros usuários",
|
||||
"You can't unlink yourself, you are not a member of any application": "Você não pode se desvincular, não é membro de nenhum aplicativo"
|
||||
"You can't unlink yourself, you are not a member of any application": "Você não pode se desvincular, pois não é membro de nenhum aplicativo"
|
||||
},
|
||||
"organization": {
|
||||
"Only admin can modify the %s.": "Apenas o administrador pode modificar o %s.",
|
||||
"The %s is immutable.": "O %s é imutável.",
|
||||
"Unknown modify rule %s.": "Regra de modificação %s desconhecida.",
|
||||
"adding a new user to the 'built-in' organization is currently disabled. Please note: all users in the 'built-in' organization are global administrators in Casdoor. Refer to the docs: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. If you still wish to create a user for the 'built-in' organization, go to the organization's settings page and enable the 'Has privilege consent' option.": "nte desativada. Observe que todos os usuários na organização 'built-in' são administradores globais no Casdoor. Consulte a documentação: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Se ainda desejar criar um usuário para a organização 'built-in', acesse a página de configurações da organização e habilite a opção 'Possui consentimento de privilégios'."
|
||||
"Unknown modify rule %s.": "Regra de modificação desconhecida: %s.",
|
||||
"adding a new user to the 'built-in' organization is currently disabled. Please note: all users in the 'built-in' organization are global administrators in Casdoor. Refer to the docs: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. If you still wish to create a user for the 'built-in' organization, go to the organization's settings page and enable the 'Has privilege consent' option.": "A adição de novos usuários à organização 'built-in' está desativada. Observe que todos os usuários nessa organização são administradores globais no Casdoor. Consulte a documentação: https://casdoor.org/docs/basic/core-concepts#how-does-casdoor-manage-itself. Se ainda desejar criar um usuário, vá até a página de configurações da organização e habilite a opção 'Possui consentimento de privilégios'."
|
||||
},
|
||||
"permission": {
|
||||
"The permission: \\\"%s\\\" doesn't exist": "A permissão: \\\"%s\\\" não existe"
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Id do aplicativo inválido",
|
||||
"the provider: %s does not exist": "o provedor: %s não existe"
|
||||
"Invalid application id": "ID de aplicativo inválido",
|
||||
"the provider: %s does not exist": "O provedor: %s não existe"
|
||||
},
|
||||
"resource": {
|
||||
"User is nil for tag: avatar": "Usuário é nulo para tag: avatar",
|
||||
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Nome de usuário ou fullFilePath está vazio: username = %s, fullFilePath = %s"
|
||||
"User is nil for tag: avatar": "Usuário nulo para a tag: avatar",
|
||||
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Nome de usuário ou fullFilePath vazio: username = %s, fullFilePath = %s"
|
||||
},
|
||||
"saml": {
|
||||
"Application %s not found": "Aplicativo %s não encontrado"
|
||||
@@ -146,7 +150,7 @@
|
||||
"Invalid phone receivers: %s": "Destinatários de telefone inválidos: %s"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "O objectKey: %s não é permitido",
|
||||
"The objectKey: %s is not allowed": "A chave de objeto: %s não é permitida",
|
||||
"The provider type: %s is not supported": "O tipo de provedor: %s não é suportado"
|
||||
},
|
||||
"subscription": {
|
||||
@@ -154,40 +158,41 @@
|
||||
},
|
||||
"token": {
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s não é suportado neste aplicativo",
|
||||
"Invalid application or wrong clientSecret": "Aplicativo inválido ou clientSecret errado",
|
||||
"Invalid application or wrong clientSecret": "Aplicativo inválido ou clientSecret incorreto",
|
||||
"Invalid client_id": "client_id inválido",
|
||||
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "URI de redirecionamento: %s não existe na lista de URI de redirecionamento permitida",
|
||||
"Token not found, invalid accessToken": "Token não encontrado, token de acesso inválido"
|
||||
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "O URI de redirecionamento: %s não existe na lista de URIs permitidos",
|
||||
"Token not found, invalid accessToken": "Token não encontrado, accessToken inválido"
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Nome de exibição não pode ser vazio",
|
||||
"MFA email is enabled but email is empty": "MFA por e-mail está ativado, mas o e-mail está vazio",
|
||||
"MFA phone is enabled but phone number is empty": "MFA por telefone está ativado, mas o número de telefone está vazio",
|
||||
"New password cannot contain blank space.": "A nova senha não pode conter espaço em branco.",
|
||||
"the user's owner and name should not be empty": "o proprietário e o nome do usuário não devem estar vazios"
|
||||
"Display name cannot be empty": "O nome de exibição não pode estar vazio",
|
||||
"MFA email is enabled but email is empty": "MFA por e-mail está habilitado, mas o e-mail está vazio",
|
||||
"MFA phone is enabled but phone number is empty": "MFA por telefone está habilitado, mas o número de telefone está vazio",
|
||||
"New password cannot contain blank space.": "A nova senha não pode conter espaços em branco.",
|
||||
"The new password must be different from your current password": "A nova senha deve ser diferente da senha atual",
|
||||
"the user's owner and name should not be empty": "O proprietário e o nome do usuário não devem estar vazios"
|
||||
},
|
||||
"util": {
|
||||
"No application is found for userId: %s": "Nenhum aplicativo encontrado para userId: %s",
|
||||
"No provider for category: %s is found for application: %s": "Nenhum provedor para categoria: %s encontrado para aplicativo: %s",
|
||||
"No application is found for userId: %s": "Nenhum aplicativo encontrado para o userId: %s",
|
||||
"No provider for category: %s is found for application: %s": "Nenhum provedor da categoria: %s encontrado para o aplicativo: %s",
|
||||
"The provider: %s is not found": "O provedor: %s não foi encontrado"
|
||||
},
|
||||
"verification": {
|
||||
"Invalid captcha provider.": "Provedor de captcha inválido.",
|
||||
"Phone number is invalid in your region %s": "Número de telefone é inválido em sua região %s",
|
||||
"Phone number is invalid in your region %s": "Número de telefone inválido na sua região %s",
|
||||
"The verification code has already been used!": "O código de verificação já foi utilizado!",
|
||||
"The verification code has not been sent yet!": "O código de verificação ainda não foi enviado!",
|
||||
"Turing test failed.": "Teste de Turing falhou.",
|
||||
"Turing test failed.": "O teste de Turing falhou.",
|
||||
"Unable to get the email modify rule.": "Não foi possível obter a regra de modificação de e-mail.",
|
||||
"Unable to get the phone modify rule.": "Não foi possível obter a regra de modificação de telefone.",
|
||||
"Unknown type": "Tipo desconhecido",
|
||||
"Wrong verification code!": "Código de verificação incorreto!",
|
||||
"You should verify your code in %d min!": "Você deve verificar seu código em %d min!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "por favor, adicione um provedor de SMS à lista \\\"Providers\\\" da aplicação: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "por favor, adicione um provedor de e-mail à lista \\\"Providers\\\" da aplicação: %s",
|
||||
"the user does not exist, please sign up first": "o usuário não existe, cadastre-se primeiro"
|
||||
"You should verify your code in %d min!": "Você deve verificar seu código em %d minuto(s)!",
|
||||
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "Adicione um provedor de SMS à lista \\\"Providers\\\" do aplicativo: %s",
|
||||
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "Adicione um provedor de e-mail à lista \\\"Providers\\\" do aplicativo: %s",
|
||||
"the user does not exist, please sign up first": "O usuário não existe, cadastre-se primeiro"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Please call WebAuthnSigninBegin first": "Por favor, chame WebAuthnSigninBegin primeiro"
|
||||
"Found no credentials for this user": "Nenhuma credencial encontrada para este usuário",
|
||||
"Please call WebAuthnSigninBegin first": "Por favor, inicie com WebAuthnSigninBegin primeiro"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Не удалось войти в систему: %s",
|
||||
"Invalid token": "Недействительный токен",
|
||||
"State expected: %s, but got: %s": "Ожидался статус: %s, но получен: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Аккаунт провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован через %%s, пожалуйста, используйте другой способ регистрации",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Аккаунт провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован через %s, пожалуйста, используйте другой способ регистрации",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Аккаунт для провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован как новый аккаунт. Пожалуйста, обратитесь в службу поддержки IT",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Аккаунт поставщика: %s и имя пользователя: %s (%s) уже связаны с другим аккаунтом: %s (%s)",
|
||||
"The application: %s does not exist": "Приложение: %s не существует",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Ваш IP-адрес: %s заблокирован согласно конфигурации: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Срок действия вашего пароля истек. Пожалуйста, сбросьте пароль, нажав \\\"Забыли пароль\\\"",
|
||||
"Your region is not allow to signup by phone": "Ваш регион не разрешает регистрацию по телефону",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "пароль или код неверны",
|
||||
"password or code is incorrect, you have %s remaining chances": "Неправильный пароль или код, у вас осталось %s попыток",
|
||||
"unsupported password type: %s": "неподдерживаемый тип пароля: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "эта операция недоступна в демонстрационном режиме",
|
||||
"this operation requires administrator to perform": "эта операция требует прав администратора"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP-сервер существует"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA по электронной почте включен, но электронная почта не указана",
|
||||
"MFA phone is enabled but phone number is empty": "MFA по телефону включен, но номер телефона не указан",
|
||||
"New password cannot contain blank space.": "Новый пароль не может содержать пробелы.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "владелец и имя пользователя не должны быть пустыми"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Prihlásenie zlyhalo: %s",
|
||||
"Invalid token": "Neplatný token",
|
||||
"State expected: %s, but got: %s": "Očakávaný stav: %s, ale dostali sme: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) neexistuje a nie je povolené zaregistrovať nový účet cez %%s, prosím použite iný spôsob registrácie",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) neexistuje a nie je povolené zaregistrovať nový účet cez %s, prosím použite iný spôsob registrácie",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) neexistuje a nie je povolené zaregistrovať nový účet, prosím kontaktujte vašu IT podporu",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) je už prepojený s iným účtom: %s (%s)",
|
||||
"The application: %s does not exist": "Aplikácia: %s neexistuje",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Vaša IP adresa: %s bola zablokovaná podľa konfigurácie: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Vaše heslo je expirované. Prosím, resetujte svoje heslo kliknutím na \\\"Zabudnuté heslo\\\"",
|
||||
"Your region is not allow to signup by phone": "Váš región neumožňuje registráciu cez telefón",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "heslo alebo kód je nesprávne",
|
||||
"password or code is incorrect, you have %s remaining chances": "heslo alebo kód je nesprávne, máte %s zostávajúcich pokusov",
|
||||
"unsupported password type: %s": "nepodporovaný typ hesla: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "táto operácia nie je povolená v demo režime",
|
||||
"this operation requires administrator to perform": "táto operácia vyžaduje vykonanie administrátorom"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP server existuje"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA e-mail je zapnutý, ale e-mail je prázdny",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefón je zapnutý, ale telefónne číslo je prázdne",
|
||||
"New password cannot contain blank space.": "Nové heslo nemôže obsahovať medzery.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "vlastník a meno používateľa nesmú byť prázdne"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Misslyckades logga in: %s",
|
||||
"Invalid token": "Ogiltig token",
|
||||
"State expected: %s, but got: %s": "Förväntat tillstånd: %s, men fick: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Kontot för leverantör: %s och användarnamn: %s (%s) finns inte och det är inte tillåtet att registrera ett nytt konto via %%s, använd ett annat sätt att registrera dig",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Kontot för leverantör: %s och användarnamn: %s (%s) finns inte och det är inte tillåtet att registrera ett nytt konto via %s, använd ett annat sätt att registrera dig",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Kontot för leverantör: %s och användarnamn: %s (%s) finns inte och det är inte tillåtet att registrera ett nytt konto, kontakta din IT-support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Kontot för leverantör: %s och användarnamn: %s (%s) är redan länkat till ett annat konto: %s (%s)",
|
||||
"The application: %s does not exist": "Applikationen: %s finns inte",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Din IP-adress: %s har blockerats enligt konfigurationen av: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Ditt lösenord har gått ut. Återställ det genom att klicka på \\\"Glömt lösenord\\\"",
|
||||
"Your region is not allow to signup by phone": "Din region tillåter inte registrering via telefon",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "lösenord eller kod är felaktig",
|
||||
"password or code is incorrect, you have %s remaining chances": "lösenord eller kod är felaktig, du har %s försök kvar",
|
||||
"unsupported password type: %s": "lösenordstypen stöds inte: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "denna åtgärd är inte tillåten i demoläge",
|
||||
"this operation requires administrator to perform": "denna åtgärd kräver administratör för att genomföras"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP-servern finns redan"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA-e-post är aktiverat men e-post är tom",
|
||||
"MFA phone is enabled but phone number is empty": "MFA-telefon är aktiverat men telefonnummer är tomt",
|
||||
"New password cannot contain blank space.": "Nytt lösenord får inte innehålla mellanslag.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "användarens ägare och namn får inte vara tomma"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Giriş yapılamadı: %s",
|
||||
"Invalid token": "Geçersiz token",
|
||||
"State expected: %s, but got: %s": "Beklenen durum: %s, fakat alınan: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Provider: %s ve kullanıcı adı: %s (%s) için hesap mevcut değil ve %%s ile yeni hesap açılmasına izin verilmiyor, lütfen başka yöntemle kaydolun",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Provider: %s ve kullanıcı adı: %s (%s) için hesap mevcut değil ve %s ile yeni hesap açılmasına izin verilmiyor, lütfen başka yöntemle kaydolun",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Provider: %s ve kullanıcı adı: %s (%s) için hesap mevcut değil ve yeni hesap açılmasına izin verilmiyor, lütfen BT destek ile iletişime geçin",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Provider: %s ve kullanıcı adı: %s (%s) zaten başka bir hesaba bağlı: %s (%s)",
|
||||
"The application: %s does not exist": "Uygulama: %s bulunamadı",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "IP adresiniz: %s, yapılandırmaya göre yasaklandı: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Şifrenizin süresi doldu. Lütfen \\\"Şifremi unuttum\\\"a tıklayarak şifrenizi sıfırlayın",
|
||||
"Your region is not allow to signup by phone": "Bölgeniz telefonla kayıt yapmaya izin verilmiyor",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "şifre veya kod yanlış",
|
||||
"password or code is incorrect, you have %s remaining chances": "şifre veya kod yanlış, %s hakkınız kaldı",
|
||||
"unsupported password type: %s": "desteklenmeyen şifre türü: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "bu işlem demo modunda izin verilmiyor",
|
||||
"this operation requires administrator to perform": "bu işlem yönetici tarafından gerçekleştirilmelidir"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP sunucusu zaten var"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA e-postası etkin ancak e-posta boş",
|
||||
"MFA phone is enabled but phone number is empty": "MFA telefonu etkin ancak telefon numarası boş",
|
||||
"New password cannot contain blank space.": "Yeni şifre boşluk içeremez.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "kullanıcının sahibi ve adı boş olmamalıdır"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Не вдалося увійти: %s",
|
||||
"Invalid token": "Недійсний токен",
|
||||
"State expected: %s, but got: %s": "Очікувалося стан: %s, але отримано: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Обліковий запис для провайдера: %s та імені користувача: %s (%s) не існує і не дозволяється реєструвати як новий через %%s, використайте інший спосіб",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Обліковий запис для провайдера: %s та імені користувача: %s (%s) не існує і не дозволяється реєструвати як новий через %s, використайте інший спосіб",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Обліковий запис для провайдера: %s та імені користувача: %s (%s) не існує і не дозволяється реєструвати як новий, зверніться до IT-підтримки",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Обліковий запис для провайдера: %s та імені користувача: %s (%s) уже пов’язаний з іншим обліковим записом: %s (%s)",
|
||||
"The application: %s does not exist": "Додаток: %s не існує",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Ваша IP-адреса: %s заблокована відповідно до конфігурації: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Ditt lösenord har gått ut. Återställ det genom att klicka på \\\"Glömt lösenord\\\"",
|
||||
"Your region is not allow to signup by phone": "У вашому регіоні реєстрація за телефоном недоступна",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "пароль або код неправильний",
|
||||
"password or code is incorrect, you have %s remaining chances": "пароль або код неправильний, у вас залишилось %s спроб",
|
||||
"unsupported password type: %s": "непідтримуваний тип пароля: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "ця операція недоступна в демо-режимі",
|
||||
"this operation requires administrator to perform": "ця операція потребує прав адміністратора"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Сервер LDAP існує"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA email увімкнено, але email порожній",
|
||||
"MFA phone is enabled but phone number is empty": "MFA телефон увімкнено, але номер телефону порожній",
|
||||
"New password cannot contain blank space.": "Новий пароль не може містити пробіли.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "власник ім’я користувача не повинні бути порожніми"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "Đăng nhập không thành công: %s",
|
||||
"Invalid token": "Mã thông báo không hợp lệ",
|
||||
"State expected: %s, but got: %s": "Trạng thái dự kiến: %s, nhưng nhận được: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) không tồn tại và không được phép đăng ký làm tài khoản mới qua %%s, vui lòng sử dụng cách khác để đăng ký",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) không tồn tại và không được phép đăng ký làm tài khoản mới qua %s, vui lòng sử dụng cách khác để đăng ký",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) không tồn tại và không được phép đăng ký như một tài khoản mới, vui lòng liên hệ với bộ phận hỗ trợ công nghệ thông tin của bạn",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) đã được liên kết với tài khoản khác: %s (%s)",
|
||||
"The application: %s does not exist": "Ứng dụng: %s không tồn tại",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "Địa chỉ IP của bạn: %s đã bị cấm theo cấu hình của: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "Mật khẩu của bạn đã hết hạn. Vui lòng đặt lại mật khẩu bằng cách nhấp vào \\\"Quên mật khẩu\\\"",
|
||||
"Your region is not allow to signup by phone": "Vùng của bạn không được phép đăng ký bằng điện thoại",
|
||||
"form type %s already exists": "form type %s already exists",
|
||||
"password or code is incorrect": "mật khẩu hoặc mã không đúng",
|
||||
"password or code is incorrect, you have %s remaining chances": "Mật khẩu hoặc mã không chính xác, bạn còn %s lần cơ hội",
|
||||
"unsupported password type: %s": "Loại mật khẩu không được hỗ trợ: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "thao tác này không được phép trong chế độ demo",
|
||||
"this operation requires administrator to perform": "thao tác này yêu cầu quản trị viên thực hiện"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "Invitation %s does not exist"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Máy chủ LDAP tồn tại"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA email đã bật nhưng email trống",
|
||||
"MFA phone is enabled but phone number is empty": "MFA điện thoại đã bật nhưng số điện thoại trống",
|
||||
"New password cannot contain blank space.": "Mật khẩu mới không thể chứa dấu trắng.",
|
||||
"The new password must be different from your current password": "The new password must be different from your current password",
|
||||
"the user's owner and name should not be empty": "chủ sở hữu và tên người dùng không được để trống"
|
||||
},
|
||||
"util": {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"Failed to login in: %s": "登录失败: %s",
|
||||
"Invalid token": "无效token",
|
||||
"State expected: %s, but got: %s": "期望状态为: %s, 实际状态为: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许通过 %s 注册新账户, 请使用其他方式注册",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %s, please use another way to sign up": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许通过 %s 注册新账户, 请使用其他方式注册",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许注册新账户, 请联系IT支持",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "提供商账户: %s与用户名: %s (%s)已经与其他账户绑定: %s (%s)",
|
||||
"The application: %s does not exist": "应用%s不存在",
|
||||
@@ -86,6 +86,7 @@
|
||||
"Your IP address: %s has been banned according to the configuration of: ": "您的IP地址:%s 根据以下配置已被禁止: ",
|
||||
"Your password has expired. Please reset your password by clicking \\\"Forgot password\\\"": "您的密码已过期。请点击 \\\"忘记密码\\\" 以重置密码",
|
||||
"Your region is not allow to signup by phone": "所在地区不支持手机号注册",
|
||||
"form type %s already exists": "表单类型 %s 已经存在",
|
||||
"password or code is incorrect": "密码错误",
|
||||
"password or code is incorrect, you have %s remaining chances": "密码错误,您还有 %s 次尝试的机会",
|
||||
"unsupported password type: %s": "不支持的密码类型: %s"
|
||||
@@ -106,6 +107,9 @@
|
||||
"this operation is not allowed in demo mode": "demo模式下不允许该操作",
|
||||
"this operation requires administrator to perform": "只有管理员才能进行此操作"
|
||||
},
|
||||
"invitation": {
|
||||
"Invitation %s does not exist": "邀请%s不存在"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "LDAP服务器已存在"
|
||||
},
|
||||
@@ -164,6 +168,7 @@
|
||||
"MFA email is enabled but email is empty": "MFA 电子邮件已启用,但电子邮件为空",
|
||||
"MFA phone is enabled but phone number is empty": "MFA 电话已启用,但电话号码为空",
|
||||
"New password cannot contain blank space.": "新密码不可以包含空格",
|
||||
"The new password must be different from your current password": "新密码必须与您当前的密码不同",
|
||||
"the user's owner and name should not be empty": "用户的组织和名称不能为空"
|
||||
},
|
||||
"util": {
|
||||
|
||||
11
idp/lark.go
11
idp/lark.go
@@ -135,6 +135,7 @@ func (idp *LarkIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
"open_id": "ou-caecc734c2e3328a62489fe0648c4b98779515d3",
|
||||
"union_id": "on-d89jhsdhjsajkda7828enjdj328ydhhw3u43yjhdj",
|
||||
"email": "zhangsan@feishu.cn",
|
||||
"enterprise_email": "zhangsan@company.com",
|
||||
"user_id": "5d9bdxxx",
|
||||
"mobile": "+86130002883xx",
|
||||
"tenant_key": "736588c92lxf175d",
|
||||
@@ -160,6 +161,7 @@ type LarkUserInfo struct {
|
||||
OpenId string `json:"open_id"`
|
||||
UnionId string `json:"union_id"`
|
||||
Email string `json:"email"`
|
||||
EnterpriseEmail string `json:"enterprise_email"`
|
||||
UserId string `json:"user_id"`
|
||||
Mobile string `json:"mobile"`
|
||||
TenantKey string `json:"tenant_key"`
|
||||
@@ -168,7 +170,6 @@ type LarkUserInfo struct {
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// GetUserInfo use LarkAccessToken gotten before return LinkedInUserInf
|
||||
// GetUserInfo use LarkAccessToken gotten before return LinkedInUserInfo
|
||||
// get more detail via: https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin?context=linkedin/consumer/context
|
||||
func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
@@ -207,6 +208,12 @@ func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Use enterprise_email as fallback when email is empty
|
||||
email := larkUserInfo.Data.Email
|
||||
if email == "" {
|
||||
email = larkUserInfo.Data.EnterpriseEmail
|
||||
}
|
||||
|
||||
var phoneNumber string
|
||||
var countryCode string
|
||||
if len(larkUserInfo.Data.Mobile) != 0 {
|
||||
@@ -222,7 +229,7 @@ func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
||||
Id: larkUserInfo.Data.OpenId,
|
||||
DisplayName: larkUserInfo.Data.Name,
|
||||
Username: larkUserInfo.Data.UserId,
|
||||
Email: larkUserInfo.Data.Email,
|
||||
Email: email,
|
||||
AvatarUrl: larkUserInfo.Data.AvatarUrl,
|
||||
Phone: phoneNumber,
|
||||
CountryCode: countryCode,
|
||||
|
||||
@@ -60,6 +60,11 @@ type SamlItem struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type JwtItem struct {
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
type Application struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
@@ -67,6 +72,7 @@ type Application struct {
|
||||
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
Logo string `xorm:"varchar(200)" json:"logo"`
|
||||
Order int `json:"order"`
|
||||
HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"`
|
||||
Description string `xorm:"varchar(100)" json:"description"`
|
||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
@@ -96,6 +102,7 @@ type Application struct {
|
||||
CertPublicKey string `xorm:"-" json:"certPublicKey"`
|
||||
Tags []string `xorm:"mediumtext" json:"tags"`
|
||||
SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
|
||||
SamlHashAlgorithm string `xorm:"varchar(20)" json:"samlHashAlgorithm"`
|
||||
IsShared bool `json:"isShared"`
|
||||
IpRestriction string `json:"ipRestriction"`
|
||||
|
||||
@@ -106,6 +113,7 @@ type Application struct {
|
||||
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
|
||||
TokenSigningMethod string `xorm:"varchar(100)" json:"tokenSigningMethod"`
|
||||
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
|
||||
TokenAttributes []*JwtItem `xorm:"mediumtext" json:"tokenAttributes"`
|
||||
ExpireInHours int `json:"expireInHours"`
|
||||
RefreshExpireInHours int `json:"refreshExpireInHours"`
|
||||
SignupUrl string `xorm:"varchar(200)" json:"signupUrl"`
|
||||
@@ -266,6 +274,14 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Verification code",
|
||||
Visible: true,
|
||||
CustomCss: ".verification-code {}\n.verification-code-input{}",
|
||||
Placeholder: "",
|
||||
Rule: "None",
|
||||
}
|
||||
application.SigninItems = append(application.SigninItems, signinItem)
|
||||
signinItem = &SigninItem{
|
||||
Name: "Agreement",
|
||||
Visible: true,
|
||||
@@ -550,7 +566,6 @@ func GetMaskedApplication(application *Application, userId string) *Application
|
||||
application.Providers = providerItems
|
||||
|
||||
application.GrantTypes = nil
|
||||
application.Tags = nil
|
||||
application.RedirectUris = nil
|
||||
application.TokenFormat = "***"
|
||||
application.TokenFields = nil
|
||||
@@ -626,13 +641,17 @@ func GetAllowedApplications(applications []*Application, userId string, lang str
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func UpdateApplication(id string, application *Application) (bool, error) {
|
||||
func UpdateApplication(id string, application *Application, isGlobalAdmin bool) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
oldApplication, err := getApplication(owner, name)
|
||||
if oldApplication == nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !isGlobalAdmin && oldApplication.Organization != application.Organization {
|
||||
return false, fmt.Errorf("auth:Unauthorized operation")
|
||||
}
|
||||
|
||||
if name == "app-built-in" {
|
||||
application.Name = name
|
||||
}
|
||||
@@ -709,7 +728,7 @@ func AddApplication(application *Application) (bool, error) {
|
||||
}
|
||||
|
||||
func deleteApplication(application *Application) (bool, error) {
|
||||
affected, err := ormer.Engine.ID(core.PK{application.Owner, application.Name}).Delete(&Application{})
|
||||
affected, err := ormer.Engine.ID(core.PK{application.Owner, application.Name}).Where("organization = ?", application.Organization).Delete(&Application{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -478,7 +478,10 @@ func CheckLoginPermission(userId string, application *Application) (bool, error)
|
||||
continue
|
||||
}
|
||||
|
||||
if !permission.isUserHit(userId) && !permission.isRoleHit(userId) {
|
||||
userHit := permission.isUserHit(userId)
|
||||
roleHit := permission.isRoleHit(userId)
|
||||
|
||||
if !userHit && !roleHit {
|
||||
if permission.Effect == "Allow" {
|
||||
allowPermissionCount += 1
|
||||
} else {
|
||||
@@ -493,18 +496,56 @@ func CheckLoginPermission(userId string, application *Application) (bool, error)
|
||||
}
|
||||
|
||||
var isAllowed bool
|
||||
isAllowed, err = enforcer.Enforce(userId, application.Name, "Read")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if isAllowed {
|
||||
if permission.Effect == "Allow" {
|
||||
allowCount += 1
|
||||
if userHit {
|
||||
isAllowed, err = enforcer.Enforce(userId, application.Name, "Read")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
if permission.Effect == "Deny" {
|
||||
denyCount += 1
|
||||
|
||||
if isAllowed {
|
||||
if permission.Effect == "Allow" {
|
||||
allowCount += 1
|
||||
}
|
||||
} else {
|
||||
if permission.Effect == "Deny" {
|
||||
denyCount += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
if roleHit {
|
||||
targetRoles, err := getRolesByUser(userId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var checkRoleList []*Role
|
||||
|
||||
for _, role := range permission.Roles {
|
||||
if role == "*" {
|
||||
checkRoleList = targetRoles
|
||||
break
|
||||
}
|
||||
|
||||
for _, targetRole := range targetRoles {
|
||||
if role == targetRole.GetId() {
|
||||
checkRoleList = append(checkRoleList, targetRole)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, role := range checkRoleList {
|
||||
isAllowed, err = enforcer.Enforce(role.GetId(), application.Name, "Read")
|
||||
|
||||
if isAllowed {
|
||||
if permission.Effect == "Allow" {
|
||||
allowCount += 1
|
||||
}
|
||||
} else {
|
||||
if permission.Effect == "Deny" {
|
||||
denyCount += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ package object
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/casdoor/casdoor/cred"
|
||||
)
|
||||
|
||||
type ValidatorFunc func(password string) string
|
||||
@@ -96,3 +98,26 @@ func checkPasswordComplexity(password string, options []string) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// CheckPasswordNotSameAsCurrent checks if the new password is different from the current password
|
||||
func CheckPasswordNotSameAsCurrent(user *User, newPassword string, organization *Organization) bool {
|
||||
if user.Password == "" {
|
||||
// User doesn't have a password set (e.g., OAuth-only users), allow any password
|
||||
return true
|
||||
}
|
||||
|
||||
credManager := cred.GetCredManager(organization.PasswordType)
|
||||
if credManager == nil {
|
||||
// If no credential manager is available, we can't compare passwords
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if the new password is the same as the current password
|
||||
// Try with both organization salt and user salt (like CheckPassword function does)
|
||||
if credManager.IsPasswordCorrect(newPassword, user.Password, organization.PasswordSalt) ||
|
||||
credManager.IsPasswordCorrect(newPassword, user.Password, user.PasswordSalt) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestSmtpServer(provider *Provider) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func SendEmail(provider *Provider, title string, content string, dest string, sender string) error {
|
||||
func SendEmail(provider *Provider, title string, content string, dest []string, sender string) error {
|
||||
emailProvider := email.GetEmailProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, provider.Port, provider.DisableSsl, provider.Endpoint, provider.Method, provider.HttpHeaders, provider.UserMapping, provider.IssuerUrl)
|
||||
|
||||
fromAddress := provider.ClientId2
|
||||
|
||||
160
object/form.go
Normal file
160
object/form.go
Normal file
@@ -0,0 +1,160 @@
|
||||
// Copyright 2025 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 FormItem struct {
|
||||
Name string `json:"name"`
|
||||
Label string `json:"label"`
|
||||
Visible bool `json:"visible"`
|
||||
Width string `json:"width"`
|
||||
}
|
||||
|
||||
type Form 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"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||
FormItems []*FormItem `xorm:"varchar(5000)" json:"formItems"`
|
||||
}
|
||||
|
||||
func GetMaskedForm(form *Form, isMaskEnabled bool) *Form {
|
||||
if !isMaskEnabled {
|
||||
return form
|
||||
}
|
||||
|
||||
if form == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return form
|
||||
}
|
||||
|
||||
func GetMaskedForms(forms []*Form, isMaskEnabled bool) []*Form {
|
||||
if !isMaskEnabled {
|
||||
return forms
|
||||
}
|
||||
|
||||
for _, form := range forms {
|
||||
form = GetMaskedForm(form, isMaskEnabled)
|
||||
}
|
||||
return forms
|
||||
}
|
||||
|
||||
func GetGlobalForms() ([]*Form, error) {
|
||||
forms := []*Form{}
|
||||
err := ormer.Engine.Asc("owner").Desc("created_time").Find(&forms)
|
||||
if err != nil {
|
||||
return forms, err
|
||||
}
|
||||
|
||||
return forms, nil
|
||||
}
|
||||
|
||||
func GetForms(owner string) ([]*Form, error) {
|
||||
forms := []*Form{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&forms, &Form{Owner: owner})
|
||||
if err != nil {
|
||||
return forms, err
|
||||
}
|
||||
|
||||
return forms, nil
|
||||
}
|
||||
|
||||
func getForm(owner string, name string) (*Form, error) {
|
||||
form := Form{Owner: owner, Name: name}
|
||||
existed, err := ormer.Engine.Get(&form)
|
||||
if err != nil {
|
||||
return &form, err
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &form, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetForm(id string) (*Form, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
return getForm(owner, name)
|
||||
}
|
||||
|
||||
func UpdateForm(id string, form *Form) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
existingForm, err := getForm(owner, name)
|
||||
if existingForm == nil {
|
||||
return false, fmt.Errorf("the form: %s is not found", id)
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if form == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
_, err = ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(form)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func AddForm(form *Form) (bool, error) {
|
||||
affected, err := ormer.Engine.Insert(form)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func DeleteForm(form *Form) (bool, error) {
|
||||
affected, err := ormer.Engine.ID(core.PK{form.Owner, form.Name}).Delete(&Form{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func (form *Form) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", form.Owner, form.Name)
|
||||
}
|
||||
|
||||
func GetFormCount(owner string, field, value string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
return session.Count(&Form{})
|
||||
}
|
||||
|
||||
func GetPaginationForms(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Form, error) {
|
||||
forms := []*Form{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&forms)
|
||||
if err != nil {
|
||||
return forms, err
|
||||
}
|
||||
|
||||
return forms, nil
|
||||
}
|
||||
@@ -46,6 +46,8 @@ type InitData struct {
|
||||
Sessions []*Session `json:"sessions"`
|
||||
Subscriptions []*Subscription `json:"subscriptions"`
|
||||
Transactions []*Transaction `json:"transactions"`
|
||||
|
||||
EnforcerPolicies map[string][][]string `json:"enforcerPolicies"`
|
||||
}
|
||||
|
||||
var initDataNewOnly bool
|
||||
@@ -85,9 +87,6 @@ func InitFromFile() {
|
||||
for _, model := range initData.Models {
|
||||
initDefinedModel(model)
|
||||
}
|
||||
for _, permission := range initData.Permissions {
|
||||
initDefinedPermission(permission)
|
||||
}
|
||||
for _, payment := range initData.Payments {
|
||||
initDefinedPayment(payment)
|
||||
}
|
||||
@@ -116,7 +115,11 @@ func InitFromFile() {
|
||||
initDefinedAdapter(adapter)
|
||||
}
|
||||
for _, enforcer := range initData.Enforcers {
|
||||
initDefinedEnforcer(enforcer)
|
||||
policies := initData.EnforcerPolicies[enforcer.GetId()]
|
||||
initDefinedEnforcer(enforcer, policies)
|
||||
}
|
||||
for _, permission := range initData.Permissions {
|
||||
initDefinedPermission(permission)
|
||||
}
|
||||
for _, plan := range initData.Plans {
|
||||
initDefinedPlan(plan)
|
||||
@@ -175,6 +178,8 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
|
||||
Sessions: []*Session{},
|
||||
Subscriptions: []*Subscription{},
|
||||
Transactions: []*Transaction{},
|
||||
|
||||
EnforcerPolicies: map[string][][]string{},
|
||||
}
|
||||
err := util.JsonToStruct(s, data)
|
||||
if err != nil {
|
||||
@@ -694,7 +699,7 @@ func initDefinedAdapter(adapter *Adapter) {
|
||||
}
|
||||
}
|
||||
|
||||
func initDefinedEnforcer(enforcer *Enforcer) {
|
||||
func initDefinedEnforcer(enforcer *Enforcer, policies [][]string) {
|
||||
existed, err := getEnforcer(enforcer.Owner, enforcer.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -716,6 +721,27 @@ func initDefinedEnforcer(enforcer *Enforcer) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = enforcer.InitEnforcer()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, policy := range policies {
|
||||
if enforcer.HasPolicy(policy) {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = enforcer.AddPolicy(policy)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
err = enforcer.SavePolicy()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initDefinedPlan(plan *Plan) {
|
||||
|
||||
@@ -146,6 +146,16 @@ func writeInitDataToFile(filePath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
enforcerPolicies := make(map[string][][]string)
|
||||
for _, enforcer := range enforcers {
|
||||
err = enforcer.InitEnforcer()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
enforcerPolicies[enforcer.GetId()] = enforcer.GetPolicy()
|
||||
}
|
||||
|
||||
data := &InitData{
|
||||
Organizations: organizations,
|
||||
Applications: applications,
|
||||
@@ -172,6 +182,8 @@ func writeInitDataToFile(filePath string) error {
|
||||
Sessions: sessions,
|
||||
Subscriptions: subscriptions,
|
||||
Transactions: transactions,
|
||||
|
||||
EnforcerPolicies: enforcerPolicies,
|
||||
}
|
||||
|
||||
text := util.StructToJsonFormatted(data)
|
||||
|
||||
@@ -235,3 +235,8 @@ func (invitation *Invitation) IsInvitationCodeValid(application *Application, in
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (invitation *Invitation) GetInvitationLink(host string, application string) string {
|
||||
frontEnd, _ := getOriginFromHost(host)
|
||||
return fmt.Sprintf("%s/signup/%s?invitationCode=%s", frontEnd, application, invitation.Code)
|
||||
}
|
||||
|
||||
@@ -23,18 +23,19 @@ type Ldap struct {
|
||||
Owner string `xorm:"varchar(100)" json:"owner"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
|
||||
ServerName string `xorm:"varchar(100)" json:"serverName"`
|
||||
Host string `xorm:"varchar(100)" json:"host"`
|
||||
Port int `xorm:"int" json:"port"`
|
||||
EnableSsl bool `xorm:"bool" json:"enableSsl"`
|
||||
AllowSelfSignedCert bool `xorm:"bool" json:"allowSelfSignedCert"`
|
||||
Username string `xorm:"varchar(100)" json:"username"`
|
||||
Password string `xorm:"varchar(100)" json:"password"`
|
||||
BaseDn string `xorm:"varchar(500)" json:"baseDn"`
|
||||
Filter string `xorm:"varchar(200)" json:"filter"`
|
||||
FilterFields []string `xorm:"varchar(100)" json:"filterFields"`
|
||||
DefaultGroup string `xorm:"varchar(100)" json:"defaultGroup"`
|
||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||
ServerName string `xorm:"varchar(100)" json:"serverName"`
|
||||
Host string `xorm:"varchar(100)" json:"host"`
|
||||
Port int `xorm:"int" json:"port"`
|
||||
EnableSsl bool `xorm:"bool" json:"enableSsl"`
|
||||
AllowSelfSignedCert bool `xorm:"bool" json:"allowSelfSignedCert"`
|
||||
Username string `xorm:"varchar(100)" json:"username"`
|
||||
Password string `xorm:"varchar(100)" json:"password"`
|
||||
BaseDn string `xorm:"varchar(500)" json:"baseDn"`
|
||||
Filter string `xorm:"varchar(200)" json:"filter"`
|
||||
FilterFields []string `xorm:"varchar(100)" json:"filterFields"`
|
||||
DefaultGroup string `xorm:"varchar(100)" json:"defaultGroup"`
|
||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||
CustomAttributes map[string]string `json:"customAttributes"`
|
||||
|
||||
AutoSync int `json:"autoSync"`
|
||||
LastSync string `xorm:"varchar(100)" json:"lastSync"`
|
||||
@@ -151,7 +152,7 @@ func UpdateLdap(ldap *Ldap) (bool, error) {
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.ID(ldap.Id).Cols("owner", "server_name", "host",
|
||||
"port", "enable_ssl", "username", "password", "base_dn", "filter", "filter_fields", "auto_sync", "default_group", "password_type", "allow_self_signed_cert").Update(ldap)
|
||||
"port", "enable_ssl", "username", "password", "base_dn", "filter", "filter_fields", "auto_sync", "default_group", "password_type", "allow_self_signed_cert", "custom_attributes").Update(ldap)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -58,9 +58,10 @@ type LdapUser struct {
|
||||
RegisteredAddress string
|
||||
PostalAddress string
|
||||
|
||||
GroupId string `json:"groupId"`
|
||||
Address string `json:"address"`
|
||||
MemberOf string `json:"memberOf"`
|
||||
GroupId string `json:"groupId"`
|
||||
Address string `json:"address"`
|
||||
MemberOf string `json:"memberOf"`
|
||||
Attributes map[string]string `json:"attributes"`
|
||||
}
|
||||
|
||||
func (ldap *Ldap) GetLdapConn() (c *LdapConn, err error) {
|
||||
@@ -158,6 +159,10 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
|
||||
SearchAttributes = append(SearchAttributes, "uid")
|
||||
}
|
||||
|
||||
for attribute := range ldapServer.CustomAttributes {
|
||||
SearchAttributes = append(SearchAttributes, attribute)
|
||||
}
|
||||
|
||||
searchReq := goldap.NewSearchRequest(ldapServer.BaseDn, goldap.ScopeWholeSubtree, goldap.NeverDerefAliases,
|
||||
0, 0, false,
|
||||
ldapServer.Filter, SearchAttributes, nil)
|
||||
@@ -211,6 +216,13 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
|
||||
user.PostalAddress = attribute.Values[0]
|
||||
case "memberOf":
|
||||
user.MemberOf = attribute.Values[0]
|
||||
default:
|
||||
if propName, ok := ldapServer.CustomAttributes[attribute.Name]; ok {
|
||||
if user.Attributes == nil {
|
||||
user.Attributes = make(map[string]string)
|
||||
}
|
||||
user.Attributes[propName] = attribute.Values[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
ldapUsers = append(ldapUsers, user)
|
||||
@@ -269,6 +281,7 @@ func AutoAdjustLdapUser(users []LdapUser) []LdapUser {
|
||||
Email: util.ReturnAnyNotEmpty(user.Email, user.EmailAddress, user.Mail),
|
||||
Mobile: util.ReturnAnyNotEmpty(user.Mobile, user.MobileTelephoneNumber, user.TelephoneNumber),
|
||||
Address: util.ReturnAnyNotEmpty(user.Address, user.PostalAddress, user.RegisteredAddress),
|
||||
Attributes: user.Attributes,
|
||||
}
|
||||
}
|
||||
return res
|
||||
@@ -345,6 +358,7 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
|
||||
Tag: tag,
|
||||
Score: score,
|
||||
Ldap: syncUser.Uuid,
|
||||
Properties: syncUser.Attributes,
|
||||
}
|
||||
|
||||
if ldap.DefaultGroup != "" {
|
||||
|
||||
@@ -427,4 +427,9 @@ func (a *Ormer) createTable() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Form))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ type Permission struct {
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
}
|
||||
|
||||
const builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
|
||||
const builtInMaxFields = 6 // Casdoor built-in adapter, use V5 to filter permission, so has 6 max field
|
||||
|
||||
func GetPermissionCount(owner, field, value string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
|
||||
@@ -410,15 +410,23 @@ m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`
|
||||
policyDefinition := strings.Split(cfg.String("policy_definition::p"), ",")
|
||||
|
||||
fieldsNum := len(policyDefinition)
|
||||
if fieldsNum > builtInAvailableField {
|
||||
return nil, fmt.Errorf("the maximum policy_definition field number cannot exceed %d, got %d", builtInAvailableField, fieldsNum)
|
||||
if fieldsNum > builtInMaxFields {
|
||||
return nil, fmt.Errorf("the maximum policy_definition field number cannot exceed %d, got %d", builtInMaxFields, fieldsNum)
|
||||
}
|
||||
|
||||
// filled empty field with "" and V5 with "permissionId"
|
||||
for i := builtInAvailableField - fieldsNum; i > 0; i-- {
|
||||
policyDefinition = append(policyDefinition, "")
|
||||
if fieldsNum == builtInMaxFields {
|
||||
sixthField := strings.TrimSpace(policyDefinition[builtInMaxFields-1])
|
||||
if sixthField != "permissionId" {
|
||||
return nil, fmt.Errorf("when adding policies with permissions, the sixth field of policy_definition must be permissionId, got %s", policyDefinition[builtInMaxFields-1])
|
||||
}
|
||||
} else {
|
||||
needFill := builtInMaxFields - fieldsNum
|
||||
for i := 0; i < needFill-1; i++ {
|
||||
policyDefinition = append(policyDefinition, "")
|
||||
}
|
||||
policyDefinition = append(policyDefinition, "permissionId")
|
||||
}
|
||||
policyDefinition = append(policyDefinition, "permissionId")
|
||||
|
||||
m, err := model.NewModelFromString(modelText)
|
||||
if err != nil {
|
||||
|
||||
@@ -43,6 +43,8 @@ type Record struct {
|
||||
type Response struct {
|
||||
Status string `json:"status"`
|
||||
Msg string `json:"msg"`
|
||||
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
func maskPassword(recordString string) string {
|
||||
@@ -52,6 +54,10 @@ func maskPassword(recordString string) string {
|
||||
func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
|
||||
clientIp := strings.Replace(util.GetClientIpFromRequest(ctx.Request), ": ", "", -1)
|
||||
action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1)
|
||||
if strings.HasPrefix(action, "notify-payment") {
|
||||
action = "notify-payment"
|
||||
}
|
||||
|
||||
requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"})
|
||||
if len(requestUri) > 1000 {
|
||||
requestUri = requestUri[0:1000]
|
||||
@@ -74,6 +80,19 @@ func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if action != "buy-product" {
|
||||
resp.Data = nil
|
||||
}
|
||||
|
||||
dataResp := ""
|
||||
if resp.Data != nil {
|
||||
dataByte, err := json.Marshal(resp.Data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dataResp = fmt.Sprintf(", data:%s", string(dataByte))
|
||||
}
|
||||
|
||||
language := ctx.Request.Header.Get("Accept-Language")
|
||||
if len(language) > 2 {
|
||||
language = language[0:2]
|
||||
@@ -91,7 +110,7 @@ func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
|
||||
Language: languageCode,
|
||||
Object: object,
|
||||
StatusCode: 200,
|
||||
Response: fmt.Sprintf("{status:\"%s\", msg:\"%s\"}", resp.Status, resp.Msg),
|
||||
Response: fmt.Sprintf("{status:\"%s\", msg:\"%s\"%s}", resp.Status, resp.Msg, dataResp),
|
||||
IsTriggered: false,
|
||||
}
|
||||
return &record, nil
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -70,7 +71,13 @@ func NewSamlResponse(application *Application, user *User, host string, certific
|
||||
if application.UseEmailAsSamlNameId {
|
||||
nameIDValue = user.Email
|
||||
}
|
||||
subject.CreateElement("saml:NameID").SetText(nameIDValue)
|
||||
nameId := subject.CreateElement("saml:NameID")
|
||||
if application.UseEmailAsSamlNameId {
|
||||
nameId.CreateAttr("Format", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")
|
||||
} else {
|
||||
nameId.CreateAttr("Format", "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified")
|
||||
}
|
||||
nameId.SetText(nameIDValue)
|
||||
subjectConfirmation := subject.CreateElement("saml:SubjectConfirmation")
|
||||
subjectConfirmation.CreateAttr("Method", "urn:oasis:names:tc:SAML:2.0:cm:bearer")
|
||||
subjectConfirmationData := subjectConfirmation.CreateElement("saml:SubjectConfirmationData")
|
||||
@@ -108,20 +115,28 @@ func NewSamlResponse(application *Application, user *User, host string, certific
|
||||
displayName.CreateAttr("NameFormat", "urn:oasis:names:tc:SAML:2.0:attrname-format:basic")
|
||||
displayName.CreateElement("saml:AttributeValue").CreateAttr("xsi:type", "xs:string").Element().SetText(user.DisplayName)
|
||||
|
||||
err := ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range application.SamlAttributes {
|
||||
role := attributes.CreateElement("saml:Attribute")
|
||||
role.CreateAttr("Name", item.Name)
|
||||
role.CreateAttr("NameFormat", item.NameFormat)
|
||||
role.CreateElement("saml:AttributeValue").CreateAttr("xsi:type", "xs:string").Element().SetText(item.Value)
|
||||
|
||||
valueList := replaceAttributeValue(user, item.Value)
|
||||
for _, value := range valueList {
|
||||
av := role.CreateElement("saml:AttributeValue")
|
||||
av.CreateAttr("xmlns:xs", "http://www.w3.org/2001/XMLSchema")
|
||||
av.CreateAttr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
|
||||
av.CreateAttr("xsi:type", "xs:string").Element().SetText(value)
|
||||
}
|
||||
}
|
||||
|
||||
roles := attributes.CreateElement("saml:Attribute")
|
||||
roles.CreateAttr("Name", "Roles")
|
||||
roles.CreateAttr("NameFormat", "urn:oasis:names:tc:SAML:2.0:attrname-format:basic")
|
||||
err := ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, role := range user.Roles {
|
||||
roles.CreateElement("saml:AttributeValue").CreateAttr("xsi:type", "xs:string").Element().SetText(role.Name)
|
||||
@@ -355,7 +370,13 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
X509Certificate: certificate,
|
||||
}
|
||||
ctx := dsig.NewDefaultSigningContext(randomKeyStore)
|
||||
ctx.Hash = crypto.SHA1
|
||||
if application.SamlHashAlgorithm == "" || application.SamlHashAlgorithm == "SHA1" {
|
||||
ctx.Hash = crypto.SHA1
|
||||
} else if application.SamlHashAlgorithm == "SHA256" {
|
||||
ctx.Hash = crypto.SHA256
|
||||
} else if application.SamlHashAlgorithm == "SHA512" {
|
||||
ctx.Hash = crypto.SHA512
|
||||
}
|
||||
|
||||
if application.EnableSamlC14n10 {
|
||||
ctx.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList("")
|
||||
@@ -495,7 +516,14 @@ func NewSamlResponse11(application *Application, user *User, requestID string, h
|
||||
return samlResponse, nil
|
||||
}
|
||||
|
||||
func GetSamlRedirectAddress(owner string, application string, relayState string, samlRequest string, host string) string {
|
||||
func GetSamlRedirectAddress(owner string, application string, relayState string, samlRequest string, host string, username string, loginHint string) string {
|
||||
originF, _ := getOriginFromHost(host)
|
||||
return fmt.Sprintf("%s/login/saml/authorize/%s/%s?relayState=%s&samlRequest=%s", originF, owner, application, relayState, samlRequest)
|
||||
baseURL := fmt.Sprintf("%s/login/saml/authorize/%s/%s?relayState=%s&samlRequest=%s", originF, owner, application, relayState, samlRequest)
|
||||
if username != "" {
|
||||
baseURL += fmt.Sprintf("&username=%s", url.QueryEscape(username))
|
||||
}
|
||||
if loginHint != "" {
|
||||
baseURL += fmt.Sprintf("&login_hint=%s", url.QueryEscape(loginHint))
|
||||
}
|
||||
return baseURL
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
@@ -152,10 +153,19 @@ func buildSpCertificateStore(provider *Provider, samlResponse string) (certStore
|
||||
certEncodedData = provider.IdP
|
||||
}
|
||||
|
||||
certData, err := base64.StdEncoding.DecodeString(certEncodedData)
|
||||
if err != nil {
|
||||
return dsig.MemoryX509CertificateStore{}, err
|
||||
var certData []byte
|
||||
block, _ := pem.Decode([]byte(certEncodedData))
|
||||
if block != nil {
|
||||
// this was a PEM file
|
||||
// block.Bytes are DER encoded so the following code block should happily accept it
|
||||
certData = block.Bytes
|
||||
} else {
|
||||
certData, err = base64.StdEncoding.DecodeString(certEncodedData)
|
||||
if err != nil {
|
||||
return dsig.MemoryX509CertificateStore{}, err
|
||||
}
|
||||
}
|
||||
|
||||
idpCert, err := x509.ParseCertificate(certData)
|
||||
if err != nil {
|
||||
return dsig.MemoryX509CertificateStore{}, err
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -35,6 +36,7 @@ const (
|
||||
ProviderTypeTencentCloudCOS = "Tencent Cloud COS"
|
||||
ProviderTypeAzureBlob = "Azure Blob"
|
||||
ProviderTypeLocalFileSystem = "Local File System"
|
||||
ProviderTypeMinIO = "MinIO"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -211,5 +213,13 @@ func refineObjectKey(provider *Provider, objectKey string) string {
|
||||
if provider.Type == ProviderTypeGoogleCloudStorage {
|
||||
return strings.TrimPrefix(objectKey, "/")
|
||||
}
|
||||
|
||||
if provider.Type == ProviderTypeMinIO && provider.PathPrefix != "" {
|
||||
p := path.Join("/", strings.Trim(provider.PathPrefix, "/"))
|
||||
if strings.HasPrefix(objectKey, p+"/") {
|
||||
return strings.TrimPrefix(objectKey, p+"/")
|
||||
}
|
||||
}
|
||||
|
||||
return objectKey
|
||||
}
|
||||
|
||||
@@ -153,13 +153,15 @@ func GetMaskedSyncers(syncers []*Syncer, errs ...error) ([]*Syncer, error) {
|
||||
return syncers, nil
|
||||
}
|
||||
|
||||
func UpdateSyncer(id string, syncer *Syncer) (bool, error) {
|
||||
func UpdateSyncer(id string, syncer *Syncer, isGlobalAdmin bool) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
s, err := getSyncer(owner, name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if s == nil {
|
||||
return false, nil
|
||||
} else if !isGlobalAdmin && s.Organization != syncer.Organization {
|
||||
return false, fmt.Errorf("auth:Unauthorized operation")
|
||||
}
|
||||
|
||||
session := ormer.Engine.ID(core.PK{owner, name}).AllCols()
|
||||
@@ -218,7 +220,7 @@ func AddSyncer(syncer *Syncer) (bool, error) {
|
||||
}
|
||||
|
||||
func DeleteSyncer(syncer *Syncer) (bool, error) {
|
||||
affected, err := ormer.Engine.ID(core.PK{syncer.Owner, syncer.Name}).Delete(&Syncer{})
|
||||
affected, err := ormer.Engine.ID(core.PK{syncer.Owner, syncer.Name}).Where("organization = ?", syncer.Organization).Delete(&Syncer{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -273,11 +275,16 @@ func (syncer *Syncer) getKeyColumn() *TableColumn {
|
||||
return column
|
||||
}
|
||||
|
||||
func (syncer *Syncer) getKey() string {
|
||||
func (syncer *Syncer) getLocalPrimaryKey() string {
|
||||
column := syncer.getKeyColumn()
|
||||
return util.CamelToSnakeCase(column.CasdoorName)
|
||||
}
|
||||
|
||||
func (syncer *Syncer) getTargetTablePrimaryKey() string {
|
||||
column := syncer.getKeyColumn()
|
||||
return column.Name
|
||||
}
|
||||
|
||||
func RunSyncer(syncer *Syncer) error {
|
||||
err := syncer.initAdapter()
|
||||
if err != nil {
|
||||
|
||||
@@ -65,7 +65,7 @@ func (syncer *Syncer) syncUsers() error {
|
||||
}
|
||||
}
|
||||
|
||||
key := syncer.getKey()
|
||||
key := syncer.getLocalPrimaryKey()
|
||||
|
||||
myUsers := map[string]*User{}
|
||||
for _, m := range users {
|
||||
|
||||
@@ -75,7 +75,7 @@ func (syncer *Syncer) getCasdoorColumns() []string {
|
||||
}
|
||||
|
||||
func (syncer *Syncer) updateUser(user *OriginalUser) (bool, error) {
|
||||
key := syncer.getKey()
|
||||
key := syncer.getTargetTablePrimaryKey()
|
||||
m := syncer.getMapFromOriginalUser(user)
|
||||
pkValue := m[key]
|
||||
delete(m, key)
|
||||
|
||||
@@ -180,12 +180,14 @@ func (token *Token) popularHashes() {
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateToken(id string, token *Token) (bool, error) {
|
||||
func UpdateToken(id string, token *Token, isGlobalAdmin bool) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if t, err := getToken(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if t == nil {
|
||||
return false, nil
|
||||
} else if !isGlobalAdmin && t.Organization != token.Organization {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
token.popularHashes()
|
||||
@@ -210,7 +212,7 @@ func AddToken(token *Token) (bool, error) {
|
||||
}
|
||||
|
||||
func DeleteToken(token *Token) (bool, error) {
|
||||
affected, err := ormer.Engine.ID(core.PK{token.Owner, token.Name}).Delete(&Token{})
|
||||
affected, err := ormer.Engine.ID(core.PK{token.Owner, token.Name}).Where("organization = ?", token.Organization).Delete(&Token{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -67,6 +67,14 @@ type CasAttributes struct {
|
||||
LongTermAuthenticationRequestTokenUsed bool `xml:"cas:longTermAuthenticationRequestTokenUsed"`
|
||||
IsFromNewLogin bool `xml:"cas:isFromNewLogin"`
|
||||
MemberOf []string `xml:"cas:memberOf"`
|
||||
FirstName string `xml:"cas:firstName,omitempty"`
|
||||
LastName string `xml:"cas:lastName,omitempty"`
|
||||
Title string `xml:"cas:title,omitempty"`
|
||||
Email string `xml:"cas:email,omitempty"`
|
||||
Affiliation string `xml:"cas:affiliation,omitempty"`
|
||||
Avatar string `xml:"cas:avatar,omitempty"`
|
||||
Phone string `xml:"cas:phone,omitempty"`
|
||||
DisplayName string `xml:"cas:displayName,omitempty"`
|
||||
UserAttributes *CasUserAttributes
|
||||
ExtraAttributes []*CasAnyAttribute `xml:",any"`
|
||||
}
|
||||
@@ -240,6 +248,24 @@ func GenerateCasToken(userId string, service string) (string, error) {
|
||||
} else {
|
||||
value = escapedValue
|
||||
}
|
||||
switch k {
|
||||
case "firstName":
|
||||
authenticationSuccess.Attributes.FirstName = value
|
||||
case "lastName":
|
||||
authenticationSuccess.Attributes.LastName = value
|
||||
case "title":
|
||||
authenticationSuccess.Attributes.Title = value
|
||||
case "email":
|
||||
authenticationSuccess.Attributes.Email = value
|
||||
case "affiliation":
|
||||
authenticationSuccess.Attributes.Affiliation = value
|
||||
case "avatar":
|
||||
authenticationSuccess.Attributes.Avatar = value
|
||||
case "phone":
|
||||
authenticationSuccess.Attributes.Phone = value
|
||||
case "displayName":
|
||||
authenticationSuccess.Attributes.DisplayName = value
|
||||
}
|
||||
authenticationSuccess.Attributes.UserAttributes.Attributes = append(authenticationSuccess.Attributes.UserAttributes.Attributes, &CasNamedAttribute{
|
||||
Name: k,
|
||||
Value: value,
|
||||
|
||||
@@ -34,6 +34,8 @@ type Claims struct {
|
||||
// the `azp` (Authorized Party) claim. Optional. See https://openid.net/specs/openid-connect-core-1_0.html#IDToken
|
||||
Azp string `json:"azp,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
|
||||
SigninMethod string `json:"signinMethod,omitempty"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
@@ -154,6 +156,8 @@ type ClaimsShort struct {
|
||||
Scope string `json:"scope,omitempty"`
|
||||
Azp string `json:"azp,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
|
||||
SigninMethod string `json:"signinMethod,omitempty"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
@@ -174,6 +178,8 @@ type ClaimsWithoutThirdIdp struct {
|
||||
Scope string `json:"scope,omitempty"`
|
||||
Azp string `json:"azp,omitempty"`
|
||||
Provider string `json:"provider,omitempty"`
|
||||
|
||||
SigninMethod string `json:"signinMethod,omitempty"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
@@ -303,6 +309,7 @@ func getShortClaims(claims Claims) ClaimsShort {
|
||||
Scope: claims.Scope,
|
||||
RegisteredClaims: claims.RegisteredClaims,
|
||||
Azp: claims.Azp,
|
||||
SigninMethod: claims.SigninMethod,
|
||||
Provider: claims.Provider,
|
||||
}
|
||||
return res
|
||||
@@ -317,12 +324,13 @@ func getClaimsWithoutThirdIdp(claims Claims) ClaimsWithoutThirdIdp {
|
||||
Scope: claims.Scope,
|
||||
RegisteredClaims: claims.RegisteredClaims,
|
||||
Azp: claims.Azp,
|
||||
SigninMethod: claims.SigninMethod,
|
||||
Provider: claims.Provider,
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func getClaimsCustom(claims Claims, tokenField []string) jwt.MapClaims {
|
||||
func getClaimsCustom(claims Claims, tokenField []string, tokenAttributes []*JwtItem) jwt.MapClaims {
|
||||
res := make(jwt.MapClaims)
|
||||
|
||||
userValue := reflect.ValueOf(claims.User).Elem()
|
||||
@@ -339,10 +347,11 @@ func getClaimsCustom(claims Claims, tokenField []string) jwt.MapClaims {
|
||||
res["tag"] = claims.Tag
|
||||
res["scope"] = claims.Scope
|
||||
res["azp"] = claims.Azp
|
||||
res["signinMethod"] = claims.SigninMethod
|
||||
res["provider"] = claims.Provider
|
||||
|
||||
for _, field := range tokenField {
|
||||
if strings.HasPrefix(field, "Properties") {
|
||||
if strings.HasPrefix(field, "Properties.") {
|
||||
/*
|
||||
Use selected properties fields as custom claims.
|
||||
Converts `Properties.my_field` to custom claim with name `my_field`.
|
||||
@@ -361,6 +370,12 @@ func getClaimsCustom(claims Claims, tokenField []string) jwt.MapClaims {
|
||||
res[fieldName] = finalField.Interface()
|
||||
}
|
||||
|
||||
} else if field == "permissionNames" {
|
||||
permissionNames := []string{}
|
||||
for _, val := range claims.User.Permissions {
|
||||
permissionNames = append(permissionNames, val.Name)
|
||||
}
|
||||
res[util.SnakeToCamel(util.CamelToSnakeCase(field))] = permissionNames
|
||||
} else { // Use selected user field as claims.
|
||||
userField := userValue.FieldByName(field)
|
||||
if userField.IsValid() {
|
||||
@@ -370,6 +385,16 @@ func getClaimsCustom(claims Claims, tokenField []string) jwt.MapClaims {
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range tokenAttributes {
|
||||
valueList := replaceAttributeValue(claims.User, item.Value)
|
||||
|
||||
if len(valueList) == 1 {
|
||||
res[item.Name] = valueList[0]
|
||||
} else {
|
||||
res[item.Name] = valueList
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -395,7 +420,7 @@ func refineUser(user *User) *User {
|
||||
return user
|
||||
}
|
||||
|
||||
func generateJwtToken(application *Application, user *User, provider string, nonce string, scope string, host string) (string, string, string, error) {
|
||||
func generateJwtToken(application *Application, user *User, provider string, signinMethod string, nonce string, scope string, host string) (string, string, string, error) {
|
||||
nowTime := time.Now()
|
||||
expireTime := nowTime.Add(time.Duration(application.ExpireInHours) * time.Hour)
|
||||
refreshExpireTime := nowTime.Add(time.Duration(application.RefreshExpireInHours) * time.Hour)
|
||||
@@ -423,10 +448,11 @@ func generateJwtToken(application *Application, user *User, provider string, non
|
||||
TokenType: "access-token",
|
||||
Nonce: nonce,
|
||||
// FIXME: A workaround for custom claim by reusing `tag` in user info
|
||||
Tag: user.Tag,
|
||||
Scope: scope,
|
||||
Azp: application.ClientId,
|
||||
Provider: provider,
|
||||
Tag: user.Tag,
|
||||
Scope: scope,
|
||||
Azp: application.ClientId,
|
||||
Provider: provider,
|
||||
SigninMethod: signinMethod,
|
||||
RegisteredClaims: jwt.RegisteredClaims{
|
||||
Issuer: originBackend,
|
||||
Subject: user.Id,
|
||||
@@ -481,10 +507,10 @@ func generateJwtToken(application *Application, user *User, provider string, non
|
||||
claimsShort.TokenType = "refresh-token"
|
||||
refreshToken = jwt.NewWithClaims(jwtMethod, claimsShort)
|
||||
} else if application.TokenFormat == "JWT-Custom" {
|
||||
claimsCustom := getClaimsCustom(claims, application.TokenFields)
|
||||
claimsCustom := getClaimsCustom(claims, application.TokenFields, application.TokenAttributes)
|
||||
|
||||
token = jwt.NewWithClaims(jwtMethod, claimsCustom)
|
||||
refreshClaims := getClaimsCustom(claims, application.TokenFields)
|
||||
refreshClaims := getClaimsCustom(claims, application.TokenFields, application.TokenAttributes)
|
||||
refreshClaims["exp"] = jwt.NewNumericDate(refreshExpireTime)
|
||||
refreshClaims["TokenType"] = "refresh-token"
|
||||
refreshToken = jwt.NewWithClaims(jwtMethod, refreshClaims)
|
||||
|
||||
@@ -136,7 +136,7 @@ func CheckOAuthLogin(clientId string, responseType string, redirectUri string, s
|
||||
return "", application, nil
|
||||
}
|
||||
|
||||
func GetOAuthCode(userId string, clientId string, provider string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, host string, lang string) (*Code, error) {
|
||||
func GetOAuthCode(userId string, clientId string, provider string, signinMethod string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, host string, lang string) (*Code, error) {
|
||||
user, err := GetUser(userId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -171,7 +171,7 @@ func GetOAuthCode(userId string, clientId string, provider string, responseType
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, provider, nonce, scope, host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, provider, signinMethod, nonce, scope, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -379,7 +379,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newAccessToken, newRefreshToken, tokenName, err := generateJwtToken(application, user, "", "", scope, host)
|
||||
newAccessToken, newRefreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", scope, host)
|
||||
if err != nil {
|
||||
return &TokenError{
|
||||
Error: EndpointError,
|
||||
@@ -537,6 +537,13 @@ func GetPasswordToken(application *Application, username string, password string
|
||||
if user.Ldap != "" {
|
||||
err = CheckLdapUserPassword(user, password, "en")
|
||||
} else {
|
||||
// For OAuth users who don't have a password set, they cannot use password grant type
|
||||
if user.Password == "" {
|
||||
return nil, &TokenError{
|
||||
Error: InvalidGrant,
|
||||
ErrorDescription: "OAuth users cannot use password grant type, please use authorization code flow",
|
||||
}, nil
|
||||
}
|
||||
err = CheckPassword(user, password, "en")
|
||||
}
|
||||
if err != nil {
|
||||
@@ -558,7 +565,7 @@ func GetPasswordToken(application *Application, username string, password string
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", scope, host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", scope, host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
@@ -604,7 +611,7 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
|
||||
Type: "application",
|
||||
}
|
||||
|
||||
accessToken, _, tokenName, err := generateJwtToken(application, nullUser, "", "", scope, host)
|
||||
accessToken, _, tokenName, err := generateJwtToken(application, nullUser, "", "", "", scope, host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
@@ -668,7 +675,7 @@ func GetTokenByUser(application *Application, user *User, scope string, nonce st
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", nonce, scope, host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", nonce, scope, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -775,7 +782,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", host)
|
||||
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", "", "", host)
|
||||
if err != nil {
|
||||
return nil, &TokenError{
|
||||
Error: EndpointError,
|
||||
|
||||
@@ -67,7 +67,7 @@ type User struct {
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
FirstName string `xorm:"varchar(100)" json:"firstName"`
|
||||
LastName string `xorm:"varchar(100)" json:"lastName"`
|
||||
Avatar string `xorm:"varchar(500)" json:"avatar"`
|
||||
Avatar string `xorm:"text" json:"avatar"`
|
||||
AvatarType string `xorm:"varchar(100)" json:"avatarType"`
|
||||
PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"`
|
||||
Email string `xorm:"varchar(100) index" json:"email"`
|
||||
@@ -510,6 +510,8 @@ func GetUserByPhone(owner string, phone string) (*User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
phone = util.GetSeperatedPhone(phone)
|
||||
|
||||
user := User{Owner: owner, Phone: phone}
|
||||
existed, err := ormer.Engine.Get(&user)
|
||||
if err != nil {
|
||||
@@ -528,6 +530,8 @@ func GetUserByPhoneOnly(phone string) (*User, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
phone = util.GetSeperatedPhone(phone)
|
||||
|
||||
user := User{Phone: phone}
|
||||
existed, err := ormer.Engine.Get(&user)
|
||||
if err != nil {
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/casdoor/casdoor/xlsx"
|
||||
)
|
||||
@@ -73,7 +75,7 @@ func parseListItem(lines *[]string, i int) []string {
|
||||
return trimmedItems
|
||||
}
|
||||
|
||||
func UploadUsers(owner string, path string) (bool, error) {
|
||||
func UploadUsers(owner string, path string, userObj *User, lang string) (bool, error) {
|
||||
table := xlsx.ReadXlsxFile(path)
|
||||
|
||||
oldUserMap, err := getUserMap(owner)
|
||||
@@ -81,13 +83,58 @@ func UploadUsers(owner string, path string) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
transUsers, err := StringArrayToStruct[User](table)
|
||||
uploadedUsers, err := StringArrayToStruct[User](table)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(uploadedUsers) == 0 {
|
||||
return false, fmt.Errorf("no users are provided")
|
||||
}
|
||||
|
||||
organizationName := uploadedUsers[0].Owner
|
||||
if organizationName == "" || !userObj.IsGlobalAdmin() {
|
||||
organizationName = owner
|
||||
}
|
||||
|
||||
organization, err := getOrganization("admin", organizationName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if organization == nil {
|
||||
return false, fmt.Errorf(i18n.Translate(lang, "auth:The organization: %s does not exist"), organizationName)
|
||||
}
|
||||
|
||||
newUsers := []*User{}
|
||||
for _, user := range transUsers {
|
||||
for _, user := range uploadedUsers {
|
||||
if _, ok := oldUserMap[user.GetId()]; !ok {
|
||||
user.Owner = organizationName
|
||||
if user.CreatedTime == "" {
|
||||
user.CreatedTime = util.GetCurrentTime()
|
||||
}
|
||||
if user.Id == "" {
|
||||
user.Id = util.GenerateId()
|
||||
}
|
||||
if user.Type == "" {
|
||||
user.Type = "normal-user"
|
||||
}
|
||||
user.PasswordType = "plain"
|
||||
if user.DisplayName == "" {
|
||||
user.DisplayName = user.Name
|
||||
}
|
||||
user.Avatar = organization.DefaultAvatar
|
||||
if user.Region == "" {
|
||||
user.Region = userObj.Region
|
||||
}
|
||||
if user.Address == nil {
|
||||
user.Address = []string{}
|
||||
}
|
||||
if user.CountryCode == "" {
|
||||
user.CountryCode = userObj.CountryCode
|
||||
}
|
||||
if user.SignupApplication == "" {
|
||||
user.SignupApplication = organization.DefaultApplication
|
||||
}
|
||||
|
||||
newUsers = append(newUsers, user)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,8 @@ func GetUserByFields(organization string, field string) (*User, error) {
|
||||
}
|
||||
|
||||
// check phone
|
||||
user, err = GetUserByField(organization, "phone", field)
|
||||
phone := util.GetSeperatedPhone(field)
|
||||
user, err = GetUserByField(organization, "phone", phone)
|
||||
if user != nil || err != nil {
|
||||
return user, err
|
||||
}
|
||||
@@ -247,6 +248,20 @@ func SetUserOAuthProperties(organization *Organization, user *User, providerType
|
||||
return UpdateUserForAllFields(user.GetId(), user)
|
||||
}
|
||||
|
||||
func getUserRoleNames(user *User) (res []string) {
|
||||
for _, role := range user.Roles {
|
||||
res = append(res, role.Name)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func getUserPermissionNames(user *User) (res []string) {
|
||||
for _, permission := range user.Permissions {
|
||||
res = append(res, permission.Name)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func ClearUserOAuthProperties(user *User, providerType string) (bool, error) {
|
||||
for k := range user.Properties {
|
||||
prefix := fmt.Sprintf("oauth_%s_", providerType)
|
||||
@@ -812,3 +827,49 @@ func StringArrayToStruct[T any](stringArray [][]string) ([]*T, error) {
|
||||
|
||||
return instances, nil
|
||||
}
|
||||
|
||||
func replaceAttributeValue(user *User, value string) []string {
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
valueList := []string{value}
|
||||
if strings.Contains(value, "$user.roles") {
|
||||
valueList = replaceAttributeValuesWithList("$user.roles", getUserRoleNames(user), valueList)
|
||||
}
|
||||
|
||||
if strings.Contains(value, "$user.permissions") {
|
||||
valueList = replaceAttributeValuesWithList("$user.permissions", getUserPermissionNames(user), valueList)
|
||||
}
|
||||
|
||||
if strings.Contains(value, "$user.groups") {
|
||||
valueList = replaceAttributeValuesWithList("$user.groups", user.Groups, valueList)
|
||||
}
|
||||
|
||||
valueList = replaceAttributeValues("$user.owner", user.Owner, valueList)
|
||||
valueList = replaceAttributeValues("$user.name", user.Name, valueList)
|
||||
valueList = replaceAttributeValues("$user.email", user.Email, valueList)
|
||||
valueList = replaceAttributeValues("$user.id", user.Id, valueList)
|
||||
valueList = replaceAttributeValues("$user.phone", user.Phone, valueList)
|
||||
|
||||
return valueList
|
||||
}
|
||||
|
||||
func replaceAttributeValues(val string, replaceVal string, values []string) []string {
|
||||
var newValues []string
|
||||
for _, value := range values {
|
||||
newValues = append(newValues, strings.ReplaceAll(value, val, replaceVal))
|
||||
}
|
||||
|
||||
return newValues
|
||||
}
|
||||
|
||||
func replaceAttributeValuesWithList(val string, replaceVals []string, values []string) []string {
|
||||
var newValues []string
|
||||
for _, value := range values {
|
||||
for _, rVal := range replaceVals {
|
||||
newValues = append(newValues, strings.ReplaceAll(value, val, rVal))
|
||||
}
|
||||
}
|
||||
|
||||
return newValues
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
|
||||
return err
|
||||
}
|
||||
|
||||
err = SendEmail(provider, title, content, dest, sender)
|
||||
err = SendEmail(provider, title, content, []string{dest}, sender)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -104,12 +104,14 @@ func GetWebhook(id string) (*Webhook, error) {
|
||||
return getWebhook(owner, name)
|
||||
}
|
||||
|
||||
func UpdateWebhook(id string, webhook *Webhook) (bool, error) {
|
||||
func UpdateWebhook(id string, webhook *Webhook, isGlobalAdmin bool) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if w, err := getWebhook(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if w == nil {
|
||||
return false, nil
|
||||
} else if !isGlobalAdmin && w.Organization != webhook.Organization {
|
||||
return false, fmt.Errorf("auth:Unauthorized operation")
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(webhook)
|
||||
@@ -130,7 +132,7 @@ func AddWebhook(webhook *Webhook) (bool, error) {
|
||||
}
|
||||
|
||||
func DeleteWebhook(webhook *Webhook) (bool, error) {
|
||||
affected, err := ormer.Engine.ID(core.PK{webhook.Owner, webhook.Name}).Delete(&Webhook{})
|
||||
affected, err := ormer.Engine.ID(core.PK{webhook.Owner, webhook.Name}).Where("organization = ?", webhook.Organization).Delete(&Webhook{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -34,6 +34,11 @@ type Object struct {
|
||||
AccessSecret string `json:"accessSecret"`
|
||||
}
|
||||
|
||||
type ObjectWithOrg struct {
|
||||
Object
|
||||
Organization string `json:"organization"`
|
||||
}
|
||||
|
||||
func getUsername(ctx *context.Context) (username string) {
|
||||
username, ok := ctx.Input.Session("username").(string)
|
||||
if !ok || username == "" {
|
||||
@@ -91,7 +96,7 @@ func getObject(ctx *context.Context) (string, string, error) {
|
||||
|
||||
return "", "", nil
|
||||
} else {
|
||||
if path == "/api/add-policy" || path == "/api/remove-policy" || path == "/api/update-policy" {
|
||||
if path == "/api/add-policy" || path == "/api/remove-policy" || path == "/api/update-policy" || path == "/api/send-invitation" {
|
||||
id := ctx.Input.Query("id")
|
||||
if id != "" {
|
||||
return util.GetOwnerAndNameFromIdWithError(id)
|
||||
@@ -104,12 +109,27 @@ func getObject(ctx *context.Context) (string, string, error) {
|
||||
}
|
||||
|
||||
var obj Object
|
||||
|
||||
if strings.HasSuffix(path, "-application") || strings.HasSuffix(path, "-token") ||
|
||||
strings.HasSuffix(path, "-syncer") || strings.HasSuffix(path, "-webhook") {
|
||||
var objWithOrg ObjectWithOrg
|
||||
err := json.Unmarshal(body, &objWithOrg)
|
||||
if err != nil {
|
||||
return "", "", nil
|
||||
}
|
||||
return objWithOrg.Organization, objWithOrg.Name, nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(body, &obj)
|
||||
if err != nil {
|
||||
// this is not error
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
if strings.HasSuffix(path, "-organization") {
|
||||
return obj.Name, obj.Name, nil
|
||||
}
|
||||
|
||||
if path == "/api/delete-resource" {
|
||||
tokens := strings.Split(obj.Name, "/")
|
||||
if len(tokens) >= 5 {
|
||||
|
||||
@@ -102,6 +102,7 @@ func initAPI() {
|
||||
beego.Router("/api/add-invitation", &controllers.ApiController{}, "POST:AddInvitation")
|
||||
beego.Router("/api/delete-invitation", &controllers.ApiController{}, "POST:DeleteInvitation")
|
||||
beego.Router("/api/verify-invitation", &controllers.ApiController{}, "GET:VerifyInvitation")
|
||||
beego.Router("/api/send-invitation", &controllers.ApiController{}, "POST:SendInvitation")
|
||||
|
||||
beego.Router("/api/get-applications", &controllers.ApiController{}, "GET:GetApplications")
|
||||
beego.Router("/api/get-application", &controllers.ApiController{}, "GET:GetApplication")
|
||||
@@ -239,6 +240,13 @@ func initAPI() {
|
||||
beego.Router("/api/get-prometheus-info", &controllers.ApiController{}, "GET:GetPrometheusInfo")
|
||||
beego.Handler("/api/metrics", promhttp.Handler())
|
||||
|
||||
beego.Router("/api/get-global-forms", &controllers.ApiController{}, "GET:GetGlobalForms")
|
||||
beego.Router("/api/get-forms", &controllers.ApiController{}, "GET:GetForms")
|
||||
beego.Router("/api/get-form", &controllers.ApiController{}, "GET:GetForm")
|
||||
beego.Router("/api/update-form", &controllers.ApiController{}, "POST:UpdateForm")
|
||||
beego.Router("/api/add-form", &controllers.ApiController{}, "POST:AddForm")
|
||||
beego.Router("/api/delete-form", &controllers.ApiController{}, "POST:DeleteForm")
|
||||
|
||||
beego.Router("/api/get-syncers", &controllers.ApiController{}, "GET:GetSyncers")
|
||||
beego.Router("/api/get-syncer", &controllers.ApiController{}, "GET:GetSyncer")
|
||||
beego.Router("/api/update-syncer", &controllers.ApiController{}, "POST:UpdateSyncer")
|
||||
|
||||
@@ -89,7 +89,7 @@ func fastAutoSignin(ctx *context.Context) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
code, err := object.GetOAuthCode(userId, clientId, "", responseType, redirectUri, scope, state, nonce, codeChallenge, ctx.Request.Host, getAcceptLanguage(ctx))
|
||||
code, err := object.GetOAuthCode(userId, clientId, "", "autoSignin", responseType, redirectUri, scope, state, nonce, codeChallenge, ctx.Request.Host, getAcceptLanguage(ctx))
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else if code.Message != "" {
|
||||
|
||||
@@ -638,7 +638,7 @@
|
||||
{
|
||||
"in": "query",
|
||||
"name": "id",
|
||||
"description": "The id(organization/application/user) of session",
|
||||
"description": "The id(organization/user/application) of session",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1448,7 +1448,7 @@
|
||||
{
|
||||
"in": "query",
|
||||
"name": "id",
|
||||
"description": "The id(organization/application/user) of session",
|
||||
"description": "The id(organization/user/application) of session",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
@@ -3318,8 +3318,8 @@
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "id",
|
||||
"description": "The id(organization/application/user) of session",
|
||||
"name": "sessionPkId",
|
||||
"description": "The id(organization/user/application) of session",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
@@ -4034,8 +4034,8 @@
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "id",
|
||||
"description": "The id(organization/application/user) of session",
|
||||
"name": "sessionPkId",
|
||||
"description": "The id(organization/user/application) of session",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
@@ -5457,7 +5457,7 @@
|
||||
{
|
||||
"in": "query",
|
||||
"name": "id",
|
||||
"description": "The id(organization/application/user) of session",
|
||||
"description": "The id(organization/user/application) of session",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
@@ -413,7 +413,7 @@ paths:
|
||||
parameters:
|
||||
- in: query
|
||||
name: id
|
||||
description: The id(organization/application/user) of session
|
||||
description: The id(organization/user/application) of session
|
||||
required: true
|
||||
type: string
|
||||
- in: query
|
||||
@@ -935,7 +935,7 @@ paths:
|
||||
parameters:
|
||||
- in: query
|
||||
name: id
|
||||
description: The id(organization/application/user) of session
|
||||
description: The id(organization/user/application) of session
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
@@ -2159,8 +2159,8 @@ paths:
|
||||
operationId: ApiController.GetSingleSession
|
||||
parameters:
|
||||
- in: query
|
||||
name: id
|
||||
description: The id(organization/application/user) of session
|
||||
name: sessionPkId
|
||||
description: The id(organization/user/application) of session
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
@@ -2629,8 +2629,8 @@ paths:
|
||||
operationId: ApiController.IsSessionDuplicated
|
||||
parameters:
|
||||
- in: query
|
||||
name: id
|
||||
description: The id(organization/application/user) of session
|
||||
name: sessionPkId
|
||||
description: The id(organization/user/application) of session
|
||||
required: true
|
||||
type: string
|
||||
- in: query
|
||||
@@ -3567,7 +3567,7 @@ paths:
|
||||
parameters:
|
||||
- in: query
|
||||
name: id
|
||||
description: The id(organization/application/user) of session
|
||||
description: The id(organization/user/application) of session
|
||||
required: true
|
||||
type: string
|
||||
responses:
|
||||
|
||||
19
util/log.go
19
util/log.go
@@ -16,6 +16,7 @@ package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -28,20 +29,12 @@ func getIpInfo(clientIp string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
ips := strings.Split(clientIp, ",")
|
||||
res := strings.TrimSpace(ips[0])
|
||||
//res := ""
|
||||
//for i := range ips {
|
||||
// ip := strings.TrimSpace(ips[i])
|
||||
// ipstr := fmt.Sprintf("%s: %s", ip, "")
|
||||
// if i != len(ips)-1 {
|
||||
// res += ipstr + " -> "
|
||||
// } else {
|
||||
// res += ipstr
|
||||
// }
|
||||
//}
|
||||
first := strings.TrimSpace(strings.Split(clientIp, ",")[0])
|
||||
if host, _, err := net.SplitHostPort(first); err == nil {
|
||||
return strings.Trim(host, "[]")
|
||||
}
|
||||
|
||||
return res
|
||||
return strings.Trim(first, "[]")
|
||||
}
|
||||
|
||||
func GetClientIpFromRequest(req *http.Request) string {
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"unicode"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/nyaruka/phonenumbers"
|
||||
)
|
||||
|
||||
func ParseInt(s string) int {
|
||||
@@ -278,6 +279,19 @@ func GetMaskedPhone(phone string) string {
|
||||
return rePhone.ReplaceAllString(phone, "$1****$2")
|
||||
}
|
||||
|
||||
func GetSeperatedPhone(phone string) string {
|
||||
if strings.HasPrefix(phone, "+") {
|
||||
phoneNumberParsed, err := phonenumbers.Parse(phone, "")
|
||||
if err != nil {
|
||||
return phone
|
||||
}
|
||||
|
||||
phone = fmt.Sprintf("%d", phoneNumberParsed.GetNationalNumber())
|
||||
}
|
||||
|
||||
return phone
|
||||
}
|
||||
|
||||
func GetMaskedEmail(email string) string {
|
||||
if email == "" {
|
||||
return ""
|
||||
|
||||
@@ -20,26 +20,34 @@ module.exports = {
|
||||
target: "http://localhost:8000",
|
||||
changeOrigin: true,
|
||||
},
|
||||
"/cas/serviceValidate": {
|
||||
"/cas/**/serviceValidate": {
|
||||
target: "http://localhost:8000",
|
||||
changeOrigin: true,
|
||||
},
|
||||
"/cas/proxyValidate": {
|
||||
"/cas/**/proxyValidate": {
|
||||
target: "http://localhost:8000",
|
||||
changeOrigin: true,
|
||||
},
|
||||
"/cas/proxy": {
|
||||
"/cas/**/proxy": {
|
||||
target: "http://localhost:8000",
|
||||
changeOrigin: true,
|
||||
},
|
||||
"/cas/validate": {
|
||||
"/cas/**/validate": {
|
||||
target: "http://localhost:8000",
|
||||
changeOrigin: true,
|
||||
},
|
||||
"/cas/**/p3/serviceValidate": {
|
||||
target: "http://localhost:8000",
|
||||
changeOrigin: true,
|
||||
},
|
||||
"/cas/**/p3/proxyValidate": {
|
||||
target: "http://localhost:8000",
|
||||
changeOrigin: true,
|
||||
},
|
||||
"/scim": {
|
||||
target: "http://localhost:8000",
|
||||
changeOrigin: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"@web3-onboard/gnosis": "^2.1.10",
|
||||
"@web3-onboard/infinity-wallet": "^2.0.4",
|
||||
"@web3-onboard/injected-wallets": "^2.10.4",
|
||||
"@web3-onboard/phantom": "^2.1.1",
|
||||
"@web3-onboard/react": "^2.8.10",
|
||||
"@web3-onboard/sequence": "^2.0.8",
|
||||
"@web3-onboard/taho": "^2.0.5",
|
||||
|
||||
@@ -115,7 +115,7 @@ class App extends Component {
|
||||
this.setState({selectedMenuKey: "/logs"});
|
||||
} else if (uri.includes("/products") || uri.includes("/payments") || uri.includes("/plans") || uri.includes("/pricings") || uri.includes("/subscriptions")) {
|
||||
this.setState({selectedMenuKey: "/business"});
|
||||
} else if (uri.includes("/sysinfo") || uri.includes("/syncers") || uri.includes("/webhooks")) {
|
||||
} else if (uri.includes("/sysinfo") || uri.includes("/forms") || uri.includes("/syncers") || uri.includes("/webhooks")) {
|
||||
this.setState({selectedMenuKey: "/admin"});
|
||||
} else if (uri.includes("/signup")) {
|
||||
this.setState({selectedMenuKey: "/signup"});
|
||||
|
||||
@@ -37,6 +37,7 @@ import ThemeEditor from "./common/theme/ThemeEditor";
|
||||
import SigninTable from "./table/SigninTable";
|
||||
import Editor from "./common/Editor";
|
||||
import * as GroupBackend from "./backend/GroupBackend";
|
||||
import TokenAttributeTable from "./table/TokenAttributeTable";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
@@ -116,6 +117,7 @@ class ApplicationEditPage extends React.Component {
|
||||
providers: [],
|
||||
uploading: false,
|
||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||
tokenAttributes: [],
|
||||
samlAttributes: [],
|
||||
samlMetadata: null,
|
||||
isAuthorized: true,
|
||||
@@ -463,11 +465,36 @@ class ApplicationEditPage extends React.Component {
|
||||
<Select virtual={false} disabled={this.state.application.tokenFormat !== "JWT-Custom"} mode="tags" showSearch style={{width: "100%"}} value={this.state.application.tokenFields} onChange={(value => {this.updateApplicationField("tokenFields", value);})}>
|
||||
<Option key={"provider"} value={"provider"}>{"Provider"}</Option>)
|
||||
{
|
||||
Setting.getUserCommonFields().map((item, index) => <Option key={index} value={item}>{item}</Option>)
|
||||
[...Setting.getUserCommonFields(), "permissionNames"].map((item, index) => <Option key={index} value={item}>{item}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
{
|
||||
this.state.application.tokenFormat === "JWT-Custom" ? (<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Token attributes"), i18next.t("general:Token attributes - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<TokenAttributeTable
|
||||
title={i18next.t("general:Token attributes")}
|
||||
table={this.state.application.tokenAttributes}
|
||||
application={this.state.application}
|
||||
onUpdateTable={(value) => {this.updateApplicationField("tokenAttributes", value);}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>) : null
|
||||
}
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("application:Order"), i18next.t("application:Order - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<InputNumber style={{width: "150px"}} value={this.state.application.order} min={0} step={1} precision={0} addonAfter="" onChange={value => {
|
||||
this.updateApplicationField("order", value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("application:Token expire"), i18next.t("application:Token expire - Tooltip"))} :
|
||||
@@ -801,6 +828,26 @@ class ApplicationEditPage extends React.Component {
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("application:SAML hash algorithm"), i18next.t("application:SAML hash algorithm - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}}
|
||||
value={this.state.application.samlHashAlgorithm}
|
||||
onChange={(value => {
|
||||
this.updateApplicationField("samlHashAlgorithm", value);
|
||||
})} >
|
||||
{
|
||||
[
|
||||
{id: "SHA1", name: "SHA1"},
|
||||
{id: "SHA256", name: "SHA256"},
|
||||
{id: "SHA512", name: "SHA512"},
|
||||
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:SAML attributes"), i18next.t("general:SAML attributes - Tooltip"))} :
|
||||
@@ -1074,7 +1121,7 @@ class ApplicationEditPage extends React.Component {
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
</Col>
|
||||
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => this.updateApplicationField("footerHtml", Setting.getDefaultFooterContent())} >
|
||||
{i18next.t("provider:Reset to Default HTML")}
|
||||
{i18next.t("general:Reset to Default")}
|
||||
</Button>
|
||||
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => this.updateApplicationField("footerHtml", Setting.getEmptyFooterContent())} >
|
||||
{i18next.t("application:Reset to Empty")}
|
||||
|
||||
@@ -113,6 +113,34 @@ class ApplicationListPage extends BaseListPage {
|
||||
});
|
||||
}
|
||||
|
||||
copyApplication(i) {
|
||||
const original = this.state.data[i];
|
||||
const randomSuffix = Setting.getRandomName();
|
||||
const newName = `${original.name}_${randomSuffix}`;
|
||||
|
||||
const copiedApplication = {
|
||||
...original,
|
||||
name: newName,
|
||||
createdTime: moment().format(),
|
||||
displayName: "Copy Application - " + newName,
|
||||
clientId: "",
|
||||
clientSecret: "",
|
||||
};
|
||||
|
||||
ApplicationBackend.addApplication(copiedApplication)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.props.history.push({pathname: `/applications/${copiedApplication.organization}/${newName}`, mode: "add"});
|
||||
Setting.showMessage("success", i18next.t("general:Successfully copied"));
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to copy")}: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
renderTable(applications) {
|
||||
const columns = [
|
||||
{
|
||||
@@ -237,12 +265,13 @@ class ApplicationListPage extends BaseListPage {
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: "",
|
||||
key: "op",
|
||||
width: "170px",
|
||||
width: "230px",
|
||||
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(`/applications/${record.organization}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.copyApplication(index)}>{i18next.t("general:Copy")}</Button>
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteApplication(index)}
|
||||
@@ -255,6 +284,7 @@ class ApplicationListPage extends BaseListPage {
|
||||
},
|
||||
];
|
||||
|
||||
const filteredColumns = Setting.filterTableColumns(columns, this.props.formItems ?? this.state.formItems);
|
||||
const paginationProps = {
|
||||
total: this.state.pagination.total,
|
||||
showQuickJumper: true,
|
||||
@@ -264,7 +294,7 @@ class ApplicationListPage extends BaseListPage {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table scroll={{x: "max-content"}} columns={columns} dataSource={applications} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
|
||||
<Table scroll={{x: "max-content"}} columns={filteredColumns} dataSource={applications} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
|
||||
title={() => (
|
||||
<div>
|
||||
{i18next.t("general:Applications")}
|
||||
|
||||
@@ -19,6 +19,7 @@ import Highlighter from "react-highlight-words";
|
||||
import i18next from "i18next";
|
||||
import * as Setting from "./Setting";
|
||||
import * as TourConfig from "./TourConfig";
|
||||
import * as FormBackend from "./backend/FormBackend";
|
||||
|
||||
class BaseListPage extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -36,16 +37,18 @@ class BaseListPage extends React.Component {
|
||||
searchedColumn: "",
|
||||
isAuthorized: true,
|
||||
isTourVisible: TourConfig.getTourVisible(),
|
||||
formItems: [],
|
||||
};
|
||||
}
|
||||
|
||||
handleOrganizationChange = () => {
|
||||
this.setState({
|
||||
organizationName: this.props.match?.params.organizationName || Setting.getRequestOrganization(this.props.account),
|
||||
},
|
||||
() => {
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination});
|
||||
});
|
||||
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination});
|
||||
};
|
||||
|
||||
handleTourChange = () => {
|
||||
@@ -71,6 +74,37 @@ class BaseListPage extends React.Component {
|
||||
UNSAFE_componentWillMount() {
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination});
|
||||
this.getForm();
|
||||
}
|
||||
|
||||
getForm() {
|
||||
const tag = this.props.account.tag;
|
||||
const formType = this.props.match?.path?.replace(/^\//, "");
|
||||
let formName = formType;
|
||||
if (tag !== "") {
|
||||
formName = formType + "-tag-" + tag;
|
||||
FormBackend.getForm(this.props.account.owner, formName)
|
||||
.then(res => {
|
||||
if (res.status === "ok" && res.data) {
|
||||
this.setState({formItems: res.data.formItems});
|
||||
} else {
|
||||
this.fetchFormWithoutTag(formType);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.fetchFormWithoutTag(formType);
|
||||
}
|
||||
}
|
||||
|
||||
fetchFormWithoutTag(formName) {
|
||||
FormBackend.getForm(this.props.account.owner, formName)
|
||||
.then(res => {
|
||||
if (res.status === "ok" && res.data) {
|
||||
this.setState({formItems: res.data.formItems});
|
||||
} else {
|
||||
this.setState({formItems: []});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getColumnSearchProps = (dataIndex, customRender = null) => ({
|
||||
|
||||
225
web/src/FormEditPage.js
Normal file
225
web/src/FormEditPage.js
Normal file
@@ -0,0 +1,225 @@
|
||||
// Copyright 2025 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 {Button, Card, Col, Input, Row, Select} from "antd";
|
||||
import * as FormBackend from "./backend/FormBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
import FormItemTable from "./table/FormItemTable";
|
||||
import UserListPage from "./UserListPage";
|
||||
import ApplicationListPage from "./ApplicationListPage";
|
||||
import ProviderListPage from "./ProviderListPage";
|
||||
import OrganizationListPage from "./OrganizationListPage";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
class FormEditPage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
formName: props.match.params.formName,
|
||||
form: null,
|
||||
formItems: [],
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.getForm();
|
||||
}
|
||||
|
||||
getForm() {
|
||||
FormBackend.getForm(this.props.account.owner, this.state.formName)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
form: res.data,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateFormField(key, value) {
|
||||
const form = this.state.form;
|
||||
form[key] = value;
|
||||
this.setState({
|
||||
form: form,
|
||||
});
|
||||
}
|
||||
|
||||
renderForm() {
|
||||
return (
|
||||
<Card size="small" title={
|
||||
<div>
|
||||
{i18next.t("form:Edit Form")}
|
||||
<Button onClick={() => this.submitFormEdit(false)}>{i18next.t("general:Save")}</Button>
|
||||
<Button style={{marginLeft: "20px"}} type="primary"
|
||||
onClick={() => this.submitFormEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||
</div>
|
||||
} style={{marginLeft: "5px"}} type="inner">
|
||||
<Row style={{marginTop: "10px"}}>
|
||||
<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
|
||||
value={this.state.form.name}
|
||||
disabled={true}
|
||||
onChange={e => {this.updateFormField("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 value={this.state.form.displayName} onChange={e => {
|
||||
this.updateFormField("displayName", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}}>
|
||||
<Col style={{marginTop: "5px"}} span={Setting.isMobile() ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Type"), i18next.t("general:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
<Select
|
||||
style={{width: "100%"}}
|
||||
value={this.state.form.type}
|
||||
onChange={value => {
|
||||
this.updateFormField("type", value);
|
||||
this.updateFormField("name", value);
|
||||
this.updateFormField("displayName", value);
|
||||
const defaultItems = new FormItemTable({formType: value}).getItems();
|
||||
this.updateFormField("formItems", defaultItems);
|
||||
}}
|
||||
>
|
||||
{Setting.getFormTypeOptions().map(option => (
|
||||
<Option key={option.id} value={option.id}>{i18next.t(option.name)}</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}}>
|
||||
<Col style={{marginTop: "5px"}} span={Setting.isMobile() ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("user:Tag"), i18next.t("user:Tag - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
<Input value={this.state.form.tag} onChange={e => {
|
||||
this.updateFormField("tag", e.target.value);
|
||||
this.updateFormField("name", e.target.value ? `${this.state.form.type}-tag-${e.target.value}` : this.state.form.type);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}}>
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("form:Form items"), i18next.t("form:Form items - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
<FormItemTable
|
||||
title={i18next.t("form:Form items")}
|
||||
table={this.state.form.formItems}
|
||||
onUpdateTable={(value) => {
|
||||
this.updateFormField("formItems", value);
|
||||
}}
|
||||
formType={this.state.form.type}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}}>
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Preview"), i18next.t("general:Preview - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
{
|
||||
this.renderListPreview()
|
||||
}
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
renderListPreview() {
|
||||
let listPageComponent = null;
|
||||
|
||||
if (this.state.form.type === "users") {
|
||||
listPageComponent = (<UserListPage {...this.props} formItems={this.state.form.formItems} />);
|
||||
} else if (this.state.form.type === "applications") {
|
||||
listPageComponent = (<ApplicationListPage {...this.props} formItems={this.state.form.formItems} />);
|
||||
} else if (this.state.form.type === "providers") {
|
||||
listPageComponent = (<ProviderListPage {...this.props} formItems={this.state.form.formItems} />);
|
||||
} else if (this.state.form.type === "organizations") {
|
||||
listPageComponent = (<OrganizationListPage {...this.props} formItems={this.state.form.formItems} />);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{position: "relative", border: "1px solid rgb(217,217,217)", height: "600px", cursor: "pointer"}} onClick={(e) => {Setting.openLink(`/${this.state.form.type}`);}}>
|
||||
<div style={{position: "relative", height: "100%", overflow: "auto"}}>
|
||||
<div style={{display: "inline-block", position: "relative", zIndex: 1, pointerEvents: "none"}}>
|
||||
{listPageComponent}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{position: "absolute", top: 0, left: 0, right: 0, bottom: 0, zIndex: 10, background: "rgba(0,0,0,0.4)", pointerEvents: "none"}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
submitFormEdit(exitAfterSave) {
|
||||
const form = Setting.deepCopy(this.state.form);
|
||||
FormBackend.updateForm(this.state.form.owner, this.state.formName, form)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
if (res.data) {
|
||||
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||
this.setState({
|
||||
formName: this.state.form.name,
|
||||
});
|
||||
if (exitAfterSave) {
|
||||
this.props.history.push("/forms");
|
||||
} else {
|
||||
this.props.history.push(`/forms/${this.state.form.name}`);
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", i18next.t("general:Failed to save"));
|
||||
this.updateFormField("name", this.state.formName);
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
this.state.form !== null ? this.renderForm() : null
|
||||
}
|
||||
<div style={{marginTop: "20px", marginLeft: "40px"}}>
|
||||
<Button size="large" onClick={() => this.submitFormEdit(false)}>{i18next.t("general:Save")}</Button>
|
||||
<Button style={{marginLeft: "20px"}} type="primary" size="large"
|
||||
onClick={() => this.submitFormEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default FormEditPage;
|
||||
245
web/src/FormListPage.js
Normal file
245
web/src/FormListPage.js
Normal file
@@ -0,0 +1,245 @@
|
||||
// Copyright 2025 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 {Button, Col, List, Popconfirm, Row, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import * as Setting from "./Setting";
|
||||
import * as FormBackend from "./backend/FormBackend";
|
||||
import i18next from "i18next";
|
||||
|
||||
class FormListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
newForm() {
|
||||
const randomName = Setting.getRandomName();
|
||||
return {
|
||||
owner: this.props.account.owner,
|
||||
name: `form_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
displayName: `New Form - ${randomName}`,
|
||||
formItems: [],
|
||||
};
|
||||
}
|
||||
|
||||
addForm() {
|
||||
const newForm = this.newForm();
|
||||
FormBackend.addForm(newForm)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
sessionStorage.setItem("formListUrl", window.location.pathname);
|
||||
this.props.history.push({
|
||||
pathname: `/forms/${newForm.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}`);
|
||||
});
|
||||
}
|
||||
|
||||
deleteForm(record) {
|
||||
FormBackend.deleteForm(record)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||
this.setState({
|
||||
data: this.state.data.filter((item) => item.name !== record.name),
|
||||
pagination: {
|
||||
...this.state.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 delete")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
renderTable(forms) {
|
||||
const columns = [
|
||||
{
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
width: "160px",
|
||||
sorter: (a, b) => a.name.localeCompare(b.name),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/forms/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Display name"),
|
||||
dataIndex: "displayName",
|
||||
key: "displayName",
|
||||
width: "200px",
|
||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Type"),
|
||||
dataIndex: "type",
|
||||
key: "type",
|
||||
width: "120px",
|
||||
sorter: (a, b) => a.type.localeCompare(b.type),
|
||||
render: (text, record, index) => {
|
||||
const typeOption = Setting.getFormTypeOptions().find(option => option.id === text);
|
||||
return typeOption ? i18next.t(typeOption.name) : text;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("form:Form items"),
|
||||
dataIndex: "formItems",
|
||||
key: "formItems",
|
||||
...this.getColumnSearchProps("formItems"),
|
||||
render: (text, record, index) => {
|
||||
const providers = text;
|
||||
if (!providers || providers.length === 0) {
|
||||
return `(${i18next.t("general:empty")})`;
|
||||
}
|
||||
|
||||
const visibleProviders = providers.filter(item => item.visible !== false);
|
||||
const leftItems = [];
|
||||
const rightItems = [];
|
||||
visibleProviders.forEach((item, idx) => {
|
||||
if (idx % 2 === 0) {
|
||||
leftItems.push(item);
|
||||
} else {
|
||||
rightItems.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
const getList = (items) => (
|
||||
<List
|
||||
size="small"
|
||||
locale={{emptyText: " "}}
|
||||
dataSource={items}
|
||||
renderItem={providerItem => (
|
||||
<List.Item>
|
||||
<div style={{display: "inline"}}>{i18next.t(providerItem.label)}</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col span={12}>{getList(leftItems)}</Col>
|
||||
<Col span={12}>{getList(rightItems)}</Col>
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: "action",
|
||||
key: "action",
|
||||
width: "180px",
|
||||
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(`/forms/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
title={`${i18next.t("general:Sure to delete")}: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteForm(record)}
|
||||
okText={i18next.t("general:OK")}
|
||||
cancelText={i18next.t("general:Cancel")}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary"
|
||||
danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</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={forms}
|
||||
rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered
|
||||
pagination={paginationProps}
|
||||
title={() => (
|
||||
<div>
|
||||
{i18next.t("general:Forms")}
|
||||
<Button type="primary" size="small"
|
||||
onClick={this.addForm.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
loading={this.state.loading}
|
||||
onChange={this.handleTableChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
fetch = (params = {}) => {
|
||||
const field = params.searchedColumn, value = params.searchText;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
this.setState({loading: true});
|
||||
FormBackend.getForms(this.props.account.owner, 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 FormListPage;
|
||||
@@ -117,7 +117,7 @@ class GroupListPage extends BaseListPage {
|
||||
|
||||
return (
|
||||
<Upload {...props}>
|
||||
<Button icon={<UploadOutlined />} id="upload-button" type="primary" size="small">
|
||||
<Button icon={<UploadOutlined />} id="upload-button" size="small">
|
||||
{i18next.t("group:Upload (.xlsx)")}
|
||||
</Button>
|
||||
</Upload>
|
||||
@@ -268,7 +268,7 @@ class GroupListPage extends BaseListPage {
|
||||
title={() => (
|
||||
<div>
|
||||
{i18next.t("general:Groups")}
|
||||
<Button style={{marginRight: "5px"}} type="primary" size="small" onClick={this.addGroup.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
<Button style={{marginRight: "15px"}} type="primary" size="small" onClick={this.addGroup.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
{
|
||||
this.renderUpload()
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Input, InputNumber, Row, Select} from "antd";
|
||||
import {Button, Card, Col, Input, InputNumber, Modal, Row, Select, Table} from "antd";
|
||||
import {CopyOutlined} from "@ant-design/icons";
|
||||
import * as InvitationBackend from "./backend/InvitationBackend";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
@@ -37,6 +37,7 @@ class InvitationEditPage extends React.Component {
|
||||
applications: [],
|
||||
groups: [],
|
||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||
sendLoading: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -123,6 +124,35 @@ class InvitationEditPage extends React.Component {
|
||||
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
|
||||
}
|
||||
|
||||
renderSendEmailModal() {
|
||||
const emailColumns = [
|
||||
{title: "email", dataIndex: "email"},
|
||||
];
|
||||
const emails = this.state.emails?.split("\n")?.filter(email => Setting.isValidEmail(email));
|
||||
const emailData = emails?.map((email) => {return {email: email};});
|
||||
|
||||
return <Modal title={i18next.t("general:Send")}
|
||||
style={{height: "800px"}}
|
||||
open={this.state.showSendModal}
|
||||
closable
|
||||
footer={[
|
||||
<Button key={1} loading={this.state.sendLoading} type="primary"
|
||||
onClick={() => {
|
||||
this.setState({sendLoading: true});
|
||||
InvitationBackend.sendInvitation(this.state.invitation, emails).then(() => {
|
||||
this.setState({sendLoading: false});
|
||||
Setting.showMessage("success", i18next.t("general:Successfully sent"));
|
||||
}).catch(err => Setting.showMessage("success", err.message));
|
||||
}}>{i18next.t("general:Send")}</Button>,
|
||||
]}
|
||||
onCancel={() => {this.setState({showSendModal: false});}}>
|
||||
<div >
|
||||
<p>You will send invitation email to:</p>
|
||||
<Table showHeader={false} columns={emailColumns} dataSource={emailData} size={"small"}></Table>
|
||||
</div>
|
||||
</Modal>;
|
||||
}
|
||||
|
||||
renderInvitation() {
|
||||
const isCreatedByPlan = this.state.invitation.tag === "auto_created_invitation_for_plan";
|
||||
return (
|
||||
@@ -199,6 +229,17 @@ class InvitationEditPage extends React.Component {
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Send")}
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input.TextArea autoSize={{minRows: 3, maxRows: 10}} value={this.state.emails} onChange={(value) => {
|
||||
this.setState({emails: value.target.value});
|
||||
}}></Input.TextArea>
|
||||
<Button type="primary" style={{marginTop: "20px"}} onClick={() => this.setState({showSendModal: true})}>{i18next.t("general:Send")}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("invitation:Quota"), i18next.t("invitation:Quota - Tooltip"))} :
|
||||
@@ -338,6 +379,7 @@ class InvitationEditPage extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{this.state.showSendModal ? this.renderSendEmailModal() : null}
|
||||
{
|
||||
this.state.invitation !== null ? this.renderInvitation() : null
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
import * as GroupBackend from "./backend/GroupBackend";
|
||||
import AttributesMapperTable from "./table/AttributesMapperTable";
|
||||
|
||||
const {Option} = Select;
|
||||
|
||||
@@ -278,6 +279,14 @@ class LdapEditPage extends React.Component {
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
|
||||
{Setting.getLabel(i18next.t("ldap:Custom attributes"), i18next.t("ldap:Custom attributes - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={21} >
|
||||
<AttributesMapperTable customAttributes={this.state.ldap.customAttributes} onUpdateTable={(value) => {this.updateLdapField("customAttributes", value);}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}}>
|
||||
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
|
||||
{Setting.getLabel(i18next.t("ldap:Auto Sync"), i18next.t("ldap:Auto Sync - Tooltip"))} :
|
||||
|
||||
@@ -73,6 +73,8 @@ import PricingEditPage from "./PricingEditPage";
|
||||
import SubscriptionListPage from "./SubscriptionListPage";
|
||||
import SubscriptionEditPage from "./SubscriptionEditPage";
|
||||
import SystemInfo from "./SystemInfo";
|
||||
import FormListPage from "./FormListPage";
|
||||
import FormEditPage from "./FormEditPage";
|
||||
import SyncerListPage from "./SyncerListPage";
|
||||
import SyncerEditPage from "./SyncerEditPage";
|
||||
import WebhookListPage from "./WebhookListPage";
|
||||
@@ -330,11 +332,13 @@ function ManagementPage(props) {
|
||||
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="/forms">{i18next.t("general:Forms")}</Link>, "/forms"),
|
||||
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="/forms">{i18next.t("general:Forms")}</Link>, "/forms"),
|
||||
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
|
||||
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks")]));
|
||||
}
|
||||
@@ -434,6 +438,8 @@ function ManagementPage(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="/forms" render={(props) => renderLoginIfNotLoggedIn(<FormListPage account={account} {...props} />)} />
|
||||
<Route exact path="/forms/:formName" render={(props) => renderLoginIfNotLoggedIn(<FormEditPage 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} />)} />
|
||||
|
||||
@@ -271,6 +271,7 @@ class OrganizationListPage extends BaseListPage {
|
||||
},
|
||||
];
|
||||
|
||||
const filteredColumns = Setting.filterTableColumns(columns, this.props.formItems ?? this.state.formItems);
|
||||
const paginationProps = {
|
||||
total: this.state.pagination.total,
|
||||
showQuickJumper: true,
|
||||
@@ -280,7 +281,7 @@ class OrganizationListPage extends BaseListPage {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table scroll={{x: "max-content"}} columns={columns} dataSource={organizations} rowKey="name" size="middle" bordered pagination={paginationProps}
|
||||
<Table scroll={{x: "max-content"}} columns={filteredColumns} dataSource={organizations} rowKey="name" size="middle" bordered pagination={paginationProps}
|
||||
title={() => (
|
||||
<div>
|
||||
{i18next.t("general:Organizations")}
|
||||
|
||||
@@ -99,6 +99,7 @@ class PermissionListPage extends BaseListPage {
|
||||
Setting.showMessage("error", "File failed to upload");
|
||||
}
|
||||
}
|
||||
|
||||
renderPermissionUpload() {
|
||||
const props = {
|
||||
name: "file",
|
||||
@@ -113,7 +114,7 @@ class PermissionListPage extends BaseListPage {
|
||||
|
||||
return (
|
||||
<Upload {...props}>
|
||||
<Button icon={<UploadOutlined />} id="upload-button" type="primary" size="small">
|
||||
<Button icon={<UploadOutlined />} id="upload-button" size="small">
|
||||
{i18next.t("user:Upload (.xlsx)")}
|
||||
</Button></Upload>
|
||||
);
|
||||
@@ -390,7 +391,7 @@ class PermissionListPage extends BaseListPage {
|
||||
title={() => (
|
||||
<div>
|
||||
{i18next.t("general:Permissions")}
|
||||
<Button id="add-button" style={{marginRight: "5px"}} type="primary" size="small" onClick={this.addPermission.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
<Button id="add-button" style={{marginRight: "15px"}} type="primary" size="small" onClick={this.addPermission.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
{
|
||||
this.renderPermissionUpload()
|
||||
}
|
||||
|
||||
@@ -367,6 +367,19 @@ class ProviderEditPage extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
getDomainLabel(provider) {
|
||||
switch (provider.category) {
|
||||
case "OAuth":
|
||||
if (provider.type === "AzureAD" || provider.type === "AzureADB2C") {
|
||||
return Setting.getLabel(i18next.t("provider:Tenant ID"), i18next.t("provider:Tenant ID - Tooltip"));
|
||||
} else {
|
||||
return Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"));
|
||||
}
|
||||
default:
|
||||
return Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"));
|
||||
}
|
||||
}
|
||||
|
||||
getProviderSubTypeOptions(type) {
|
||||
if (type === "WeCom" || type === "Infoflow") {
|
||||
return (
|
||||
@@ -597,6 +610,7 @@ class ProviderEditPage extends React.Component {
|
||||
this.updateProviderField("disableSsl", false);
|
||||
this.updateProviderField("title", "Casdoor Verification Code");
|
||||
this.updateProviderField("content", Setting.getDefaultHtmlEmailContent());
|
||||
this.updateProviderField("metadata", Setting.getDefaultInvitationHtmlEmailContent());
|
||||
this.updateProviderField("receiver", this.props.account.email);
|
||||
} else if (value === "SMS") {
|
||||
this.updateProviderField("type", "Twilio SMS");
|
||||
@@ -962,7 +976,7 @@ class ProviderEditPage extends React.Component {
|
||||
this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "AzureADB2C" && (this.state.provider.type !== "Casdoor" && this.state.category !== "Storage") && this.state.provider.type !== "Okta" && this.state.provider.type !== "Nextcloud" ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
||||
{this.getDomainLabel(this.state.provider)} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input prefix={<LinkOutlined />} value={this.state.provider.domain} onChange={e => {
|
||||
@@ -1242,10 +1256,10 @@ class ProviderEditPage extends React.Component {
|
||||
<Col span={22} >
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => this.updateProviderField("content", "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes. <reset-link>Or click %link to reset</reset-link>")} >
|
||||
{i18next.t("provider:Reset to Default Text")}
|
||||
{i18next.t("general:Reset to Default")}
|
||||
</Button>
|
||||
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary" onClick={() => this.updateProviderField("content", Setting.getDefaultHtmlEmailContent())} >
|
||||
{i18next.t("provider:Reset to Default HTML")}
|
||||
{i18next.t("general:Reset to Default")}
|
||||
</Button>
|
||||
</Row>
|
||||
<Row>
|
||||
@@ -1271,6 +1285,42 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(`${i18next.t("provider:Email content")}-${i18next.t("general:Invitations")}`, i18next.t("provider:Email content - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => this.updateProviderField("metadata", "You have invited to join Casdoor. Here is your invitation code: %s, please enter in 5 minutes. Or click %link to signup")} >
|
||||
{i18next.t("general:Reset to Default")}
|
||||
</Button>
|
||||
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary" onClick={() => this.updateProviderField("metadata", Setting.getDefaultInvitationHtmlEmailContent())} >
|
||||
{i18next.t("general:Reset to Default")}
|
||||
</Button>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={Setting.isMobile() ? 22 : 11}>
|
||||
<div style={{height: "300px", margin: "10px"}}>
|
||||
<Editor
|
||||
value={this.state.provider.metadata}
|
||||
fillHeight
|
||||
dark
|
||||
lang="html"
|
||||
onChange={value => {
|
||||
this.updateProviderField("metadata", value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Col>
|
||||
<Col span={1} />
|
||||
<Col span={Setting.isMobile() ? 22 : 11}>
|
||||
<div style={{margin: "10px"}}>
|
||||
<div dangerouslySetInnerHTML={{__html: this.state.provider.metadata.replace("%code", "123456")}} />
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}}>
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Test Email"), i18next.t("provider:Test Email - Tooltip"))} :
|
||||
|
||||
@@ -230,6 +230,7 @@ class ProviderListPage extends BaseListPage {
|
||||
},
|
||||
];
|
||||
|
||||
const filteredColumns = Setting.filterTableColumns(columns, this.props.formItems ?? this.state.formItems);
|
||||
const paginationProps = {
|
||||
total: this.state.pagination.total,
|
||||
showQuickJumper: true,
|
||||
@@ -239,7 +240,7 @@ class ProviderListPage extends BaseListPage {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table scroll={{x: "max-content"}} columns={columns} dataSource={providers} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
|
||||
<Table scroll={{x: "max-content"}} columns={filteredColumns} dataSource={providers} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
|
||||
title={() => (
|
||||
<div>
|
||||
{i18next.t("general:Providers")}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user