Compare commits

..

35 Commits

Author SHA1 Message Date
ailiujiarui
4cc2120fed feat: fix the top Navbar UI is broken issue (#3000) 2024-06-09 17:05:04 +08:00
Yang Luo
93b0f52f26 feat: Revert "feat: fix cannot create "/files" folder issue in local file storage provider in Docker" (#2997)
This reverts commit e228045e37.
2024-06-06 11:09:02 +08:00
DacongDA
e228045e37 feat: fix cannot create "/files" folder issue in local file storage provider in Docker (#2994) 2024-06-06 10:49:56 +08:00
DacongDA
6b8c24e1f0 feat: fix password not encrypted issue in SetPassword() API (#2990)
* fix: fix password not encrypted in set password and password type not changed

* Update user.go

---------

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2024-06-04 13:32:13 +08:00
DacongDA
8a79bb64dd feat: test SMTP connection with browser parameters (#2986) 2024-06-04 01:34:36 +08:00
DacongDA
e5f9aab28f feat: support resetting password on first login (#2980)
* feat: support reset password in first login

* feat: disable needUpdatePassword when user haven't email and phone and mfa
2024-06-02 01:00:55 +08:00
Yang Luo
7d05b69aac feat: remove useless code 2024-05-28 20:33:55 +08:00
DacongDA
868e66e866 feat: fix QQ login error when using mobile browser (#2971) 2024-05-27 01:07:15 +08:00
Husile
40ad3c9234 feat: support MFA fields in syncer (#2966)
* feat:add fields of sync-database

* feat:add fields of sync-database
2024-05-27 01:06:59 +08:00
Alex Babel
e2cd0604c2 feat: add back arm64 support in Docker image (#2969) 2024-05-26 01:22:49 +08:00
Yang Luo
78c3065fbb feat: fix address field bug in user edit page 2024-05-24 17:19:27 +08:00
DacongDA
af2a9f0374 feat: get phone number and country from Google OAuth provider (#2965)
* feat: get phone number and country from Google OAuth provider

* feat: fix i18n
2024-05-23 00:42:36 +08:00
DacongDA
bfcfb56336 feat: add address line 1 and 2 in web UI (#2961) 2024-05-19 23:55:38 +08:00
DacongDA
c48306d117 feat: check signup item email regex in signup page (#2960)
* feat: check email regex in frontend

* Update SignupPage.js

---------

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2024-05-19 22:07:34 +08:00
DacongDA
6efec6b4b5 feat: support "label" field for signin item table (#2956) 2024-05-19 03:07:36 +08:00
DacongDA
2daf26aa88 feat: use lowercase username when isUsernameLowered is enabled (#2952)
* feat: auto trim username during login and lowercase when isUsernameLowered enabled in conf

* fix: fix linter error

* fix: fix linter error

* fix: fix linter error
2024-05-17 11:43:19 +08:00
DacongDA
21c151bcf8 feat: fix password not updated bug when updating syncer (#2945) 2024-05-13 00:12:35 +08:00
DacongDA
b6b0b7d318 feat: support checking whether send-webhook API has error (#2944)
* feat: add webhook response for record

* refactor: refactor SendWebhook and use readall to read response body

* fix: improve code format

* fix: improve code format

* fix: improve code format
2024-05-12 20:30:15 +08:00
Yang Luo
0ecc1d599f feat: fix bug in AddUsersInBatch() 2024-05-11 16:59:33 +08:00
Yang Luo
3456fc6695 fix: update go-sms-sender to v0.23.0 2024-05-10 14:05:53 +08:00
DacongDA
c302dc7b8e fix: fix bug when init plan and pricing and record (#2934)
* fix: fix potential bugs in init data

* fix: improve code format

* fix: fix bug when init plan and pricing and record
2024-05-07 23:33:01 +08:00
DacongDA
d24ddd4f1c feat: fix potential bugs in init_data.go (#2932)
* fix: fix potential bugs in init data

* fix: improve code format
2024-05-07 23:11:08 +08:00
Yang Luo
572616d390 fix: fix bug in ProviderItem.CountryCodes 2024-05-07 17:17:45 +08:00
Yang Luo
2187310dbc feat: fix bug in initDefinedOrganization() 2024-05-06 13:57:08 +08:00
DacongDA
26345bb21b feat: add sms provider sendcloud (#2927) 2024-05-06 13:38:55 +08:00
DacongDA
e0455df504 feat: improve record content masking (#2923)
* feat: hide password in record

* feat: improve code format

* feat: improve code format
2024-05-05 12:42:09 +08:00
DacongDA
1dfbbf0e90 feat: fix bug that fails to import built-in org via init_data.json (#2922) 2024-05-05 01:06:15 +08:00
Yang Luo
d43d58dee2 feat: fix getProviders() owner bug in product edit page 2024-05-01 18:04:50 +08:00
Yang Luo
9eb4b12041 fix: rename to countryCodes for UI 2024-05-01 11:44:21 +08:00
Yang Luo
3a45a4ee77 fix: rename to countryCodes 2024-05-01 09:47:44 +08:00
Yang Luo
43393f034b feat: fix the Email provider fails to match bug in GetProviderByCategoryAndRule() 2024-05-01 09:44:19 +08:00
Yang Luo
bafa80513b fix: improve ProviderTable column UI 2024-05-01 00:46:48 +08:00
Yang Luo
8d08140421 fix: fix typo in initBuiltInPermission() 2024-05-01 00:41:16 +08:00
DacongDA
3d29e27d54 feat: support multiple SMS providers for different regions (#2914)
* feat: support using different sms provider for different region

* feat: add multiple support for select and remove log

* feat: revert change for countryCode in loginPage

* feat: revert change for countryCode in user_util.go

* feat: revert change for countryCode in auth.go

* Update application_item.go

* Update CountryCodeSelect.js

* Update ProviderTable.js

---------

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2024-05-01 00:40:47 +08:00
filwaline
199f1d4d10 feat: fix Auto-login causing AuthCodeWithPKCE Failures (#2911) 2024-04-30 12:14:50 +08:00
71 changed files with 969 additions and 235 deletions

View File

@@ -194,7 +194,7 @@ jobs:
with: with:
context: . context: .
target: STANDARD target: STANDARD
platforms: linux/amd64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: casbin/casdoor:${{steps.get-current-tag.outputs.tag }},casbin/casdoor:latest tags: casbin/casdoor:${{steps.get-current-tag.outputs.tag }},casbin/casdoor:latest
@@ -204,7 +204,7 @@ jobs:
with: with:
context: . context: .
target: ALLINONE target: ALLINONE
platforms: linux/amd64 platforms: linux/amd64,linux/arm64
push: true push: true
tags: casbin/casdoor-all-in-one:${{steps.get-current-tag.outputs.tag }},casbin/casdoor-all-in-one:latest tags: casbin/casdoor-all-in-one:${{steps.get-current-tag.outputs.tag }},casbin/casdoor-all-in-one:latest

View File

@@ -1,10 +1,10 @@
FROM node:18.19.0 AS FRONT FROM --platform=$BUILDPLATFORM node:18.19.0 AS FRONT
WORKDIR /web WORKDIR /web
COPY ./web . COPY ./web .
RUN yarn install --frozen-lockfile --network-timeout 1000000 && yarn run build RUN yarn install --frozen-lockfile --network-timeout 1000000 && yarn run build
FROM golang:1.20.12 AS BACK FROM --platform=$BUILDPLATFORM golang:1.20.12 AS BACK
WORKDIR /go/src/casdoor WORKDIR /go/src/casdoor
COPY . . COPY . .
RUN ./build.sh RUN ./build.sh
@@ -13,6 +13,9 @@ RUN go test -v -run TestGetVersionInfo ./util/system_test.go ./util/system.go >
FROM alpine:latest AS STANDARD FROM alpine:latest AS STANDARD
LABEL MAINTAINER="https://casdoor.org/" LABEL MAINTAINER="https://casdoor.org/"
ARG USER=casdoor ARG USER=casdoor
ARG TARGETOS
ARG TARGETARCH
ENV BUILDX_ARCH="${TARGETOS:-linux}_${TARGETARCH:-amd64}"
RUN sed -i 's/https/http/' /etc/apk/repositories RUN sed -i 's/https/http/' /etc/apk/repositories
RUN apk add --update sudo RUN apk add --update sudo
@@ -28,7 +31,7 @@ RUN adduser -D $USER -u 1000 \
USER 1000 USER 1000
WORKDIR / WORKDIR /
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/server ./server COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/server_${BUILDX_ARCH} ./server
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/swagger ./swagger COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/swagger ./swagger
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/conf/app.conf ./conf/app.conf COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/conf/app.conf ./conf/app.conf
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/version_info.txt ./go/src/casdoor/version_info.txt
@@ -47,12 +50,15 @@ RUN apt update \
FROM db AS ALLINONE FROM db AS ALLINONE
LABEL MAINTAINER="https://casdoor.org/" LABEL MAINTAINER="https://casdoor.org/"
ARG TARGETOS
ARG TARGETARCH
ENV BUILDX_ARCH="${TARGETOS:-linux}_${TARGETARCH:-amd64}"
RUN apt update RUN apt update
RUN apt install -y ca-certificates && update-ca-certificates RUN apt install -y ca-certificates && update-ca-certificates
WORKDIR / WORKDIR /
COPY --from=BACK /go/src/casdoor/server ./server COPY --from=BACK /go/src/casdoor/server_${BUILDX_ARCH} ./server
COPY --from=BACK /go/src/casdoor/swagger ./swagger COPY --from=BACK /go/src/casdoor/swagger ./swagger
COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf

View File

@@ -8,4 +8,6 @@ else
echo "Google is blocked, Go proxy is enabled: GOPROXY=https://goproxy.cn,direct" echo "Google is blocked, Go proxy is enabled: GOPROXY=https://goproxy.cn,direct"
export GOPROXY="https://goproxy.cn,direct" export GOPROXY="https://goproxy.cn,direct"
fi fi
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o server .
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o server_linux_amd64 .
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-w -s" -o server_linux_arm64 .

View File

@@ -117,7 +117,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
if form.Type == ResponseTypeLogin { if form.Type == ResponseTypeLogin {
c.SetSessionUsername(userId) c.SetSessionUsername(userId)
util.LogInfo(c.Ctx, "API: [%s] signed in", userId) util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
resp = &Response{Status: "ok", Msg: "", Data: userId} resp = &Response{Status: "ok", Msg: "", Data: userId, Data2: user.NeedUpdatePassword}
} else if form.Type == ResponseTypeCode { } else if form.Type == ResponseTypeCode {
clientId := c.Input().Get("clientId") clientId := c.Input().Get("clientId")
responseType := c.Input().Get("responseType") responseType := c.Input().Get("responseType")
@@ -139,7 +139,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
} }
resp = codeToResponse(code) resp = codeToResponse(code)
resp.Data2 = user.NeedUpdatePassword
if application.EnableSigninSession || application.HasPromptPage() { if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in // The prompt page needs the user to be signed in
c.SetSessionUsername(userId) c.SetSessionUsername(userId)
@@ -152,6 +152,8 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
nonce := c.Input().Get("nonce") nonce := c.Input().Get("nonce")
token, _ := object.GetTokenByUser(application, user, scope, nonce, c.Ctx.Request.Host) token, _ := object.GetTokenByUser(application, user, scope, nonce, c.Ctx.Request.Host)
resp = tokenToResponse(token) resp = tokenToResponse(token)
resp.Data2 = user.NeedUpdatePassword
} }
} else if form.Type == ResponseTypeSaml { // saml flow } else if form.Type == ResponseTypeSaml { // saml flow
res, redirectUrl, method, err := object.GetSamlResponse(application, user, form.SamlRequest, c.Ctx.Request.Host) res, redirectUrl, method, err := object.GetSamlResponse(application, user, form.SamlRequest, c.Ctx.Request.Host)
@@ -159,7 +161,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
c.ResponseError(err.Error(), nil) c.ResponseError(err.Error(), nil)
return return
} }
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]string{"redirectUrl": redirectUrl, "method": method}} resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]interface{}{"redirectUrl": redirectUrl, "method": method, "needUpdatePassword": user.NeedUpdatePassword}}
if application.EnableSigninSession || application.HasPromptPage() { if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in // The prompt page needs the user to be signed in

View File

@@ -27,11 +27,12 @@ import (
) )
type EmailForm struct { type EmailForm struct {
Title string `json:"title"` Title string `json:"title"`
Content string `json:"content"` Content string `json:"content"`
Sender string `json:"sender"` Sender string `json:"sender"`
Receivers []string `json:"receivers"` Receivers []string `json:"receivers"`
Provider string `json:"provider"` Provider string `json:"provider"`
ProviderObject object.Provider `json:"providerObject"`
} }
type SmsForm struct { type SmsForm struct {
@@ -74,7 +75,6 @@ func (c *ApiController) SendEmail() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
} else { } else {
// called by Casdoor SDK via Client ID & Client Secret, so the used Email provider will be the application' Email provider or the default Email provider // called by Casdoor SDK via Client ID & Client Secret, so the used Email provider will be the application' Email provider or the default Email provider
provider, err = c.GetProviderFromContext("Email") provider, err = c.GetProviderFromContext("Email")
@@ -84,6 +84,13 @@ func (c *ApiController) SendEmail() {
} }
} }
if emailForm.ProviderObject.Name != "" {
if emailForm.ProviderObject.ClientSecret == "***" {
emailForm.ProviderObject.ClientSecret = provider.ClientSecret
}
provider = &emailForm.ProviderObject
}
// when receiver is the reserved keyword: "TestSmtpServer", it means to test the SMTP server instead of sending a real Email // when receiver is the reserved keyword: "TestSmtpServer", it means to test the SMTP server instead of sending a real Email
if len(emailForm.Receivers) == 1 && emailForm.Receivers[0] == "TestSmtpServer" { if len(emailForm.Receivers) == 1 && emailForm.Receivers[0] == "TestSmtpServer" {
err = object.DailSmtpServer(provider) err = object.DailSmtpServer(provider)

View File

@@ -20,6 +20,7 @@ import (
"strings" "strings"
"github.com/beego/beego/utils/pagination" "github.com/beego/beego/utils/pagination"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
@@ -293,6 +294,11 @@ func (c *ApiController) UpdateUser() {
return return
} }
isUsernameLowered := conf.GetConfigBool("isUsernameLowered")
if isUsernameLowered {
user.Name = strings.ToLower(user.Name)
}
isAdmin := c.IsAdmin() isAdmin := c.IsAdmin()
if pass, err := object.CheckPermissionForUpdateUser(oldUser, &user, isAdmin, c.GetAcceptLanguage()); !pass { if pass, err := object.CheckPermissionForUpdateUser(oldUser, &user, isAdmin, c.GetAcceptLanguage()); !pass {
c.ResponseError(err) c.ResponseError(err)
@@ -503,8 +509,21 @@ func (c *ApiController) SetPassword() {
return return
} }
organization, err := object.GetOrganizationByUser(targetUser)
if err != nil {
c.ResponseError(err.Error())
return
}
if organization == nil {
c.ResponseError(fmt.Sprintf(c.T("the organization: %s is not found"), targetUser.Owner))
return
}
targetUser.Password = newPassword targetUser.Password = newPassword
_, err = object.SetUserField(targetUser, "password", targetUser.Password) targetUser.UpdateUserPassword(organization)
targetUser.NeedUpdatePassword = false
_, err = object.UpdateUser(userId, targetUser, []string{"password", "need_update_password", "password_type"}, false)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

View File

@@ -295,7 +295,7 @@ func (c *ApiController) SendVerificationCode() {
vform.CountryCode = mfaProps.CountryCode vform.CountryCode = mfaProps.CountryCode
} }
provider, err = application.GetSmsProvider(vform.Method) provider, err = application.GetSmsProvider(vform.Method, vform.CountryCode)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

6
go.mod
View File

@@ -9,12 +9,12 @@ require (
github.com/beego/beego v1.12.12 github.com/beego/beego v1.12.12
github.com/beevik/etree v1.1.0 github.com/beevik/etree v1.1.0
github.com/casbin/casbin/v2 v2.77.2 github.com/casbin/casbin/v2 v2.77.2
github.com/casdoor/go-sms-sender v0.20.0 github.com/casdoor/go-sms-sender v0.24.0
github.com/casdoor/gomail/v2 v2.0.1 github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/notify v0.45.0 github.com/casdoor/notify v0.45.0
github.com/casdoor/oss v1.6.0 github.com/casdoor/oss v1.6.0
github.com/casdoor/xorm-adapter/v3 v3.1.0 github.com/casdoor/xorm-adapter/v3 v3.1.0
github.com/casvisor/casvisor-go-sdk v1.3.0 github.com/casvisor/casvisor-go-sdk v1.4.0
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
github.com/denisenkom/go-mssqldb v0.9.0 github.com/denisenkom/go-mssqldb v0.9.0
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
@@ -45,7 +45,7 @@ require (
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/russellhaering/gosaml2 v0.9.0 github.com/russellhaering/gosaml2 v0.9.0
github.com/russellhaering/goxmldsig v1.2.0 github.com/russellhaering/goxmldsig v1.2.0
github.com/sendgrid/sendgrid-go v3.14.0+incompatible // indirect github.com/sendgrid/sendgrid-go v3.14.0+incompatible
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible github.com/shirou/gopsutil v3.21.11+incompatible
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed

10
go.sum
View File

@@ -1003,6 +1003,8 @@ github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0I
github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI=
github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg= github.com/apache/arrow/go/v12 v12.0.0/go.mod h1:d+tV/eHZZ7Dz7RPrFKtPK02tpr+c9/PEd/zm8mDS9Vg=
github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
github.com/apistd/uni-go-sdk v0.0.2 h1:7kqETCOz/rz8AQU55XGzxDFGoFeMgeZL5fGwvxKBZrc=
github.com/apistd/uni-go-sdk v0.0.2/go.mod h1:eIqYos4IbHgE/rB75r05ypNLahooEMJCrbjXq322b74=
github.com/appleboy/go-fcm v0.1.5/go.mod h1:MSxZ4LqGRsnywOjnlXJXMqbjZrG4vf+0oHitfC9HRH0= github.com/appleboy/go-fcm v0.1.5/go.mod h1:MSxZ4LqGRsnywOjnlXJXMqbjZrG4vf+0oHitfC9HRH0=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
@@ -1083,8 +1085,8 @@ github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk= github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXyD9XPs= github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXyD9XPs=
github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc= github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc=
github.com/casdoor/go-sms-sender v0.20.0 h1:yLbCakV04DzzehhgBklOrSeCFjMwpfKBeemz9b+Y8OM= github.com/casdoor/go-sms-sender v0.24.0 h1:LNLsce3EG/87I3JS6UiajF3LlQmdIiCgebEu0IE4wSM=
github.com/casdoor/go-sms-sender v0.20.0/go.mod h1:cQs7qqohMJBgIVZebOCB8ko09naG1vzFJEH59VNIscs= github.com/casdoor/go-sms-sender v0.24.0/go.mod h1:bOm4H8/YfJmEHjBatEVQFOnAf0OOn1B0Wi5B7zDhws0=
github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w= github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w=
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q= github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk= github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk=
@@ -1093,8 +1095,8 @@ github.com/casdoor/oss v1.6.0 h1:IOWrGLJ+VO82qS796eaRnzFPPA1Sn3cotYTi7O/VIlQ=
github.com/casdoor/oss v1.6.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE= github.com/casdoor/oss v1.6.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk= github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM= github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
github.com/casvisor/casvisor-go-sdk v1.3.0 h1:HVgm2g3lWpNX2wBNidzR743QY4O5kAjLUJ9tS2juO8g= github.com/casvisor/casvisor-go-sdk v1.4.0 h1:hbZEGGJ1cwdHFAxeXrMoNw6yha6Oyg2F0qQhBNCN/dg=
github.com/casvisor/casvisor-go-sdk v1.3.0/go.mod h1:frnNtH5GA0wxzAQLyZxxfL0RSsSub9GQPi2Ybe86ocE= github.com/casvisor/casvisor-go-sdk v1.4.0/go.mod h1:frnNtH5GA0wxzAQLyZxxfL0RSsSub9GQPi2Ybe86ocE=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=

View File

@@ -25,6 +25,7 @@ import (
"time" "time"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/nyaruka/phonenumbers"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
@@ -130,6 +131,23 @@ type GoogleUserInfo struct {
Locale string `json:"locale"` Locale string `json:"locale"`
} }
type GooglePeopleApiPhoneNumberMetaData struct {
Primary bool `json:"primary"`
}
type GooglePeopleApiPhoneNumber struct {
CanonicalForm string `json:"canonicalForm"`
MetaData GooglePeopleApiPhoneNumberMetaData `json:"metadata"`
Value string `json:"value"`
Type string `json:"type"`
}
type GooglePeopleApiResult struct {
PhoneNumbers []GooglePeopleApiPhoneNumber `json:"phoneNumbers"`
Etag string `json:"etag"`
ResourceName string `json:"resourceName"`
}
func (idp *GoogleIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) { func (idp *GoogleIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
if strings.HasPrefix(token.AccessToken, GoogleIdTokenKey) { if strings.HasPrefix(token.AccessToken, GoogleIdTokenKey) {
googleIdToken, ok := token.Extra(GoogleIdTokenKey).(GoogleIdToken) googleIdToken, ok := token.Extra(GoogleIdTokenKey).(GoogleIdToken)
@@ -167,12 +185,49 @@ func (idp *GoogleIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error)
return nil, errors.New("google email is empty") return nil, errors.New("google email is empty")
} }
url = fmt.Sprintf("https://people.googleapis.com/v1/people/me?personFields=phoneNumbers&access_token=%s", token.AccessToken)
resp, err = idp.Client.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err = io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var googlePeopleResult GooglePeopleApiResult
err = json.Unmarshal(body, &googlePeopleResult)
if err != nil {
return nil, err
}
var phoneNumber string
var countryCode string
if len(googlePeopleResult.PhoneNumbers) != 0 {
for _, phoneData := range googlePeopleResult.PhoneNumbers {
if phoneData.MetaData.Primary {
phoneNumber = phoneData.CanonicalForm
break
}
}
phoneNumberParsed, err := phonenumbers.Parse(phoneNumber, "")
if err != nil {
return nil, err
}
countryCode = phonenumbers.GetRegionCodeForNumber(phoneNumberParsed)
phoneNumber = fmt.Sprintf("%d", phoneNumberParsed.GetNationalNumber())
}
userInfo := UserInfo{ userInfo := UserInfo{
Id: googleUserInfo.Id, Id: googleUserInfo.Id,
Username: googleUserInfo.Email, Username: googleUserInfo.Email,
DisplayName: googleUserInfo.Name, DisplayName: googleUserInfo.Name,
Email: googleUserInfo.Email, Email: googleUserInfo.Email,
AvatarUrl: googleUserInfo.Picture, AvatarUrl: googleUserInfo.Picture,
Phone: phoneNumber,
CountryCode: countryCode,
} }
return &userInfo, nil return &userInfo, nil
} }

View File

@@ -46,6 +46,7 @@ type SigninItem struct {
Name string `json:"name"` Name string `json:"name"`
Visible bool `json:"visible"` Visible bool `json:"visible"`
Label string `json:"label"` Label string `json:"label"`
CustomCss string `json:"customCss"`
Placeholder string `json:"placeholder"` Placeholder string `json:"placeholder"`
Rule string `json:"rule"` Rule string `json:"rule"`
IsCustom bool `json:"isCustom"` IsCustom bool `json:"isCustom"`
@@ -209,7 +210,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem := &SigninItem{ signinItem := &SigninItem{
Name: "Back button", Name: "Back button",
Visible: true, Visible: true,
Label: ".back-button {\n top: 65px;\n left: 15px;\n position: absolute;\n}\n.back-inner-button{}", CustomCss: ".back-button {\n top: 65px;\n left: 15px;\n position: absolute;\n}\n.back-inner-button{}",
Placeholder: "", Placeholder: "",
Rule: "None", Rule: "None",
} }
@@ -217,7 +218,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{ signinItem = &SigninItem{
Name: "Languages", Name: "Languages",
Visible: true, Visible: true,
Label: ".login-languages {\n top: 55px;\n right: 5px;\n position: absolute;\n}", CustomCss: ".login-languages {\n top: 55px;\n right: 5px;\n position: absolute;\n}",
Placeholder: "", Placeholder: "",
Rule: "None", Rule: "None",
} }
@@ -225,7 +226,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{ signinItem = &SigninItem{
Name: "Logo", Name: "Logo",
Visible: true, Visible: true,
Label: ".login-logo-box {}", CustomCss: ".login-logo-box {}",
Placeholder: "", Placeholder: "",
Rule: "None", Rule: "None",
} }
@@ -233,7 +234,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{ signinItem = &SigninItem{
Name: "Signin methods", Name: "Signin methods",
Visible: true, Visible: true,
Label: ".signin-methods {}", CustomCss: ".signin-methods {}",
Placeholder: "", Placeholder: "",
Rule: "None", Rule: "None",
} }
@@ -241,7 +242,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{ signinItem = &SigninItem{
Name: "Username", Name: "Username",
Visible: true, Visible: true,
Label: ".login-username {}\n.login-username-input{}", CustomCss: ".login-username {}\n.login-username-input{}",
Placeholder: "", Placeholder: "",
Rule: "None", Rule: "None",
} }
@@ -249,7 +250,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{ signinItem = &SigninItem{
Name: "Password", Name: "Password",
Visible: true, Visible: true,
Label: ".login-password {}\n.login-password-input{}", CustomCss: ".login-password {}\n.login-password-input{}",
Placeholder: "", Placeholder: "",
Rule: "None", Rule: "None",
} }
@@ -257,7 +258,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{ signinItem = &SigninItem{
Name: "Agreement", Name: "Agreement",
Visible: true, Visible: true,
Label: ".login-agreement {}", CustomCss: ".login-agreement {}",
Placeholder: "", Placeholder: "",
Rule: "None", Rule: "None",
} }
@@ -265,7 +266,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{ signinItem = &SigninItem{
Name: "Forgot password?", Name: "Forgot password?",
Visible: true, Visible: true,
Label: ".login-forget-password {\n display: inline-flex;\n justify-content: space-between;\n width: 320px;\n margin-bottom: 25px;\n}", CustomCss: ".login-forget-password {\n display: inline-flex;\n justify-content: space-between;\n width: 320px;\n margin-bottom: 25px;\n}",
Placeholder: "", Placeholder: "",
Rule: "None", Rule: "None",
} }
@@ -273,7 +274,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{ signinItem = &SigninItem{
Name: "Login button", Name: "Login button",
Visible: true, Visible: true,
Label: ".login-button-box {\n margin-bottom: 5px;\n}\n.login-button {\n width: 100%;\n}", CustomCss: ".login-button-box {\n margin-bottom: 5px;\n}\n.login-button {\n width: 100%;\n}",
Placeholder: "", Placeholder: "",
Rule: "None", Rule: "None",
} }
@@ -281,7 +282,7 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{ signinItem = &SigninItem{
Name: "Signup link", Name: "Signup link",
Visible: true, Visible: true,
Label: ".login-signup-link {\n margin-bottom: 24px;\n display: flex;\n justify-content: end;\n}", CustomCss: ".login-signup-link {\n margin-bottom: 24px;\n display: flex;\n justify-content: end;\n}",
Placeholder: "", Placeholder: "",
Rule: "None", Rule: "None",
} }
@@ -289,12 +290,18 @@ func extendApplicationWithSigninItems(application *Application) (err error) {
signinItem = &SigninItem{ signinItem = &SigninItem{
Name: "Providers", Name: "Providers",
Visible: true, Visible: true,
Label: ".provider-img {\n width: 30px;\n margin: 5px;\n}\n.provider-big-img {\n margin-bottom: 10px;\n}", CustomCss: ".provider-img {\n width: 30px;\n margin: 5px;\n}\n.provider-big-img {\n margin-bottom: 10px;\n}",
Placeholder: "", Placeholder: "",
Rule: "None", Rule: "None",
} }
application.SigninItems = append(application.SigninItems, signinItem) application.SigninItems = append(application.SigninItems, signinItem)
} }
for idx, item := range application.SigninItems {
if item.Label != "" && item.CustomCss == "" {
application.SigninItems[idx].CustomCss = item.Label
application.SigninItems[idx].Label = ""
}
}
return return
} }
@@ -670,11 +677,7 @@ func AddApplication(application *Application) (bool, error) {
return affected != 0, nil return affected != 0, nil
} }
func DeleteApplication(application *Application) (bool, error) { func deleteApplication(application *Application) (bool, error) {
if application.Name == "app-built-in" {
return false, nil
}
affected, err := ormer.Engine.ID(core.PK{application.Owner, application.Name}).Delete(&Application{}) affected, err := ormer.Engine.ID(core.PK{application.Owner, application.Name}).Delete(&Application{})
if err != nil { if err != nil {
return false, err return false, err
@@ -683,6 +686,14 @@ func DeleteApplication(application *Application) (bool, error) {
return affected != 0, nil return affected != 0, nil
} }
func DeleteApplication(application *Application) (bool, error) {
if application.Name == "app-built-in" {
return false, nil
}
return deleteApplication(application)
}
func (application *Application) GetId() string { func (application *Application) GetId() string {
return fmt.Sprintf("%s/%s", application.Owner, application.Name) return fmt.Sprintf("%s/%s", application.Owner, application.Name)
} }

View File

@@ -38,7 +38,20 @@ func (application *Application) GetProviderByCategory(category string) (*Provide
return nil, nil return nil, nil
} }
func (application *Application) GetProviderByCategoryAndRule(category string, method string) (*Provider, error) { func isProviderItemCountryCodeMatched(providerItem *ProviderItem, countryCode string) bool {
if len(providerItem.CountryCodes) == 0 {
return true
}
for _, countryCode2 := range providerItem.CountryCodes {
if countryCode2 == "" || countryCode2 == "All" || countryCode2 == "all" || countryCode2 == countryCode {
return true
}
}
return false
}
func (application *Application) GetProviderByCategoryAndRule(category string, method string, countryCode string) (*Provider, error) {
providers, err := GetProviders(application.Organization) providers, err := GetProviders(application.Organization)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -54,7 +67,13 @@ func (application *Application) GetProviderByCategoryAndRule(category string, me
} }
for _, providerItem := range application.Providers { for _, providerItem := range application.Providers {
if providerItem.Rule == method || (providerItem.Rule == "all" || providerItem.Rule == "" || providerItem.Rule == "None") { if providerItem.Provider != nil && providerItem.Provider.Category == "SMS" {
if !isProviderItemCountryCodeMatched(providerItem, countryCode) {
continue
}
}
if providerItem.Rule == method || providerItem.Rule == "" || providerItem.Rule == "All" || providerItem.Rule == "all" || providerItem.Rule == "None" {
if provider, ok := m[providerItem.Name]; ok { if provider, ok := m[providerItem.Name]; ok {
return provider, nil return provider, nil
} }
@@ -65,11 +84,11 @@ func (application *Application) GetProviderByCategoryAndRule(category string, me
} }
func (application *Application) GetEmailProvider(method string) (*Provider, error) { func (application *Application) GetEmailProvider(method string) (*Provider, error) {
return application.GetProviderByCategoryAndRule("Email", method) return application.GetProviderByCategoryAndRule("Email", method, "All")
} }
func (application *Application) GetSmsProvider(method string) (*Provider, error) { func (application *Application) GetSmsProvider(method string, countryCode string) (*Provider, error) {
return application.GetProviderByCategoryAndRule("SMS", method) return application.GetProviderByCategoryAndRule("SMS", method, countryCode)
} }
func (application *Application) GetStorageProvider() (*Provider, error) { func (application *Application) GetStorageProvider() (*Provider, error) {

View File

@@ -154,6 +154,15 @@ func AddGroups(groups []*Group) (bool, error) {
return affected != 0, nil return affected != 0, nil
} }
func deleteGroup(group *Group) (bool, error) {
affected, err := ormer.Engine.ID(core.PK{group.Owner, group.Name}).Delete(&Group{})
if err != nil {
return false, err
}
return affected != 0, nil
}
func DeleteGroup(group *Group) (bool, error) { func DeleteGroup(group *Group) (bool, error) {
_, err := ormer.Engine.Get(group) _, err := ormer.Engine.Get(group)
if err != nil { if err != nil {
@@ -172,12 +181,7 @@ func DeleteGroup(group *Group) (bool, error) {
return false, errors.New("group has users") return false, errors.New("group has users")
} }
affected, err := ormer.Engine.ID(core.PK{group.Owner, group.Name}).Delete(&Group{}) return deleteGroup(group)
if err != nil {
return false, err
}
return affected != 0, nil
} }
func checkGroupName(name string) error { func checkGroupName(name string) error {

View File

@@ -409,7 +409,7 @@ func initBuiltInPermission() {
Groups: []string{}, Groups: []string{},
Roles: []string{}, Roles: []string{},
Domains: []string{}, Domains: []string{},
Model: "model-built-in", Model: "user-model-built-in",
Adapter: "", Adapter: "",
ResourceType: "Application", ResourceType: "Application",
Resources: []string{"app-built-in"}, Resources: []string{"app-built-in"},

View File

@@ -266,7 +266,13 @@ func initDefinedOrganization(organization *Organization) {
} }
if existed != nil { if existed != nil {
return affected, err := deleteOrganization(organization)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete organization")
}
} }
organization.CreatedTime = util.GetCurrentTime() organization.CreatedTime = util.GetCurrentTime()
organization.AccountItems = getBuiltInAccountItems() organization.AccountItems = getBuiltInAccountItems()
@@ -284,7 +290,13 @@ func initDefinedApplication(application *Application) {
} }
if existed != nil { if existed != nil {
return affected, err := deleteApplication(application)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete application")
}
} }
application.CreatedTime = util.GetCurrentTime() application.CreatedTime = util.GetCurrentTime()
_, err = AddApplication(application) _, err = AddApplication(application)
@@ -299,7 +311,13 @@ func initDefinedUser(user *User) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
return affected, err := deleteUser(user)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete user")
}
} }
user.CreatedTime = util.GetCurrentTime() user.CreatedTime = util.GetCurrentTime()
user.Id = util.GenerateId() user.Id = util.GenerateId()
@@ -319,7 +337,13 @@ func initDefinedCert(cert *Cert) {
} }
if existed != nil { if existed != nil {
return affected, err := DeleteCert(cert)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete cert")
}
} }
cert.CreatedTime = util.GetCurrentTime() cert.CreatedTime = util.GetCurrentTime()
_, err = AddCert(cert) _, err = AddCert(cert)
@@ -335,7 +359,13 @@ func initDefinedLdap(ldap *Ldap) {
} }
if existed != nil { if existed != nil {
return affected, err := DeleteLdap(ldap)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete ldap")
}
} }
_, err = AddLdap(ldap) _, err = AddLdap(ldap)
if err != nil { if err != nil {
@@ -350,7 +380,13 @@ func initDefinedProvider(provider *Provider) {
} }
if existed != nil { if existed != nil {
return affected, err := DeleteProvider(provider)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete provider")
}
} }
_, err = AddProvider(provider) _, err = AddProvider(provider)
if err != nil { if err != nil {
@@ -365,7 +401,13 @@ func initDefinedModel(model *Model) {
} }
if existed != nil { if existed != nil {
return affected, err := DeleteModel(model)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete provider")
}
} }
model.CreatedTime = util.GetCurrentTime() model.CreatedTime = util.GetCurrentTime()
_, err = AddModel(model) _, err = AddModel(model)
@@ -381,7 +423,13 @@ func initDefinedPermission(permission *Permission) {
} }
if existed != nil { if existed != nil {
return affected, err := deletePermission(permission)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete permission")
}
} }
permission.CreatedTime = util.GetCurrentTime() permission.CreatedTime = util.GetCurrentTime()
_, err = AddPermission(permission) _, err = AddPermission(permission)
@@ -397,7 +445,13 @@ func initDefinedPayment(payment *Payment) {
} }
if existed != nil { if existed != nil {
return affected, err := DeletePayment(payment)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete payment")
}
} }
payment.CreatedTime = util.GetCurrentTime() payment.CreatedTime = util.GetCurrentTime()
_, err = AddPayment(payment) _, err = AddPayment(payment)
@@ -413,7 +467,13 @@ func initDefinedProduct(product *Product) {
} }
if existed != nil { if existed != nil {
return affected, err := DeleteProduct(product)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete product")
}
} }
product.CreatedTime = util.GetCurrentTime() product.CreatedTime = util.GetCurrentTime()
_, err = AddProduct(product) _, err = AddProduct(product)
@@ -429,7 +489,13 @@ func initDefinedResource(resource *Resource) {
} }
if existed != nil { if existed != nil {
return affected, err := DeleteResource(resource)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete resource")
}
} }
resource.CreatedTime = util.GetCurrentTime() resource.CreatedTime = util.GetCurrentTime()
_, err = AddResource(resource) _, err = AddResource(resource)
@@ -445,7 +511,13 @@ func initDefinedRole(role *Role) {
} }
if existed != nil { if existed != nil {
return affected, err := deleteRole(role)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete role")
}
} }
role.CreatedTime = util.GetCurrentTime() role.CreatedTime = util.GetCurrentTime()
_, err = AddRole(role) _, err = AddRole(role)
@@ -461,7 +533,13 @@ func initDefinedSyncer(syncer *Syncer) {
} }
if existed != nil { if existed != nil {
return affected, err := DeleteSyncer(syncer)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete role")
}
} }
syncer.CreatedTime = util.GetCurrentTime() syncer.CreatedTime = util.GetCurrentTime()
_, err = AddSyncer(syncer) _, err = AddSyncer(syncer)
@@ -477,7 +555,13 @@ func initDefinedToken(token *Token) {
} }
if existed != nil { if existed != nil {
return affected, err := DeleteToken(token)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete token")
}
} }
token.CreatedTime = util.GetCurrentTime() token.CreatedTime = util.GetCurrentTime()
_, err = AddToken(token) _, err = AddToken(token)
@@ -493,7 +577,13 @@ func initDefinedWebhook(webhook *Webhook) {
} }
if existed != nil { if existed != nil {
return affected, err := DeleteWebhook(webhook)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete webhook")
}
} }
webhook.CreatedTime = util.GetCurrentTime() webhook.CreatedTime = util.GetCurrentTime()
_, err = AddWebhook(webhook) _, err = AddWebhook(webhook)
@@ -508,7 +598,13 @@ func initDefinedGroup(group *Group) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
return affected, err := deleteGroup(group)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete group")
}
} }
group.CreatedTime = util.GetCurrentTime() group.CreatedTime = util.GetCurrentTime()
_, err = AddGroup(group) _, err = AddGroup(group)
@@ -523,7 +619,13 @@ func initDefinedAdapter(adapter *Adapter) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
return affected, err := DeleteAdapter(adapter)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete adapter")
}
} }
adapter.CreatedTime = util.GetCurrentTime() adapter.CreatedTime = util.GetCurrentTime()
_, err = AddAdapter(adapter) _, err = AddAdapter(adapter)
@@ -538,7 +640,13 @@ func initDefinedEnforcer(enforcer *Enforcer) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
return affected, err := DeleteEnforcer(enforcer)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete enforcer")
}
} }
enforcer.CreatedTime = util.GetCurrentTime() enforcer.CreatedTime = util.GetCurrentTime()
_, err = AddEnforcer(enforcer) _, err = AddEnforcer(enforcer)
@@ -553,7 +661,13 @@ func initDefinedPlan(plan *Plan) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
return affected, err := DeletePlan(plan)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete plan")
}
} }
plan.CreatedTime = util.GetCurrentTime() plan.CreatedTime = util.GetCurrentTime()
_, err = AddPlan(plan) _, err = AddPlan(plan)
@@ -563,12 +677,18 @@ func initDefinedPlan(plan *Plan) {
} }
func initDefinedPricing(pricing *Pricing) { func initDefinedPricing(pricing *Pricing) {
existed, err := getPlan(pricing.Owner, pricing.Name) existed, err := getPricing(pricing.Owner, pricing.Name)
if err != nil { if err != nil {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
return affected, err := DeletePricing(pricing)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete pricing")
}
} }
pricing.CreatedTime = util.GetCurrentTime() pricing.CreatedTime = util.GetCurrentTime()
_, err = AddPricing(pricing) _, err = AddPricing(pricing)
@@ -583,7 +703,13 @@ func initDefinedInvitation(invitation *Invitation) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
return affected, err := DeleteInvitation(invitation)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete invitation")
}
} }
invitation.CreatedTime = util.GetCurrentTime() invitation.CreatedTime = util.GetCurrentTime()
_, err = AddInvitation(invitation, "en") _, err = AddInvitation(invitation, "en")
@@ -593,6 +719,7 @@ func initDefinedInvitation(invitation *Invitation) {
} }
func initDefinedRecord(record *casvisorsdk.Record) { func initDefinedRecord(record *casvisorsdk.Record) {
record.Id = 0
record.CreatedTime = util.GetCurrentTime() record.CreatedTime = util.GetCurrentTime()
_ = AddRecord(record) _ = AddRecord(record)
} }
@@ -611,7 +738,13 @@ func initDefinedSubscription(subscription *Subscription) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
return affected, err := DeleteSubscription(subscription)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete subscription")
}
} }
subscription.CreatedTime = util.GetCurrentTime() subscription.CreatedTime = util.GetCurrentTime()
_, err = AddSubscription(subscription) _, err = AddSubscription(subscription)
@@ -626,7 +759,13 @@ func initDefinedTransaction(transaction *Transaction) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
return affected, err := DeleteTransaction(transaction)
if err != nil {
panic(err)
}
if !affected {
panic("Fail to delete transaction")
}
} }
transaction.CreatedTime = util.GetCurrentTime() transaction.CreatedTime = util.GetCurrentTime()
_, err = AddTransaction(transaction) _, err = AddTransaction(transaction)

View File

@@ -241,11 +241,7 @@ func AddOrganization(organization *Organization) (bool, error) {
return affected != 0, nil return affected != 0, nil
} }
func DeleteOrganization(organization *Organization) (bool, error) { func deleteOrganization(organization *Organization) (bool, error) {
if organization.Name == "built-in" {
return false, nil
}
affected, err := ormer.Engine.ID(core.PK{organization.Owner, organization.Name}).Delete(&Organization{}) affected, err := ormer.Engine.ID(core.PK{organization.Owner, organization.Name}).Delete(&Organization{})
if err != nil { if err != nil {
return false, err return false, err
@@ -254,6 +250,14 @@ func DeleteOrganization(organization *Organization) (bool, error) {
return affected != 0, nil return affected != 0, nil
} }
func DeleteOrganization(organization *Organization) (bool, error) {
if organization.Name == "built-in" {
return false, nil
}
return deleteOrganization(organization)
}
func GetOrganizationByUser(user *User) (*Organization, error) { func GetOrganizationByUser(user *User) (*Organization, error) {
if user == nil { if user == nil {
return nil, nil return nil, nil

View File

@@ -286,13 +286,22 @@ func AddPermissionsInBatch(permissions []*Permission) (bool, error) {
return affected, nil return affected, nil
} }
func DeletePermission(permission *Permission) (bool, error) { func deletePermission(permission *Permission) (bool, error) {
affected, err := ormer.Engine.ID(core.PK{permission.Owner, permission.Name}).Delete(&Permission{}) affected, err := ormer.Engine.ID(core.PK{permission.Owner, permission.Name}).Delete(&Permission{})
if err != nil { if err != nil {
return false, err return false, err
} }
if affected != 0 { return affected != 0, nil
}
func DeletePermission(permission *Permission) (bool, error) {
affected, err := deletePermission(permission)
if err != nil {
return false, err
}
if affected {
err = removeGroupingPolicies(permission) err = removeGroupingPolicies(permission)
if err != nil { if err != nil {
return false, err return false, err
@@ -314,7 +323,7 @@ func DeletePermission(permission *Permission) (bool, error) {
} }
} }
return affected != 0, nil return affected, nil
} }
func getPermissionsByUser(userId string) ([]*Permission, error) { func getPermissionsByUser(userId string) ([]*Permission, error) {

View File

@@ -133,7 +133,7 @@ func AddPlan(plan *Plan) (bool, error) {
} }
func DeletePlan(plan *Plan) (bool, error) { func DeletePlan(plan *Plan) (bool, error) {
affected, err := ormer.Engine.ID(core.PK{plan.Owner, plan.Name}).Delete(plan) affected, err := ormer.Engine.ID(core.PK{plan.Owner, plan.Name}).Delete(Plan{})
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@@ -140,7 +140,7 @@ func AddPricing(pricing *Pricing) (bool, error) {
} }
func DeletePricing(pricing *Pricing) (bool, error) { func DeletePricing(pricing *Pricing) (bool, error) {
affected, err := ormer.Engine.ID(core.PK{pricing.Owner, pricing.Name}).Delete(pricing) affected, err := ormer.Engine.ID(core.PK{pricing.Owner, pricing.Name}).Delete(Pricing{})
if err != nil { if err != nil {
return false, err return false, err
} }

View File

@@ -50,7 +50,7 @@ type Provider struct {
Host string `xorm:"varchar(100)" json:"host"` Host string `xorm:"varchar(100)" json:"host"`
Port int `json:"port"` Port int `json:"port"`
DisableSsl bool `json:"disableSsl"` // If the provider type is WeChat, DisableSsl means EnableQRCode DisableSsl bool `json:"disableSsl"` // If the provider type is WeChat, DisableSsl means EnableQRCode, if type is Google, it means sync phone number
Title string `xorm:"varchar(100)" json:"title"` Title string `xorm:"varchar(100)" json:"title"`
Content string `xorm:"varchar(2000)" json:"content"` // If provider type is WeChat, Content means QRCode string by Base64 encoding Content string `xorm:"varchar(2000)" json:"content"` // If provider type is WeChat, Content means QRCode string by Base64 encoding
Receiver string `xorm:"varchar(100)" json:"receiver"` Receiver string `xorm:"varchar(100)" json:"receiver"`

View File

@@ -18,13 +18,14 @@ type ProviderItem struct {
Owner string `json:"owner"` Owner string `json:"owner"`
Name string `json:"name"` Name string `json:"name"`
CanSignUp bool `json:"canSignUp"` CanSignUp bool `json:"canSignUp"`
CanSignIn bool `json:"canSignIn"` CanSignIn bool `json:"canSignIn"`
CanUnlink bool `json:"canUnlink"` CanUnlink bool `json:"canUnlink"`
Prompted bool `json:"prompted"` CountryCodes []string `json:"countryCodes"`
SignupGroup string `json:"signupGroup"` Prompted bool `json:"prompted"`
Rule string `json:"rule"` SignupGroup string `json:"signupGroup"`
Provider *Provider `json:"provider"` Rule string `json:"rule"`
Provider *Provider `json:"provider"`
} }
func (application *Application) GetProviderItem(providerName string) *ProviderItem { func (application *Application) GetProviderItem(providerName string) *ProviderItem {

View File

@@ -17,6 +17,7 @@ package object
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"regexp"
"strings" "strings"
"github.com/beego/beego/context" "github.com/beego/beego/context"
@@ -25,10 +26,14 @@ import (
"github.com/casvisor/casvisor-go-sdk/casvisorsdk" "github.com/casvisor/casvisor-go-sdk/casvisorsdk"
) )
var logPostOnly bool var (
logPostOnly bool
passwordRegex *regexp.Regexp
)
func init() { func init() {
logPostOnly = conf.GetConfigBool("logPostOnly") logPostOnly = conf.GetConfigBool("logPostOnly")
passwordRegex = regexp.MustCompile("\"password\":\".+\"")
} }
type Record struct { type Record struct {
@@ -40,6 +45,10 @@ type Response struct {
Msg string `json:"msg"` Msg string `json:"msg"`
} }
func maskPassword(recordString string) string {
return passwordRegex.ReplaceAllString(recordString, "\"password\":\"***\"")
}
func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) { func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1) ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1)
action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1) action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1)
@@ -51,6 +60,7 @@ func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
object := "" object := ""
if ctx.Input.RequestBody != nil && len(ctx.Input.RequestBody) != 0 { if ctx.Input.RequestBody != nil && len(ctx.Input.RequestBody) != 0 {
object = string(ctx.Input.RequestBody) object = string(ctx.Input.RequestBody)
object = maskPassword(object)
} }
respBytes, err := json.Marshal(ctx.Input.Data()["json"]) respBytes, err := json.Marshal(ctx.Input.Data()["json"])
@@ -80,12 +90,18 @@ func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
Action: action, Action: action,
Language: languageCode, Language: languageCode,
Object: object, Object: object,
StatusCode: 200,
Response: fmt.Sprintf("{status:\"%s\", msg:\"%s\"}", resp.Status, resp.Msg), Response: fmt.Sprintf("{status:\"%s\", msg:\"%s\"}", resp.Status, resp.Msg),
IsTriggered: false, IsTriggered: false,
} }
return &record, nil return &record, nil
} }
func addRecord(record *casvisorsdk.Record) (int64, error) {
affected, err := ormer.Engine.Insert(record)
return affected, err
}
func AddRecord(record *casvisorsdk.Record) bool { func AddRecord(record *casvisorsdk.Record) bool {
if logPostOnly { if logPostOnly {
if record.Method == "GET" { if record.Method == "GET" {
@@ -98,6 +114,7 @@ func AddRecord(record *casvisorsdk.Record) bool {
} }
record.Owner = record.Organization record.Owner = record.Organization
record.Object = maskPassword(record.Object)
errWebhook := SendWebhooks(record) errWebhook := SendWebhooks(record)
if errWebhook == nil { if errWebhook == nil {
@@ -107,7 +124,7 @@ func AddRecord(record *casvisorsdk.Record) bool {
} }
if casvisorsdk.GetClient() == nil { if casvisorsdk.GetClient() == nil {
affected, err := ormer.Engine.Insert(record) affected, err := addRecord(record)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -212,6 +229,40 @@ func getFilteredWebhooks(webhooks []*Webhook, organization string, action string
return res return res
} }
func addWebhookRecord(webhook *Webhook, record *casvisorsdk.Record, statusCode int, respBody string, sendError error) error {
if statusCode == 200 {
return nil
}
if len(respBody) > 300 {
respBody = respBody[0:300]
}
webhookRecord := &casvisorsdk.Record{
Owner: record.Owner,
Name: util.GenerateId(),
CreatedTime: util.GetCurrentTime(),
Organization: record.Organization,
User: record.User,
Method: webhook.Method,
Action: "send-webhook",
RequestUri: webhook.Url,
StatusCode: statusCode,
Response: respBody,
Language: record.Language,
IsTriggered: false,
}
if sendError != nil {
webhookRecord.Response = sendError.Error()
}
_, err := addRecord(webhookRecord)
return err
}
func SendWebhooks(record *casvisorsdk.Record) error { func SendWebhooks(record *casvisorsdk.Record) error {
webhooks, err := getWebhooksByOrganization("") webhooks, err := getWebhooksByOrganization("")
if err != nil { if err != nil {
@@ -236,11 +287,16 @@ func SendWebhooks(record *casvisorsdk.Record) error {
} }
} }
err = sendWebhook(webhook, record, user) statusCode, respBody, err := sendWebhook(webhook, record, user)
if err != nil { if err != nil {
errs = append(errs, err) errs = append(errs, err)
continue
} }
err = addWebhookRecord(webhook, record, statusCode, respBody, err)
if err != nil {
errs = append(errs, err)
}
} }
if len(errs) > 0 { if len(errs) > 0 {

View File

@@ -238,6 +238,15 @@ func AddRolesInBatch(roles []*Role) bool {
return affected return affected
} }
func deleteRole(role *Role) (bool, error) {
affected, err := ormer.Engine.ID(core.PK{role.Owner, role.Name}).Delete(&Role{})
if err != nil {
return false, err
}
return affected != 0, nil
}
func DeleteRole(role *Role) (bool, error) { func DeleteRole(role *Role) (bool, error) {
roleId := role.GetId() roleId := role.GetId()
permissions, err := GetPermissionsByRole(roleId) permissions, err := GetPermissionsByRole(roleId)
@@ -253,12 +262,7 @@ func DeleteRole(role *Role) (bool, error) {
} }
} }
affected, err := ormer.Engine.ID(core.PK{role.Owner, role.Name}).Delete(&Role{}) return deleteRole(role)
if err != nil {
return false, err
}
return affected != 0, nil
} }
func (role *Role) GetId() string { func (role *Role) GetId() string {

View File

@@ -155,7 +155,8 @@ func GetMaskedSyncers(syncers []*Syncer, errs ...error) ([]*Syncer, error) {
func UpdateSyncer(id string, syncer *Syncer) (bool, error) { func UpdateSyncer(id string, syncer *Syncer) (bool, error) {
owner, name := util.GetOwnerAndNameFromId(id) owner, name := util.GetOwnerAndNameFromId(id)
if s, err := getSyncer(owner, name); err != nil { s, err := getSyncer(owner, name)
if err != nil {
return false, err return false, err
} else if s == nil { } else if s == nil {
return false, nil return false, nil
@@ -163,7 +164,7 @@ func UpdateSyncer(id string, syncer *Syncer) (bool, error) {
session := ormer.Engine.ID(core.PK{owner, name}).AllCols() session := ormer.Engine.ID(core.PK{owner, name}).AllCols()
if syncer.Password == "***" { if syncer.Password == "***" {
session.Omit("password") syncer.Password = s.Password
} }
affected, err := session.Update(syncer) affected, err := session.Update(syncer)
if err != nil { if err != nil {

View File

@@ -142,9 +142,11 @@ func (syncer *Syncer) syncUsers() error {
} }
} }
_, err = AddUsersInBatch(newUsers) if len(newUsers) != 0 {
if err != nil { _, err = AddUsersInBatch(newUsers)
return err if err != nil {
return err
}
} }
if !syncer.IsReadOnly { if !syncer.IsReadOnly {

View File

@@ -169,6 +169,12 @@ func (syncer *Syncer) setUserByKeyValue(user *User, key string, value string) {
user.TotpSecret = value user.TotpSecret = value
case "SignupApplication": case "SignupApplication":
user.SignupApplication = value user.SignupApplication = value
case "MfaPhoneEnabled":
user.MfaPhoneEnabled = util.ParseBool(value)
case "MfaEmailEnabled":
user.MfaEmailEnabled = util.ParseBool(value)
case "RecoveryCodes":
user.RecoveryCodes = strings.Split(value, ",")
} }
} }
@@ -303,6 +309,9 @@ func (syncer *Syncer) getMapFromOriginalUser(user *OriginalUser) map[string]stri
m["PreferredMfaType"] = user.PreferredMfaType m["PreferredMfaType"] = user.PreferredMfaType
m["TotpSecret"] = user.TotpSecret m["TotpSecret"] = user.TotpSecret
m["SignupApplication"] = user.SignupApplication m["SignupApplication"] = user.SignupApplication
m["MfaPhoneEnabled"] = util.BoolToString(user.MfaPhoneEnabled)
m["MfaEmailEnabled"] = util.BoolToString(user.MfaEmailEnabled)
m["RecoveryCodes"] = strings.Join(user.RecoveryCodes, ",")
m2 := map[string]string{} m2 := map[string]string{}
for _, tableColumn := range syncer.TableColumns { for _, tableColumn := range syncer.TableColumns {

View File

@@ -203,7 +203,8 @@ type User struct {
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"` LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"` SigninWrongTimes int `json:"signinWrongTimes"`
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"` ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
NeedUpdatePassword bool `json:"needUpdatePassword"`
} }
type Userinfo struct { type Userinfo struct {
@@ -682,7 +683,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup", "eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud", "microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
"spotify", "strava", "stripe", "type", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo", "spotify", "strava", "stripe", "type", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo",
"yammer", "yandex", "zoom", "custom", "yammer", "yandex", "zoom", "custom", "need_update_password",
} }
} }
if isAdmin { if isAdmin {
@@ -877,6 +878,7 @@ func AddUsers(users []*User) (bool, error) {
} }
} }
user.Name = strings.TrimSpace(user.Name)
if isUsernameLowered { if isUsernameLowered {
user.Name = strings.ToLower(user.Name) user.Name = strings.ToLower(user.Name)
} }
@@ -919,6 +921,15 @@ func AddUsersInBatch(users []*User) (bool, error) {
return affected, nil return affected, nil
} }
func deleteUser(user *User) (bool, error) {
affected, err := ormer.Engine.ID(core.PK{user.Owner, user.Name}).Delete(&User{})
if err != nil {
return false, err
}
return affected != 0, nil
}
func DeleteUser(user *User) (bool, error) { func DeleteUser(user *User) (bool, error) {
// Forced offline the user first // Forced offline the user first
_, err := DeleteSession(util.GetSessionId(user.Owner, user.Name, CasdoorApplication)) _, err := DeleteSession(util.GetSessionId(user.Owner, user.Name, CasdoorApplication))
@@ -926,12 +937,7 @@ func DeleteUser(user *User) (bool, error) {
return false, err return false, err
} }
affected, err := ormer.Engine.ID(core.PK{user.Owner, user.Name}).Delete(&User{}) return deleteUser(user)
if err != nil {
return false, err
}
return affected != 0, nil
} }
func GetUserInfo(user *User, scope string, aud string, host string) (*Userinfo, error) { func GetUserInfo(user *User, scope string, aud string, host string) (*Userinfo, error) {

View File

@@ -21,12 +21,11 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n" "github.com/casdoor/casdoor/i18n"
jsoniter "github.com/json-iterator/go"
"github.com/casdoor/casdoor/idp" "github.com/casdoor/casdoor/idp"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
jsoniter "github.com/json-iterator/go"
"github.com/xorm-io/core" "github.com/xorm-io/core"
) )
@@ -57,6 +56,13 @@ func HasUserByField(organizationName string, field string, value string) bool {
} }
func GetUserByFields(organization string, field string) (*User, error) { func GetUserByFields(organization string, field string) (*User, error) {
isUsernameLowered := conf.GetConfigBool("isUsernameLowered")
if isUsernameLowered {
field = strings.ToLower(field)
}
field = strings.TrimSpace(field)
// check username // check username
user, err := GetUserByField(organization, "name", field) user, err := GetUserByField(organization, "name", field)
if err != nil || user != nil { if err != nil || user != nil {
@@ -405,6 +411,10 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
item := GetAccountItemByName("Is deleted", organization) item := GetAccountItemByName("Is deleted", organization)
itemsChanged = append(itemsChanged, item) itemsChanged = append(itemsChanged, item)
} }
if oldUser.NeedUpdatePassword != newUser.NeedUpdatePassword {
item := GetAccountItemByName("Need update password", organization)
itemsChanged = append(itemsChanged, item)
}
if oldUser.Score != newUser.Score { if oldUser.Score != newUser.Score {
item := GetAccountItemByName("Score", organization) item := GetAccountItemByName("Score", organization)

View File

@@ -15,6 +15,7 @@
package object package object
import ( import (
"io"
"net/http" "net/http"
"strings" "strings"
@@ -22,7 +23,7 @@ import (
"github.com/casvisor/casvisor-go-sdk/casvisorsdk" "github.com/casvisor/casvisor-go-sdk/casvisorsdk"
) )
func sendWebhook(webhook *Webhook, record *casvisorsdk.Record, extendedUser *User) error { func sendWebhook(webhook *Webhook, record *casvisorsdk.Record, extendedUser *User) (int, string, error) {
client := &http.Client{} client := &http.Client{}
type RecordEx struct { type RecordEx struct {
@@ -38,7 +39,7 @@ func sendWebhook(webhook *Webhook, record *casvisorsdk.Record, extendedUser *Use
req, err := http.NewRequest(webhook.Method, webhook.Url, body) req, err := http.NewRequest(webhook.Method, webhook.Url, body)
if err != nil { if err != nil {
return err return 0, "", err
} }
req.Header.Set("Content-Type", webhook.ContentType) req.Header.Set("Content-Type", webhook.ContentType)
@@ -47,6 +48,15 @@ func sendWebhook(webhook *Webhook, record *casvisorsdk.Record, extendedUser *Use
req.Header.Set(header.Name, header.Value) req.Header.Set(header.Name, header.Value)
} }
_, err = client.Do(req) resp, err := client.Do(req)
return err if err != nil {
return 0, "", err
}
defer resp.Body.Close()
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return 0, "", err
}
return resp.StatusCode, string(bodyBytes), err
} }

View File

@@ -59,7 +59,7 @@ func fastAutoSignin(ctx *context.Context) (string, error) {
scope := ctx.Input.Query("scope") scope := ctx.Input.Query("scope")
state := ctx.Input.Query("state") state := ctx.Input.Query("state")
nonce := "" nonce := ""
codeChallenge := "" codeChallenge := ctx.Input.Query("code_challenge")
if clientId == "" || responseType != "code" || redirectUri == "" { if clientId == "" || responseType != "code" || redirectUri == "" {
return "", nil return "", nil
} }

View File

@@ -414,6 +414,7 @@ class App extends Component {
<Layout id="parent-area"> <Layout id="parent-area">
<ManagementPage <ManagementPage
account={this.state.account} account={this.state.account}
application={this.state.application}
uri={this.state.uri} uri={this.state.uri}
themeData={this.state.themeData} themeData={this.state.themeData}
themeAlgorithm={this.state.themeAlgorithm} themeAlgorithm={this.state.themeAlgorithm}

View File

@@ -108,8 +108,8 @@ class EntryPage extends React.Component {
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} /> <Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} /> <Route exact path="/login/oauth/authorize" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"code"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"saml"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} /> <Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage {...this.props} application={this.state.application} type={"saml"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/forget" render={(props) => <SelfForgetPage {...this.props} account={this.props.account} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/forget/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ForgetPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/forget/:applicationName" render={(props) => <ForgetPage {...this.props} account={this.props.account} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/prompt" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/prompt/:applicationName" render={(props) => this.renderLoginIfNotLoggedIn(<PromptPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
<Route exact path="/result" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} /> <Route exact path="/result" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />

View File

@@ -328,6 +328,8 @@ function ManagementPage(props) {
return <Redirect to="/login" />; return <Redirect to="/login" />;
} else if (props.account === undefined) { } else if (props.account === undefined) {
return null; return null;
} else if (props.account.needUpdatePassword) {
return <Redirect to={"/forget/" + props.application.name} />;
} else { } else {
return component; return component;
} }
@@ -409,7 +411,7 @@ function ManagementPage(props) {
return Setting.isMobile() || window.location.pathname.startsWith("/trees"); return Setting.isMobile() || window.location.pathname.startsWith("/trees");
} }
const menuStyleRight = Setting.isAdminUser(props.account) && !Setting.isMobile() ? "calc(180px + 280px)" : "280px"; const menuStyleRight = Setting.isAdminUser(props.account) && !Setting.isMobile() ? "calc(180px + 280px)" : "320px";
const onClose = () => { const onClose = () => {
setMenuVisible(false); setMenuVisible(false);

View File

@@ -41,7 +41,7 @@ class ProductEditPage extends React.Component {
UNSAFE_componentWillMount() { UNSAFE_componentWillMount() {
this.getProduct(); this.getProduct();
this.getOrganizations(); this.getOrganizations();
this.getPaymentProviders(); this.getPaymentProviders(this.state.organizationName);
} }
getProduct() { getProduct() {
@@ -67,8 +67,8 @@ class ProductEditPage extends React.Component {
}); });
} }
getPaymentProviders() { getPaymentProviders(organizationName) {
ProviderBackend.getProviders(this.props.account.owner) ProviderBackend.getProviders(organizationName)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
this.setState({ this.setState({

View File

@@ -828,6 +828,20 @@ class ProviderEditPage extends React.Component {
</React.Fragment> </React.Fragment>
) )
} }
{
this.state.provider.type !== "Google" ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Get phone number"), i18next.t("provider:Get phone number - Tooltip"))} :
</Col>
<Col span={1} >
<Switch disabled={!this.state.provider.clientId} checked={this.state.provider.disableSsl} onChange={checked => {
this.updateProviderField("disableSsl", checked);
}} />
</Col>
</Row>
)
}
{ {
this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "AzureADB2C" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : ( this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "AzureADB2C" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >

View File

@@ -151,6 +151,14 @@ class RecordListPage extends BaseListPage {
sorter: true, sorter: true,
...this.getColumnSearchProps("language"), ...this.getColumnSearchProps("language"),
}, },
{
title: i18next.t("record:Status code"),
dataIndex: "statusCode",
key: "statusCode",
width: "90px",
sorter: true,
...this.getColumnSearchProps("statusCode"),
},
{ {
title: i18next.t("record:Response"), title: i18next.t("record:Response"),
dataIndex: "response", dataIndex: "response",

View File

@@ -1463,7 +1463,7 @@ export function getUserCommonFields() {
return ["Owner", "Name", "CreatedTime", "UpdatedTime", "DeletedTime", "Id", "Type", "Password", "PasswordSalt", "DisplayName", "FirstName", "LastName", "Avatar", "PermanentAvatar", return ["Owner", "Name", "CreatedTime", "UpdatedTime", "DeletedTime", "Id", "Type", "Password", "PasswordSalt", "DisplayName", "FirstName", "LastName", "Avatar", "PermanentAvatar",
"Email", "EmailVerified", "Phone", "Location", "Address", "Affiliation", "Title", "IdCardType", "IdCard", "Homepage", "Bio", "Tag", "Region", "Email", "EmailVerified", "Phone", "Location", "Address", "Affiliation", "Title", "IdCardType", "IdCard", "Homepage", "Bio", "Tag", "Region",
"Language", "Gender", "Birthday", "Education", "Score", "Ranking", "IsDefaultAvatar", "IsOnline", "IsAdmin", "IsForbidden", "IsDeleted", "CreatedIp", "Language", "Gender", "Birthday", "Education", "Score", "Ranking", "IsDefaultAvatar", "IsOnline", "IsAdmin", "IsForbidden", "IsDeleted", "CreatedIp",
"PreferredMfaType", "TotpSecret", "SignupApplication"]; "PreferredMfaType", "TotpSecret", "SignupApplication", "RecoveryCodes", "MfaPhoneEnabled", "MfaEmailEnabled"];
} }
export function getDefaultFooterContent() { export function getDefaultFooterContent() {

View File

@@ -202,7 +202,7 @@ class UserEditPage extends React.Component {
return value; return value;
} }
updateUserField(key, value) { updateUserField(key, value, idx) {
if (this.props.account === null) { if (this.props.account === null) {
return; return;
} }
@@ -210,7 +210,15 @@ class UserEditPage extends React.Component {
value = this.parseUserField(key, value); value = this.parseUserField(key, value);
const user = this.state.user; const user = this.state.user;
user[key] = value; if (key === "address") {
if (!user[key]) {
user[key] = ["", ""];
}
user[key][idx] = value;
} else {
user[key] = value;
}
this.setState({ this.setState({
user: user, user: user,
}); });
@@ -501,16 +509,33 @@ class UserEditPage extends React.Component {
); );
} else if (accountItem.name === "Address") { } else if (accountItem.name === "Address") {
return ( return (
<Row style={{marginTop: "20px"}} > <React.Fragment>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Row style={{marginTop: "20px"}} >
{Setting.getLabel(i18next.t("user:Address"), i18next.t("user:Address - Tooltip"))} : <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
</Col> {Setting.getLabel(i18next.t("user:Address"), i18next.t("user:Address - Tooltip"))} :
<Col span={22} > </Col>
<Input value={this.state.user.address} onChange={e => { <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
this.updateUserField("address", e.target.value); <span>{i18next.t("user:Address line") + " 1"}</span> :
}} /> </Col>
</Col> <Col span={20} >
</Row> <Input value={!this.state.user.address ? "" : this.state.user.address[0]} onChange={e => {
this.updateUserField("address", e.target.value, 0);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
</Col>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
<span>{i18next.t("user:Address line") + " 2"}</span> :
</Col>
<Col span={20} >
<Input value={!this.state.user.address ? "" : this.state.user.address[1]} onChange={e => {
this.updateUserField("address", e.target.value, 1);
}} />
</Col>
</Row>
</React.Fragment>
); );
} else if (accountItem.name === "Affiliation") { } else if (accountItem.name === "Affiliation") {
return ( return (
@@ -1001,6 +1026,19 @@ class UserEditPage extends React.Component {
</Col> </Col>
</Row> </Row>
); );
} else if (accountItem.name === "Need update password") {
return (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("user:Need update password"), i18next.t("user:Need update password - Tooltip"))} :
</Col>
<Col span={(Setting.isMobile()) ? 22 : 2} >
<Switch disabled={(!this.state.user.phone) && (!this.state.user.email) && (!this.state.user.mfaProps)} checked={this.state.user.needUpdatePassword} onChange={checked => {
this.updateUserField("needUpdatePassword", checked);
}} />
</Col>
</Row>
);
} }
} }

View File

@@ -153,21 +153,37 @@ class AuthCallback extends React.Component {
// OAuth // OAuth
const oAuthParams = Util.getOAuthGetParameters(innerParams); const oAuthParams = Util.getOAuthGetParameters(innerParams);
const concatChar = oAuthParams?.redirectUri?.includes("?") ? "&" : "?"; const concatChar = oAuthParams?.redirectUri?.includes("?") ? "&" : "?";
const signinUrl = localStorage.getItem("signinUrl");
AuthBackend.login(body, oAuthParams) AuthBackend.login(body, oAuthParams)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
const responseType = this.getResponseType(); const responseType = this.getResponseType();
if (responseType === "login") { if (responseType === "login") {
if (res.data2) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;
}
Setting.showMessage("success", "Logged in successfully"); Setting.showMessage("success", "Logged in successfully");
// Setting.goToLinkSoft(this, "/"); // Setting.goToLinkSoft(this, "/");
const link = Setting.getFromLink(); const link = Setting.getFromLink();
Setting.goToLink(link); Setting.goToLink(link);
} else if (responseType === "code") { } else if (responseType === "code") {
if (res.data2) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;
}
const code = res.data; const code = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`); Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
// Setting.showMessage("success", `Authorization code: ${res.data}`); // Setting.showMessage("success", `Authorization code: ${res.data}`);
} else if (responseType === "token" || responseType === "id_token") { } else if (responseType === "token" || responseType === "id_token") {
if (res.data2) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;
}
const token = res.data; const token = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}${responseType}=${token}&state=${oAuthParams.state}&token_type=bearer`); Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}${responseType}=${token}&state=${oAuthParams.state}&token_type=bearer`);
} else if (responseType === "link") { } else if (responseType === "link") {
@@ -181,6 +197,11 @@ class AuthCallback extends React.Component {
relayState: oAuthParams.relayState, relayState: oAuthParams.relayState,
}); });
} else { } else {
if (res.data2.needUpdatePassword) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;
}
const SAMLResponse = res.data; const SAMLResponse = res.data;
const redirectUri = res.data2.redirectUrl; const redirectUri = res.data2.redirectUrl;
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`); Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);

View File

@@ -35,8 +35,8 @@ class ForgetPage extends React.Component {
classes: props, classes: props,
applicationName: props.applicationName ?? props.match.params?.applicationName, applicationName: props.applicationName ?? props.match.params?.applicationName,
msg: null, msg: null,
name: "", name: props.account ? props.account.name : "",
username: "", username: props.account ? props.account.name : "",
phone: "", phone: "",
email: "", email: "",
dest: "", dest: "",
@@ -44,7 +44,6 @@ class ForgetPage extends React.Component {
verifyType: "", // "email", "phone" verifyType: "", // "email", "phone"
current: 0, current: 0,
}; };
this.form = React.createRef(); this.form = React.createRef();
} }
@@ -205,6 +204,7 @@ class ForgetPage extends React.Component {
initialValues={{ initialValues={{
application: application.name, application: application.name,
organization: application.organization, organization: application.organization,
username: this.state.name,
}} }}
style={{width: "300px"}} style={{width: "300px"}}
size="large" size="large"
@@ -488,7 +488,7 @@ class ForgetPage extends React.Component {
<Row> <Row>
<Col span={24}> <Col span={24}>
<div style={{textAlign: "center", fontSize: "28px"}}> <div style={{textAlign: "center", fontSize: "28px"}}>
{i18next.t("forget:Retrieve password")} {i18next.t("forget:Reset password")}
</div> </div>
</Col> </Col>
</Row> </Row>

View File

@@ -68,6 +68,8 @@ class LoginPage extends React.Component {
this.state.applicationName = props.match?.params?.casApplicationName; this.state.applicationName = props.match?.params?.casApplicationName;
} }
localStorage.setItem("signinUrl", window.location.href);
this.form = React.createRef(); this.form = React.createRef();
} }
@@ -300,6 +302,12 @@ class LoginPage extends React.Component {
return; return;
} }
if (resp.data2) {
sessionStorage.setItem("signinUrl", window.location.href);
Setting.goToLinkSoft(ths, `/forget/${application.name}`);
return;
}
if (Setting.hasPromptPage(application)) { if (Setting.hasPromptPage(application)) {
AuthBackend.getAccount() AuthBackend.getAccount()
.then((res) => { .then((res) => {
@@ -442,15 +450,27 @@ class LoginPage extends React.Component {
const responseType = values["type"]; const responseType = values["type"];
if (responseType === "login") { if (responseType === "login") {
if (res.data2) {
sessionStorage.setItem("signinUrl", window.location.href);
Setting.goToLink(this, `/forget/${this.state.applicationName}`);
}
Setting.showMessage("success", i18next.t("application:Logged in successfully")); Setting.showMessage("success", i18next.t("application:Logged in successfully"));
this.props.onLoginSuccess(); this.props.onLoginSuccess();
} else if (responseType === "code") { } else if (responseType === "code") {
this.postCodeLoginAction(res); this.postCodeLoginAction(res);
} else if (responseType === "token" || responseType === "id_token") { } else if (responseType === "token" || responseType === "id_token") {
if (res.data2) {
sessionStorage.setItem("signinUrl", window.location.href);
Setting.goToLink(this, `/forget/${this.state.applicationName}`);
}
const amendatoryResponseType = responseType === "token" ? "access_token" : responseType; const amendatoryResponseType = responseType === "token" ? "access_token" : responseType;
const accessToken = res.data; const accessToken = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}#${amendatoryResponseType}=${accessToken}&state=${oAuthParams.state}&token_type=bearer`); Setting.goToLink(`${oAuthParams.redirectUri}#${amendatoryResponseType}=${accessToken}&state=${oAuthParams.state}&token_type=bearer`);
} else if (responseType === "saml") { } else if (responseType === "saml") {
if (res.data2.needUpdatePassword) {
sessionStorage.setItem("signinUrl", window.location.href);
Setting.goToLink(this, `/forget/${this.state.applicationName}`);
}
if (res.data2.method === "POST") { if (res.data2.method === "POST") {
this.setState({ this.setState({
samlResponse: res.data, samlResponse: res.data,
@@ -532,7 +552,7 @@ class LoginPage extends React.Component {
if (signinItem.name === "Logo") { if (signinItem.name === "Logo") {
return ( return (
<div className="login-logo-box"> <div className="login-logo-box">
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.label?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} /> <div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
{ {
Setting.renderHelmet(application) Setting.renderHelmet(application)
} }
@@ -544,7 +564,7 @@ class LoginPage extends React.Component {
} else if (signinItem.name === "Back button") { } else if (signinItem.name === "Back button") {
return ( return (
<div className="back-button"> <div className="back-button">
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.label?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} /> <div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
{ {
this.renderBackButton() this.renderBackButton()
} }
@@ -562,14 +582,14 @@ class LoginPage extends React.Component {
return ( return (
<div className="login-languages"> <div className="login-languages">
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.label?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} /> <div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
<LanguageSelect languages={application.organizationObj.languages} /> <LanguageSelect languages={application.organizationObj.languages} />
</div> </div>
); );
} else if (signinItem.name === "Signin methods") { } else if (signinItem.name === "Signin methods") {
return ( return (
<div> <div>
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.label?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} /> <div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
{this.renderMethodChoiceBox()} {this.renderMethodChoiceBox()}
</div> </div>
) )
@@ -577,10 +597,11 @@ class LoginPage extends React.Component {
} else if (signinItem.name === "Username") { } else if (signinItem.name === "Username") {
return ( return (
<div> <div>
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.label?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} /> <div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
<Form.Item <Form.Item
name="username" name="username"
className="login-username" className="login-username"
label={signinItem.label ? signinItem.label : null}
rules={[ rules={[
{ {
required: true, required: true,
@@ -653,14 +674,14 @@ class LoginPage extends React.Component {
} else if (signinItem.name === "Password") { } else if (signinItem.name === "Password") {
return ( return (
<div> <div>
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.label?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} /> <div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
{this.renderPasswordOrCodeInput()} {this.renderPasswordOrCodeInput(signinItem)}
</div> </div>
); );
} else if (signinItem.name === "Forgot password?") { } else if (signinItem.name === "Forgot password?") {
return ( return (
<div> <div>
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.label?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} /> <div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
<div className="login-forget-password"> <div className="login-forget-password">
<Form.Item name="autoSignin" valuePropName="checked" noStyle> <Form.Item name="autoSignin" valuePropName="checked" noStyle>
<Checkbox style={{float: "left"}}> <Checkbox style={{float: "left"}}>
@@ -668,7 +689,7 @@ class LoginPage extends React.Component {
</Checkbox> </Checkbox>
</Form.Item> </Form.Item>
{ {
signinItem.visible ? Setting.renderForgetLink(application, i18next.t("login:Forgot password?")) : null signinItem.visible ? Setting.renderForgetLink(application, signinItem.label ? signinItem.label : i18next.t("login:Forgot password?")) : null
} }
</div> </div>
</div> </div>
@@ -678,7 +699,7 @@ class LoginPage extends React.Component {
} else if (signinItem.name === "Login button") { } else if (signinItem.name === "Login button") {
return ( return (
<Form.Item className="login-button-box"> <Form.Item className="login-button-box">
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.label?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} /> <div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
<Button <Button
type="primary" type="primary"
htmlType="submit" htmlType="submit"
@@ -687,7 +708,7 @@ class LoginPage extends React.Component {
{ {
this.state.loginMethod === "webAuthn" ? i18next.t("login:Sign in with WebAuthn") : this.state.loginMethod === "webAuthn" ? i18next.t("login:Sign in with WebAuthn") :
this.state.loginMethod === "faceId" ? i18next.t("login:Sign in with Face ID") : this.state.loginMethod === "faceId" ? i18next.t("login:Sign in with Face ID") :
i18next.t("login:Sign In") signinItem.label ? signinItem.label : i18next.t("login:Sign In")
} }
</Button> </Button>
{ {
@@ -722,7 +743,7 @@ class LoginPage extends React.Component {
return ( return (
<div> <div>
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.label?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} /> <div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
<Form.Item> <Form.Item>
{ {
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map(providerItem => { application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map(providerItem => {
@@ -737,13 +758,13 @@ class LoginPage extends React.Component {
); );
} else if (signinItem.name.startsWith("Text ") || signinItem?.isCustom) { } else if (signinItem.name.startsWith("Text ") || signinItem?.isCustom) {
return ( return (
<div dangerouslySetInnerHTML={{__html: signinItem.label}} /> <div dangerouslySetInnerHTML={{__html: signinItem.customCss}} />
); );
} else if (signinItem.name === "Signup link") { } else if (signinItem.name === "Signup link") {
return ( return (
<div style={{width: "100%"}} className="login-signup-link"> <div style={{width: "100%"}} className="login-signup-link">
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.label?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} /> <div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
{this.renderFooter(application)} {this.renderFooter(application, signinItem)}
</div> </div>
); );
} }
@@ -840,7 +861,7 @@ class LoginPage extends React.Component {
{application.displayName} {application.displayName}
</span> </span>
</a> </a>
: :
</div> </div>
<br /> <br />
{ {
@@ -896,17 +917,20 @@ class LoginPage extends React.Component {
/>; />;
} }
renderFooter(application) { renderFooter(application, signinItem) {
return ( return (
<div> <div>
{ {
!application.enableSignUp ? null : ( !application.enableSignUp ? null : (
<React.Fragment> signinItem.label ? Setting.renderSignupLink(application, signinItem.label) :
{i18next.t("login:No account?")}&nbsp; (
{ <React.Fragment>
Setting.renderSignupLink(application, i18next.t("login:sign up now")) {i18next.t("login:No account?")}
} {
</React.Fragment> Setting.renderSignupLink(application, i18next.t("login:sign up now"))
}
</React.Fragment>
)
) )
} }
</div> </div>
@@ -1022,7 +1046,7 @@ class LoginPage extends React.Component {
}); });
} }
renderPasswordOrCodeInput() { renderPasswordOrCodeInput(signinItem) {
const application = this.getApplicationObj(); const application = this.getApplicationObj();
if (this.state.loginMethod === "password" || this.state.loginMethod === "ldap") { if (this.state.loginMethod === "password" || this.state.loginMethod === "ldap") {
return ( return (
@@ -1031,6 +1055,7 @@ class LoginPage extends React.Component {
<Form.Item <Form.Item
name="password" name="password"
className="login-password" className="login-password"
label={signinItem.label ? signinItem.label : null}
rules={[{required: true, message: i18next.t("login:Please input your password!")}]} rules={[{required: true, message: i18next.t("login:Please input your password!")}]}
> >
<Input.Password <Input.Password

View File

@@ -384,8 +384,7 @@ export function getAuthUrl(application, provider, method, code) {
let endpoint = authInfo[provider.type].endpoint; let endpoint = authInfo[provider.type].endpoint;
let redirectUri = `${window.location.origin}/callback`; let redirectUri = `${window.location.origin}/callback`;
const scope = authInfo[provider.type].scope; let scope = authInfo[provider.type].scope;
const isShortState = (provider.type === "WeChat" && navigator.userAgent.includes("MicroMessenger")) || (provider.type === "Twitter"); const isShortState = (provider.type === "WeChat" && navigator.userAgent.includes("MicroMessenger")) || (provider.type === "Twitter");
const state = Util.getStateFromQueryParams(application.name, provider.name, method, isShortState); const state = Util.getStateFromQueryParams(application.name, provider.name, method, isShortState);
const codeChallenge = "P3S-a7dr8bgM4bF6vOyiKkKETDl16rcAzao9F8UIL1Y"; // SHA256(Base64-URL-encode("casdoor-verifier")) const codeChallenge = "P3S-a7dr8bgM4bF6vOyiKkKETDl16rcAzao9F8UIL1Y"; // SHA256(Base64-URL-encode("casdoor-verifier"))
@@ -396,9 +395,11 @@ export function getAuthUrl(application, provider, method, code) {
} }
} else if (provider.type === "Apple") { } else if (provider.type === "Apple") {
redirectUri = `${window.location.origin}/api/callback`; redirectUri = `${window.location.origin}/api/callback`;
} else if (provider.type === "Google" && provider.disableSsl) {
scope += "+https://www.googleapis.com/auth/user.phonenumbers.read";
} }
if (provider.type === "Google" || provider.type === "GitHub" || provider.type === "QQ" || provider.type === "Facebook" if (provider.type === "Google" || provider.type === "GitHub" || provider.type === "Facebook"
|| provider.type === "Weibo" || provider.type === "Gitee" || provider.type === "LinkedIn" || provider.type === "GitLab" || provider.type === "AzureAD" || provider.type === "Weibo" || provider.type === "Gitee" || provider.type === "LinkedIn" || provider.type === "GitLab" || provider.type === "AzureAD"
|| provider.type === "Slack" || provider.type === "Line" || provider.type === "Amazon" || provider.type === "Auth0" || provider.type === "BattleNet" || provider.type === "Slack" || provider.type === "Line" || provider.type === "Amazon" || provider.type === "Auth0" || provider.type === "BattleNet"
|| provider.type === "Bitbucket" || provider.type === "Box" || provider.type === "CloudFoundry" || provider.type === "Dailymotion" || provider.type === "Bitbucket" || provider.type === "Box" || provider.type === "CloudFoundry" || provider.type === "Dailymotion"
@@ -410,6 +411,8 @@ export function getAuthUrl(application, provider, method, code) {
|| provider.type === "Twitch" || provider.type === "Typetalk" || provider.type === "Uber" || provider.type === "VK" || provider.type === "Wepay" || provider.type === "Twitch" || provider.type === "Typetalk" || provider.type === "Uber" || provider.type === "VK" || provider.type === "Wepay"
|| provider.type === "Xero" || provider.type === "Yahoo" || provider.type === "Yammer" || provider.type === "Yandex" || provider.type === "Zoom") { || provider.type === "Xero" || provider.type === "Yahoo" || provider.type === "Yammer" || provider.type === "Yandex" || provider.type === "Zoom") {
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`; return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
} else if (provider.type === "QQ") {
return `${endpoint}?response_type=code&client_id=${provider.clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&state=${encodeURIComponent(state)}&scope=${encodeURIComponent(scope)}`;
} else if (provider.type === "AzureADB2C") { } else if (provider.type === "AzureADB2C") {
return `https://${provider.domain}.b2clogin.com/${provider.domain}.onmicrosoft.com/${provider.appId}/oauth2/v2.0/authorize?client_id=${provider.clientId}&nonce=defaultNonce&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${scope}&response_type=code&state=${state}&prompt=login`; return `https://${provider.domain}.b2clogin.com/${provider.domain}.onmicrosoft.com/${provider.appId}/oauth2/v2.0/authorize?client_id=${provider.clientId}&nonce=defaultNonce&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${scope}&response_type=code&state=${state}&prompt=login`;
} else if (provider.type === "DingTalk") { } else if (provider.type === "DingTalk") {

View File

@@ -389,6 +389,14 @@ class SignupPage extends React.Component {
return Promise.reject(i18next.t("signup:The input is not valid Email!")); return Promise.reject(i18next.t("signup:The input is not valid Email!"));
} }
if (signupItem.regex) {
const reg = new RegExp(signupItem.regex);
if (!reg.test(this.state.email)) {
this.setState({validEmail: false});
return Promise.reject(i18next.t("signup:The input Email doesn't match the signup item regex!"));
}
}
this.setState({validEmail: true}); this.setState({validEmail: true});
return Promise.resolve(); return Promise.resolve();
}, },

View File

@@ -50,6 +50,7 @@ function testEmailProvider(provider, email = "") {
sender: provider.displayName, sender: provider.displayName,
receivers: email === "" ? ["TestSmtpServer"] : [email], receivers: email === "" ? ["TestSmtpServer"] : [email],
provider: provider.name, provider: provider.name,
providerObject: provider,
}; };
return fetch(`${Setting.ServerUrl}/api/send-email`, { return fetch(`${Setting.ServerUrl}/api/send-email`, {

View File

@@ -13,11 +13,14 @@
// limitations under the License. // limitations under the License.
import {Select} from "antd"; import {Select} from "antd";
import i18next from "i18next";
import * as Setting from "../../Setting"; import * as Setting from "../../Setting";
import React from "react"; import React from "react";
const {Option} = Select;
export const CountryCodeSelect = (props) => { export const CountryCodeSelect = (props) => {
const {onChange, style, disabled, initValue} = props; const {onChange, style, disabled, initValue, mode} = props;
const countryCodes = props.countryCodes ?? []; const countryCodes = props.countryCodes ?? [];
const [value, setValue] = React.useState(""); const [value, setValue] = React.useState("");
@@ -42,11 +45,19 @@ export const CountryCodeSelect = (props) => {
style={style} style={style}
disabled={disabled} disabled={disabled}
value={value} value={value}
mode={mode}
dropdownMatchSelectWidth={false} dropdownMatchSelectWidth={false}
optionLabelProp={"label"} optionLabelProp={"label"}
onChange={handleOnChange} onChange={handleOnChange}
filterOption={(input, option) => (option?.text ?? "").toLowerCase().includes(input.toLowerCase())} filterOption={(input, option) => (option?.text ?? "").toLowerCase().includes(input.toLowerCase())}
> >
{
props.hasDefault ? (<Option key={"All"} value={"All"} label={i18next.t("organization:All")} text={"organization:All"} >
<div style={{display: "flex", justifyContent: "space-between", marginRight: "10px"}}>
{i18next.t("organization:All")}
</div>
</Option>) : null
}
{ {
Setting.getCountryCodeData(countryCodes).map((country) => Setting.getCountryCodeOption(country)) Setting.getCountryCodeData(countryCodes).map((country) => Setting.getCountryCodeOption(country))
} }

View File

@@ -166,7 +166,7 @@
"Next Step": "Next Step", "Next Step": "Next Step",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Reset password": "Reset password",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "input password" "input password": "input password"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Nächster Schritt", "Next Step": "Nächster Schritt",
"Please input your username!": "Bitte gib deinen Benutzernamen ein!", "Please input your username!": "Bitte gib deinen Benutzernamen ein!",
"Reset": "Zurücksetzen", "Reset": "Zurücksetzen",
"Retrieve password": "Passwort abrufen", "Reset password": "Passwort abrufen",
"Unknown forget type": "Unbekannter Vergesslichkeitstyp", "Unknown forget type": "Unbekannter Vergesslichkeitstyp",
"Verify": "überprüfen" "Verify": "überprüfen"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "From address - Tooltip", "From address - Tooltip": "From address - Tooltip",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "From name - Tooltip", "From name - Tooltip": "From name - Tooltip",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name des Hosts", "Host - Tooltip": "Name des Hosts",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Kopiere den Link", "Copy Link": "Kopiere den Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "Die Eingabe ist keine Rechnungssteuer-ID!", "The input is not invoice Tax ID!": "Die Eingabe ist keine Rechnungssteuer-ID!",
"The input is not invoice title!": "Der Eingabewert ist nicht die Rechnungsbezeichnung!", "The input is not invoice title!": "Der Eingabewert ist nicht die Rechnungsbezeichnung!",
"The input is not valid Email!": "Die Eingabe ist keine gültige E-Mail-Adresse!", "The input is not valid Email!": "Die Eingabe ist keine gültige E-Mail-Adresse!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Drittanbieter-Anmeldungen, die mit dem Benutzer verknüpft sind", "3rd-party logins - Tooltip": "Drittanbieter-Anmeldungen, die mit dem Benutzer verknüpft sind",
"Address": "Adresse", "Address": "Adresse",
"Address - Tooltip": "Wohnadresse", "Address - Tooltip": "Wohnadresse",
"Address line": "Address line",
"Affiliation": "Zugehörigkeit", "Affiliation": "Zugehörigkeit",
"Affiliation - Tooltip": "Arbeitgeber, wie Firmenname oder Organisationsname", "Affiliation - Tooltip": "Arbeitgeber, wie Firmenname oder Organisationsname",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Verwaltete Konten", "Managed accounts": "Verwaltete Konten",
"Modify password...": "Passwort ändern...", "Modify password...": "Passwort ändern...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Neue E-Mail", "New Email": "Neue E-Mail",
"New Password": "Neues Passwort", "New Password": "Neues Passwort",
"New User": "Neuer Benutzer", "New User": "Neuer Benutzer",
@@ -1171,6 +1178,7 @@
"input password": "Eingabe des Passworts" "input password": "Eingabe des Passworts"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Next Step", "Next Step": "Next Step",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Reset password": "Reset password",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "input password" "input password": "input password"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Siguiente paso", "Next Step": "Siguiente paso",
"Please input your username!": "¡Por favor, ingrese su nombre de usuario!", "Please input your username!": "¡Por favor, ingrese su nombre de usuario!",
"Reset": "Restablecer", "Reset": "Restablecer",
"Retrieve password": "Recuperar contraseña", "Reset password": "Reset password",
"Unknown forget type": "Tipo de olvido desconocido", "Unknown forget type": "Tipo de olvido desconocido",
"Verify": "Verificar" "Verify": "Verificar"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "From address - Tooltip", "From address - Tooltip": "From address - Tooltip",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "From name - Tooltip", "From name - Tooltip": "From name - Tooltip",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Anfitrión", "Host": "Anfitrión",
"Host - Tooltip": "Nombre del anfitrión", "Host - Tooltip": "Nombre del anfitrión",
"IdP": "IdP = Proveedor de Identidad", "IdP": "IdP = Proveedor de Identidad",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copiar enlace", "Copy Link": "Copiar enlace",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "¡La entrada no es el ID fiscal de la factura!", "The input is not invoice Tax ID!": "¡La entrada no es el ID fiscal de la factura!",
"The input is not invoice title!": "¡El entrada no es el título de la factura!", "The input is not invoice title!": "¡El entrada no es el título de la factura!",
"The input is not valid Email!": "¡La entrada no es un correo electrónico válido!", "The input is not valid Email!": "¡La entrada no es un correo electrónico válido!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Accesos sociales ligados por el usuario", "3rd-party logins - Tooltip": "Accesos sociales ligados por el usuario",
"Address": "Dirección", "Address": "Dirección",
"Address - Tooltip": "Dirección residencial", "Address - Tooltip": "Dirección residencial",
"Address line": "Address line",
"Affiliation": "Afiliación", "Affiliation": "Afiliación",
"Affiliation - Tooltip": "Empleador, como el nombre de una empresa u organización", "Affiliation - Tooltip": "Empleador, como el nombre de una empresa u organización",
"Bio": "Bio - Biografía", "Bio": "Bio - Biografía",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Cuentas gestionadas", "Managed accounts": "Cuentas gestionadas",
"Modify password...": "Modificar contraseña...", "Modify password...": "Modificar contraseña...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Nuevo correo electrónico", "New Email": "Nuevo correo electrónico",
"New Password": "Nueva contraseña", "New Password": "Nueva contraseña",
"New User": "Nuevo Usuario", "New User": "Nuevo Usuario",
@@ -1171,6 +1178,7 @@
"input password": "Ingresar contraseña" "input password": "Ingresar contraseña"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Next Step", "Next Step": "Next Step",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Reset password": "Reset password",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "input password" "input password": "input password"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Next Step", "Next Step": "Next Step",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Reset password": "Reset password",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "input password" "input password": "input password"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Étape suivante", "Next Step": "Étape suivante",
"Please input your username!": "Veuillez saisir votre identifiant !", "Please input your username!": "Veuillez saisir votre identifiant !",
"Reset": "Réinitialiser", "Reset": "Réinitialiser",
"Retrieve password": "Récupérer le mot de passe", "Reset password": "Récupérer le mot de passe",
"Unknown forget type": "Type d'oubli inconnu", "Unknown forget type": "Type d'oubli inconnu",
"Verify": "Vérifier" "Verify": "Vérifier"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "L'adresse e-mail affichée comme expéditeur dans les e-mails envoyés", "From address - Tooltip": "L'adresse e-mail affichée comme expéditeur dans les e-mails envoyés",
"From name": "Nom de l'expéditeur", "From name": "Nom de l'expéditeur",
"From name - Tooltip": "Le nom affiché comme expéditeur dans les e-mails envoyés", "From name - Tooltip": "Le nom affiché comme expéditeur dans les e-mails envoyés",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Hôte", "Host": "Hôte",
"Host - Tooltip": "Nom d'hôte", "Host - Tooltip": "Nom d'hôte",
"IdP": "IdP (Identité Fournisseur)", "IdP": "IdP (Identité Fournisseur)",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copier le lien", "Copy Link": "Copier le lien",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "L'entrée n'est pas l'identifiant fiscal de la facture !", "The input is not invoice Tax ID!": "L'entrée n'est pas l'identifiant fiscal de la facture !",
"The input is not invoice title!": "L'entrée n'est pas un nom ou une dénomination sociale !", "The input is not invoice title!": "L'entrée n'est pas un nom ou une dénomination sociale !",
"The input is not valid Email!": "L'entrée n'est pas une adresse e-mail valide !", "The input is not valid Email!": "L'entrée n'est pas une adresse e-mail valide !",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Service de connexions tiers liés au compte", "3rd-party logins - Tooltip": "Service de connexions tiers liés au compte",
"Address": "Adresse", "Address": "Adresse",
"Address - Tooltip": "Adresse résidentielle", "Address - Tooltip": "Adresse résidentielle",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employeur, tel que le nom de l'entreprise ou de l'organisation", "Affiliation - Tooltip": "Employeur, tel que le nom de l'entreprise ou de l'organisation",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Comptes gérés", "Managed accounts": "Comptes gérés",
"Modify password...": "Modifier le mot de passe...", "Modify password...": "Modifier le mot de passe...",
"Multi-factor authentication": "Authentification multifacteur", "Multi-factor authentication": "Authentification multifacteur",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Nouvelle adresse e-mail", "New Email": "Nouvelle adresse e-mail",
"New Password": "Nouveau mot de passe", "New Password": "Nouveau mot de passe",
"New User": "Nouveau compte", "New User": "Nouveau compte",
@@ -1171,6 +1178,7 @@
"input password": "saisir le mot de passe" "input password": "saisir le mot de passe"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Next Step", "Next Step": "Next Step",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Reset password": "Reset password",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "input password" "input password": "input password"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Langkah selanjutnya", "Next Step": "Langkah selanjutnya",
"Please input your username!": "Silakan masukkan nama pengguna Anda!", "Please input your username!": "Silakan masukkan nama pengguna Anda!",
"Reset": "Menyetel-ulang", "Reset": "Menyetel-ulang",
"Retrieve password": "Mengambil password", "Reset password": "Mengambil password",
"Unknown forget type": "Tipe yang tidak diketahui terlupakan", "Unknown forget type": "Tipe yang tidak diketahui terlupakan",
"Verify": "Memverifikasi" "Verify": "Memverifikasi"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "From address - Tooltip", "From address - Tooltip": "From address - Tooltip",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "From name - Tooltip", "From name - Tooltip": "From name - Tooltip",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Tuan rumah", "Host": "Tuan rumah",
"Host - Tooltip": "Nama tuan rumah", "Host - Tooltip": "Nama tuan rumah",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Salin Tautan", "Copy Link": "Salin Tautan",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "Input ini bukan Tax ID faktur!", "The input is not invoice Tax ID!": "Input ini bukan Tax ID faktur!",
"The input is not invoice title!": "Masukan bukan judul faktur!", "The input is not invoice title!": "Masukan bukan judul faktur!",
"The input is not valid Email!": "Input yang dimasukkan bukan sesuai dengan format Email yang valid!", "The input is not valid Email!": "Input yang dimasukkan bukan sesuai dengan format Email yang valid!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Masuk sosial yang terhubung oleh pengguna", "3rd-party logins - Tooltip": "Masuk sosial yang terhubung oleh pengguna",
"Address": "Alamat", "Address": "Alamat",
"Address - Tooltip": "Alamat tempat tinggal", "Address - Tooltip": "Alamat tempat tinggal",
"Address line": "Address line",
"Affiliation": "Afiliasi", "Affiliation": "Afiliasi",
"Affiliation - Tooltip": "Pemberi Kerja, seperti nama perusahaan atau nama organisasi", "Affiliation - Tooltip": "Pemberi Kerja, seperti nama perusahaan atau nama organisasi",
"Bio": "Bio: Biografi", "Bio": "Bio: Biografi",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Akun yang dikelola", "Managed accounts": "Akun yang dikelola",
"Modify password...": "Mengubah kata sandi...", "Modify password...": "Mengubah kata sandi...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Email baru", "New Email": "Email baru",
"New Password": "Kata Sandi Baru", "New Password": "Kata Sandi Baru",
"New User": "Pengguna Baru", "New User": "Pengguna Baru",
@@ -1171,6 +1178,7 @@
"input password": "masukkan kata sandi" "input password": "masukkan kata sandi"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Next Step", "Next Step": "Next Step",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Reset password": "Reset password",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "input password" "input password": "input password"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "次のステップ", "Next Step": "次のステップ",
"Please input your username!": "ユーザー名を入力してください!", "Please input your username!": "ユーザー名を入力してください!",
"Reset": "リセット", "Reset": "リセット",
"Retrieve password": "パスワードの取得", "Reset password": "パスワードの取得",
"Unknown forget type": "未知の忘却タイプ", "Unknown forget type": "未知の忘却タイプ",
"Verify": "検証" "Verify": "検証"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "From address - Tooltip", "From address - Tooltip": "From address - Tooltip",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "From name - Tooltip", "From name - Tooltip": "From name - Tooltip",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "ホスト", "Host": "ホスト",
"Host - Tooltip": "ホストの名前", "Host - Tooltip": "ホストの名前",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "コピー リンク", "Copy Link": "コピー リンク",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "入力されたものは請求書の税番号ではありません!", "The input is not invoice Tax ID!": "入力されたものは請求書の税番号ではありません!",
"The input is not invoice title!": "インプットは請求書タイトルではありません!", "The input is not invoice title!": "インプットは請求書タイトルではありません!",
"The input is not valid Email!": "入力されたものは有効なメールではありません", "The input is not valid Email!": "入力されたものは有効なメールではありません",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "ユーザーによってリンクされたソーシャルログイン", "3rd-party logins - Tooltip": "ユーザーによってリンクされたソーシャルログイン",
"Address": "住所", "Address": "住所",
"Address - Tooltip": "住所", "Address - Tooltip": "住所",
"Address line": "Address line",
"Affiliation": "所属", "Affiliation": "所属",
"Affiliation - Tooltip": "企業名や団体名などの雇用主", "Affiliation - Tooltip": "企業名や団体名などの雇用主",
"Bio": "バイオ技術", "Bio": "バイオ技術",
@@ -1125,6 +1130,8 @@
"Managed accounts": "管理アカウント", "Managed accounts": "管理アカウント",
"Modify password...": "パスワードを変更する...", "Modify password...": "パスワードを変更する...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "新しいメール", "New Email": "新しいメール",
"New Password": "新しいパスワード", "New Password": "新しいパスワード",
"New User": "新しいユーザー", "New User": "新しいユーザー",
@@ -1171,6 +1178,7 @@
"input password": "パスワードを入力してください" "input password": "パスワードを入力してください"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Next Step", "Next Step": "Next Step",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Reset password": "Reset password",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "input password" "input password": "input password"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "다음 단계", "Next Step": "다음 단계",
"Please input your username!": "사용자 이름을 입력하세요!", "Please input your username!": "사용자 이름을 입력하세요!",
"Reset": "리셋", "Reset": "리셋",
"Retrieve password": "비밀번호를 복구하세요", "Reset password": "비밀번호를 복구하세요",
"Unknown forget type": "미지의 잊혀진 유형", "Unknown forget type": "미지의 잊혀진 유형",
"Verify": "검증하다" "Verify": "검증하다"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "From address - Tooltip", "From address - Tooltip": "From address - Tooltip",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "From name - Tooltip", "From name - Tooltip": "From name - Tooltip",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "호스트", "Host": "호스트",
"Host - Tooltip": "호스트의 이름", "Host - Tooltip": "호스트의 이름",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "링크 복사하기", "Copy Link": "링크 복사하기",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "입력한 것은 송장 세금 ID가 아닙니다!", "The input is not invoice Tax ID!": "입력한 것은 송장 세금 ID가 아닙니다!",
"The input is not invoice title!": "입력값은 송장 제목이 아닙니다!", "The input is not invoice title!": "입력값은 송장 제목이 아닙니다!",
"The input is not valid Email!": "입력 값은 유효한 이메일이 아닙니다!", "The input is not valid Email!": "입력 값은 유효한 이메일이 아닙니다!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "사용자가 연결한 소셜 로그인", "3rd-party logins - Tooltip": "사용자가 연결한 소셜 로그인",
"Address": "주소", "Address": "주소",
"Address - Tooltip": "주거지 주소", "Address - Tooltip": "주거지 주소",
"Address line": "Address line",
"Affiliation": "소속", "Affiliation": "소속",
"Affiliation - Tooltip": "고용주, 회사명 또는 조직명", "Affiliation - Tooltip": "고용주, 회사명 또는 조직명",
"Bio": "바이오", "Bio": "바이오",
@@ -1125,6 +1130,8 @@
"Managed accounts": "관리 계정", "Managed accounts": "관리 계정",
"Modify password...": "비밀번호 수정하기...", "Modify password...": "비밀번호 수정하기...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "새 이메일", "New Email": "새 이메일",
"New Password": "새로운 비밀번호", "New Password": "새로운 비밀번호",
"New User": "새로운 사용자", "New User": "새로운 사용자",
@@ -1171,6 +1178,7 @@
"input password": "비밀번호를 입력해주세요" "input password": "비밀번호를 입력해주세요"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Next Step", "Next Step": "Next Step",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Reset password": "Reset password",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "input password" "input password": "input password"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Next Step", "Next Step": "Next Step",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Reset password": "Reset password",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "input password" "input password": "input password"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Next Step", "Next Step": "Next Step",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Reset password": "Reset password",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "input password" "input password": "input password"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Próxima Etapa", "Next Step": "Próxima Etapa",
"Please input your username!": "Por favor, insira seu nome de usuário!", "Please input your username!": "Por favor, insira seu nome de usuário!",
"Reset": "Redefinir", "Reset": "Redefinir",
"Retrieve password": "Recuperar senha", "Reset password": "Recuperar senha",
"Unknown forget type": "Tipo de recuperação desconhecido", "Unknown forget type": "Tipo de recuperação desconhecido",
"Verify": "Verificar" "Verify": "Verificar"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Endereço de e-mail do remetente", "From address - Tooltip": "Endereço de e-mail do remetente",
"From name": "Nome do remetente", "From name": "Nome do remetente",
"From name - Tooltip": "Nome do remetente", "From name - Tooltip": "Nome do remetente",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Nome do host", "Host - Tooltip": "Nome do host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copiar Link", "Copy Link": "Copiar Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "A entrada não é um ID fiscal de fatura válido!", "The input is not invoice Tax ID!": "A entrada não é um ID fiscal de fatura válido!",
"The input is not invoice title!": "A entrada não é um título de fatura válido!", "The input is not invoice title!": "A entrada não é um título de fatura válido!",
"The input is not valid Email!": "A entrada não é um Email válido!", "The input is not valid Email!": "A entrada não é um Email válido!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Logins sociais vinculados pelo usuário", "3rd-party logins - Tooltip": "Logins sociais vinculados pelo usuário",
"Address": "Endereço", "Address": "Endereço",
"Address - Tooltip": "Endereço residencial", "Address - Tooltip": "Endereço residencial",
"Address line": "Address line",
"Affiliation": "Afiliação", "Affiliation": "Afiliação",
"Affiliation - Tooltip": "Empregador, como nome da empresa ou organização", "Affiliation - Tooltip": "Empregador, como nome da empresa ou organização",
"Bio": "Biografia", "Bio": "Biografia",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Contas gerenciadas", "Managed accounts": "Contas gerenciadas",
"Modify password...": "Modificar senha...", "Modify password...": "Modificar senha...",
"Multi-factor authentication": "Autenticação de vários fatores", "Multi-factor authentication": "Autenticação de vários fatores",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Novo E-mail", "New Email": "Novo E-mail",
"New Password": "Nova Senha", "New Password": "Nova Senha",
"New User": "Novo Usuário", "New User": "Novo Usuário",
@@ -1171,6 +1178,7 @@
"input password": "Digite a senha" "input password": "Digite a senha"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Следующий шаг", "Next Step": "Следующий шаг",
"Please input your username!": "Пожалуйста, введите своё имя пользователя!", "Please input your username!": "Пожалуйста, введите своё имя пользователя!",
"Reset": "Сбросить", "Reset": "Сбросить",
"Retrieve password": "Восстановить пароль", "Reset password": "Восстановить пароль",
"Unknown forget type": "Неизвестный забытый тип", "Unknown forget type": "Неизвестный забытый тип",
"Verify": "Проверить" "Verify": "Проверить"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "From address - Tooltip", "From address - Tooltip": "From address - Tooltip",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "From name - Tooltip", "From name - Tooltip": "From name - Tooltip",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Хост", "Host": "Хост",
"Host - Tooltip": "Имя хоста", "Host - Tooltip": "Имя хоста",
"IdP": "ИдП", "IdP": "ИдП",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Копировать ссылку", "Copy Link": "Копировать ссылку",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "Входные данные не являются идентификатором налога по счету-фактуре!", "The input is not invoice Tax ID!": "Входные данные не являются идентификатором налога по счету-фактуре!",
"The input is not invoice title!": "Входные данные не являются названием счета-фактуры!", "The input is not invoice title!": "Входные данные не являются названием счета-фактуры!",
"The input is not valid Email!": "Ввод не является действительным адресом электронной почты!", "The input is not valid Email!": "Ввод не является действительным адресом электронной почты!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Социальные логины, связанные пользователем", "3rd-party logins - Tooltip": "Социальные логины, связанные пользователем",
"Address": "Адрес", "Address": "Адрес",
"Address - Tooltip": "Адрес проживания", "Address - Tooltip": "Адрес проживания",
"Address line": "Address line",
"Affiliation": "Принадлежность", "Affiliation": "Принадлежность",
"Affiliation - Tooltip": "Работодатель, такой как название компании или организации", "Affiliation - Tooltip": "Работодатель, такой как название компании или организации",
"Bio": "Био", "Bio": "Био",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Управляемые аккаунты", "Managed accounts": "Управляемые аккаунты",
"Modify password...": "Изменить пароль...", "Modify password...": "Изменить пароль...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Новое электронное письмо", "New Email": "Новое электронное письмо",
"New Password": "Новый пароль", "New Password": "Новый пароль",
"New User": "Новый пользователь", "New User": "Новый пользователь",
@@ -1171,6 +1178,7 @@
"input password": "введите пароль" "input password": "введите пароль"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Next Step", "Next Step": "Next Step",
"Please input your username!": "Please input your username!", "Please input your username!": "Please input your username!",
"Reset": "Reset", "Reset": "Reset",
"Retrieve password": "Retrieve password", "Reset password": "Reset password",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Verify" "Verify": "Verify"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "input password" "input password": "input password"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Sonraki adım", "Next Step": "Sonraki adım",
"Please input your username!": "Lütfen kullanıcı adınızı girin", "Please input your username!": "Lütfen kullanıcı adınızı girin",
"Reset": "Sıfırla", "Reset": "Sıfırla",
"Retrieve password": "Şifre kurtar", "Reset password": "Şifre kurtar",
"Unknown forget type": "Unknown forget type", "Unknown forget type": "Unknown forget type",
"Verify": "Doğrula" "Verify": "Doğrula"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "Email address of \"From\"", "From address - Tooltip": "Email address of \"From\"",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "Name of \"From\"", "From name - Tooltip": "Name of \"From\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Name of host", "Host - Tooltip": "Name of host",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Copy Link", "Copy Link": "Copy Link",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "The input is not invoice Tax ID!", "The input is not invoice Tax ID!": "The input is not invoice Tax ID!",
"The input is not invoice title!": "The input is not invoice title!", "The input is not invoice title!": "The input is not invoice title!",
"The input is not valid Email!": "The input is not valid Email!", "The input is not valid Email!": "The input is not valid Email!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Social logins linked by the user", "3rd-party logins - Tooltip": "Social logins linked by the user",
"Address": "Address", "Address": "Address",
"Address - Tooltip": "Residential address", "Address - Tooltip": "Residential address",
"Address line": "Address line",
"Affiliation": "Affiliation", "Affiliation": "Affiliation",
"Affiliation - Tooltip": "Employer, such as company name or organization name", "Affiliation - Tooltip": "Employer, such as company name or organization name",
"Bio": "Bio", "Bio": "Bio",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Managed accounts", "Managed accounts": "Managed accounts",
"Modify password...": "Modify password...", "Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "New Email", "New Email": "New Email",
"New Password": "New Password", "New Password": "New Password",
"New User": "New User", "New User": "New User",
@@ -1171,6 +1178,7 @@
"input password": "şifreyi girin" "input password": "şifreyi girin"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -80,6 +80,7 @@
"Only signup": "Тільки реєстрація", "Only signup": "Тільки реєстрація",
"Org choice mode": "Режим вибору організації", "Org choice mode": "Режим вибору організації",
"Org choice mode - Tooltip": "Режим вибору організації підказка", "Org choice mode - Tooltip": "Режим вибору організації підказка",
"Please enable \\\"Signin session\\\" first before enabling \\\"Auto signin\\\"": "Please enable \\\"Signin session\\\" first before enabling \\\"Auto signin\\\"",
"Please input your application!": "Будь ласка, введіть свою заявку!", "Please input your application!": "Будь ласка, введіть свою заявку!",
"Please input your organization!": "Будь ласка, введіть вашу організацію!", "Please input your organization!": "Будь ласка, введіть вашу організацію!",
"Please select a HTML file": "Виберіть файл HTML", "Please select a HTML file": "Виберіть файл HTML",
@@ -165,7 +166,7 @@
"Next Step": "Наступний крок", "Next Step": "Наступний крок",
"Please input your username!": "Будь ласка, введіть своє ім'я користувача!", "Please input your username!": "Будь ласка, введіть своє ім'я користувача!",
"Reset": "Скинути", "Reset": "Скинути",
"Retrieve password": "Отримати пароль", "Reset password": "Отримати пароль",
"Unknown forget type": "Невідомий забутий тип", "Unknown forget type": "Невідомий забутий тип",
"Verify": "Підтвердити" "Verify": "Підтвердити"
}, },
@@ -229,6 +230,7 @@
"Email": "Електронна пошта", "Email": "Електронна пошта",
"Email - Tooltip": "Дійсна електронна пошта", "Email - Tooltip": "Дійсна електронна пошта",
"Email only": "Лише електронна пошта", "Email only": "Лише електронна пошта",
"Email or Phone": "Email or Phone",
"Enable": "Увімкнути", "Enable": "Увімкнути",
"Enable dark logo": "Увімкнути темний логотип", "Enable dark logo": "Увімкнути темний логотип",
"Enable dark logo - Tooltip": "Увімкнути темний логотип", "Enable dark logo - Tooltip": "Увімкнути темний логотип",
@@ -311,6 +313,7 @@
"Phone": "Телефон", "Phone": "Телефон",
"Phone - Tooltip": "Номер телефону", "Phone - Tooltip": "Номер телефону",
"Phone only": "Тільки телефон", "Phone only": "Тільки телефон",
"Phone or Email": "Phone or Email",
"Plan": "План", "Plan": "План",
"Plan - Tooltip": "План підказка", "Plan - Tooltip": "План підказка",
"Plans": "Плани", "Plans": "Плани",
@@ -391,6 +394,7 @@
"User type": "Тип користувача", "User type": "Тип користувача",
"User type - Tooltip": "Теги, до яких належить користувач, за умовчанням \"звичайний користувач\"", "User type - Tooltip": "Теги, до яких належить користувач, за умовчанням \"звичайний користувач\"",
"Users": "Користувачі", "Users": "Користувачі",
"Users - Tooltip": "Users - Tooltip",
"Users under all organizations": "Користувачі в усіх організаціях", "Users under all organizations": "Користувачі в усіх організаціях",
"Verifications": "Перевірки", "Verifications": "Перевірки",
"Webhooks": "Веб-хуки", "Webhooks": "Веб-хуки",
@@ -473,7 +477,6 @@
"LDAP username, Email or phone": "Ім’я користувача LDAP, електронна пошта або телефон", "LDAP username, Email or phone": "Ім’я користувача LDAP, електронна пошта або телефон",
"Loading": "Завантаження", "Loading": "Завантаження",
"Logging out...": "Вихід...", "Logging out...": "Вихід...",
"Login button": "Кнопка входу",
"MetaMask plugin not detected": "Плагін MetaMask не виявлено", "MetaMask plugin not detected": "Плагін MetaMask не виявлено",
"Model loading failure": "Помилка завантаження моделі", "Model loading failure": "Помилка завантаження моделі",
"No account?": "Немає облікового запису?", "No account?": "Немає облікового запису?",
@@ -498,6 +501,7 @@
"Sign in with Face ID": "Увійдіть за допомогою Face ID", "Sign in with Face ID": "Увійдіть за допомогою Face ID",
"Sign in with WebAuthn": "Увійдіть за допомогою WebAuthn", "Sign in with WebAuthn": "Увійдіть за допомогою WebAuthn",
"Sign in with {type}": "Увійдіть за допомогою {type}", "Sign in with {type}": "Увійдіть за допомогою {type}",
"Signin button": "Signin button",
"Signing in...": "Вхід...", "Signing in...": "Вхід...",
"Successfully logged in with WebAuthn credentials": "Успішно ввійшли за допомогою облікових даних WebAuthn", "Successfully logged in with WebAuthn credentials": "Успішно ввійшли за допомогою облікових даних WebAuthn",
"The camera is currently in use by another webpage": "Камера зараз використовується іншою веб-сторінкою", "The camera is currently in use by another webpage": "Камера зараз використовується іншою веб-сторінкою",
@@ -774,6 +778,8 @@
"From address - Tooltip": "Електронна адреса \"Від\"", "From address - Tooltip": "Електронна адреса \"Від\"",
"From name": "Від імені", "From name": "Від імені",
"From name - Tooltip": "Назва \"Від\"", "From name - Tooltip": "Назва \"Від\"",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Хост", "Host": "Хост",
"Host - Tooltip": "Ім'я хоста", "Host - Tooltip": "Ім'я хоста",
"IdP": "IDP", "IdP": "IDP",
@@ -893,7 +899,8 @@
"record": { "record": {
"Is triggered": "Спрацьовує", "Is triggered": "Спрацьовує",
"Object": "Об'єкт", "Object": "Об'єкт",
"Response": "Відповідь" "Response": "Відповідь",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Копіювати посилання", "Copy Link": "Копіювати посилання",
@@ -941,6 +948,7 @@
"Please select your country code!": "Виберіть код країни!", "Please select your country code!": "Виберіть код країни!",
"Please select your country/region!": "Виберіть свою країну/регіон!", "Please select your country/region!": "Виберіть свою країну/регіон!",
"Regex": "Регулярний вираз", "Regex": "Регулярний вираз",
"Signup button": "Signup button",
"Terms of Use": "Умови використання", "Terms of Use": "Умови використання",
"Terms of Use - Tooltip": "Умови використання, з якими користувачі повинні ознайомитися та погодитися під час реєстрації", "Terms of Use - Tooltip": "Умови використання, з якими користувачі повинні ознайомитися та погодитися під час реєстрації",
"Text 1": "Текст 1", "Text 1": "Текст 1",
@@ -948,6 +956,7 @@
"Text 3": "Текст 3", "Text 3": "Текст 3",
"Text 4": "Текст 4", "Text 4": "Текст 4",
"Text 5": "Текст 5", "Text 5": "Текст 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "Введені дані не є ідентифікаційним номером платника податків!", "The input is not invoice Tax ID!": "Введені дані не є ідентифікаційним номером платника податків!",
"The input is not invoice title!": "Введені дані не є назвою рахунку!", "The input is not invoice title!": "Введені дані не є назвою рахунку!",
"The input is not valid Email!": "Введена недійсна адреса електронної пошти!", "The input is not valid Email!": "Введена недійсна адреса електронної пошти!",
@@ -1068,6 +1077,7 @@
"3rd-party logins - Tooltip": "Соціальні входи, пов’язані користувачем", "3rd-party logins - Tooltip": "Соціальні входи, пов’язані користувачем",
"Address": "Адреса", "Address": "Адреса",
"Address - Tooltip": "Адреса місця проживання", "Address - Tooltip": "Адреса місця проживання",
"Address line": "Address line",
"Affiliation": "Приналежність", "Affiliation": "Приналежність",
"Affiliation - Tooltip": "Роботодавець, наприклад назва компанії чи організації", "Affiliation - Tooltip": "Роботодавець, наприклад назва компанії чи організації",
"Bio": "біографія", "Bio": "біографія",
@@ -1120,6 +1130,8 @@
"Managed accounts": "Керовані облікові записи", "Managed accounts": "Керовані облікові записи",
"Modify password...": "Змінити пароль...", "Modify password...": "Змінити пароль...",
"Multi-factor authentication": "Багатофакторна аутентифікація", "Multi-factor authentication": "Багатофакторна аутентифікація",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Нова електронна пошта", "New Email": "Нова електронна пошта",
"New Password": "Новий пароль", "New Password": "Новий пароль",
"New User": "Новий користувач", "New User": "Новий користувач",
@@ -1162,9 +1174,11 @@
"Values": "Цінності", "Values": "Цінності",
"Verification code sent": "Код підтвердження надіслано", "Verification code sent": "Код підтвердження надіслано",
"WebAuthn credentials": "Облікові дані WebAuthn", "WebAuthn credentials": "Облікові дані WebAuthn",
"You have changed the username, please save your change first before modifying the password": "You have changed the username, please save your change first before modifying the password",
"input password": "введіть пароль" "input password": "введіть пароль"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Приймач" "Receiver": "Приймач"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "Bước tiếp theo", "Next Step": "Bước tiếp theo",
"Please input your username!": "Vui lòng nhập tên đăng nhập của bạn!", "Please input your username!": "Vui lòng nhập tên đăng nhập của bạn!",
"Reset": "Đặt lại", "Reset": "Đặt lại",
"Retrieve password": "Truy xuất mật khẩu", "Reset password": "Truy xuất mật khẩu",
"Unknown forget type": "Loại quên chưa biết", "Unknown forget type": "Loại quên chưa biết",
"Verify": "Xác thực" "Verify": "Xác thực"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "From address - Tooltip", "From address - Tooltip": "From address - Tooltip",
"From name": "From name", "From name": "From name",
"From name - Tooltip": "From name - Tooltip", "From name - Tooltip": "From name - Tooltip",
"Get phone number": "Get phone number",
"Get phone number - Tooltip": "If sync phone number is enabled, you should enable google people api first and add scope https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "Chủ nhà", "Host": "Chủ nhà",
"Host - Tooltip": "Tên của người chủ chỗ ở", "Host - Tooltip": "Tên của người chủ chỗ ở",
"IdP": "IdP", "IdP": "IdP",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "Is triggered", "Is triggered": "Is triggered",
"Object": "Object", "Object": "Object",
"Response": "Response" "Response": "Response",
"Status code": "Status code"
}, },
"resource": { "resource": {
"Copy Link": "Sao chép liên kết", "Copy Link": "Sao chép liên kết",
@@ -953,6 +956,7 @@
"Text 3": "Text 3", "Text 3": "Text 3",
"Text 4": "Text 4", "Text 4": "Text 4",
"Text 5": "Text 5", "Text 5": "Text 5",
"The input Email doesn't match the signup item regex!": "The input Email doesn't match the signup item regex!",
"The input is not invoice Tax ID!": "Đầu vào không phải là Mã số thuế của hóa đơn!", "The input is not invoice Tax ID!": "Đầu vào không phải là Mã số thuế của hóa đơn!",
"The input is not invoice title!": "Đầu vào không phải là tiêu đề hóa đơn!", "The input is not invoice title!": "Đầu vào không phải là tiêu đề hóa đơn!",
"The input is not valid Email!": "Đầu vào không phải là địa chỉ Email hợp lệ!", "The input is not valid Email!": "Đầu vào không phải là địa chỉ Email hợp lệ!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "Đăng nhập xã hội liên kết bởi người dùng", "3rd-party logins - Tooltip": "Đăng nhập xã hội liên kết bởi người dùng",
"Address": "Địa chỉ", "Address": "Địa chỉ",
"Address - Tooltip": "Địa chỉ cư trú", "Address - Tooltip": "Địa chỉ cư trú",
"Address line": "Address line",
"Affiliation": "Liên kết", "Affiliation": "Liên kết",
"Affiliation - Tooltip": "Nhà tuyển dụng, chẳng hạn như tên công ty hoặc tổ chức", "Affiliation - Tooltip": "Nhà tuyển dụng, chẳng hạn như tên công ty hoặc tổ chức",
"Bio": "bản vẻ đời sống", "Bio": "bản vẻ đời sống",
@@ -1125,6 +1130,8 @@
"Managed accounts": "Quản lý tài khoản", "Managed accounts": "Quản lý tài khoản",
"Modify password...": "Sửa đổi mật khẩu...", "Modify password...": "Sửa đổi mật khẩu...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"New Email": "Email mới", "New Email": "Email mới",
"New Password": "Mật khẩu mới", "New Password": "Mật khẩu mới",
"New User": "Người dùng mới", "New User": "Người dùng mới",
@@ -1171,6 +1178,7 @@
"input password": "Nhập mật khẩu" "input password": "Nhập mật khẩu"
}, },
"verification": { "verification": {
"Is used": "Is used",
"Receiver": "Receiver" "Receiver": "Receiver"
}, },
"webhook": { "webhook": {

View File

@@ -166,7 +166,7 @@
"Next Step": "下一步", "Next Step": "下一步",
"Please input your username!": "请输入您的用户名!", "Please input your username!": "请输入您的用户名!",
"Reset": "重置", "Reset": "重置",
"Retrieve password": "找回密码", "Reset password": "重置密码",
"Unknown forget type": "未知的忘记密码类型", "Unknown forget type": "未知的忘记密码类型",
"Verify": "验证" "Verify": "验证"
}, },
@@ -778,6 +778,8 @@
"From address - Tooltip": "邮件里发件人的邮箱地址", "From address - Tooltip": "邮件里发件人的邮箱地址",
"From name": "发件人名称", "From name": "发件人名称",
"From name - Tooltip": "邮件里发件人的显示名称", "From name - Tooltip": "邮件里发件人的显示名称",
"Get phone number": "获取手机号码",
"Get phone number - Tooltip": "如果启用获取手机号码你需要先启用peopleApi并添加范围https://www.googleapis.com/auth/user.phonenumbers.read",
"Host": "主机", "Host": "主机",
"Host - Tooltip": "主机名", "Host - Tooltip": "主机名",
"IdP": "身份提供商", "IdP": "身份提供商",
@@ -897,7 +899,8 @@
"record": { "record": {
"Is triggered": "是否触发", "Is triggered": "是否触发",
"Object": "实体", "Object": "实体",
"Response": "响应" "Response": "响应",
"Status code": "状态码"
}, },
"resource": { "resource": {
"Copy Link": "复制链接", "Copy Link": "复制链接",
@@ -953,6 +956,7 @@
"Text 3": "文本3", "Text 3": "文本3",
"Text 4": "文本4", "Text 4": "文本4",
"Text 5": "文本5", "Text 5": "文本5",
"The input Email doesn't match the signup item regex!": "您输入的邮箱地址与注册项的regex表达式不匹配!",
"The input is not invoice Tax ID!": "您输入的纳税人识别号有误!", "The input is not invoice Tax ID!": "您输入的纳税人识别号有误!",
"The input is not invoice title!": "您输入的发票抬头有误!", "The input is not invoice title!": "您输入的发票抬头有误!",
"The input is not valid Email!": "您输入的电子邮箱格式有误!", "The input is not valid Email!": "您输入的电子邮箱格式有误!",
@@ -1073,6 +1077,7 @@
"3rd-party logins - Tooltip": "用户所绑定的社会化登录", "3rd-party logins - Tooltip": "用户所绑定的社会化登录",
"Address": "地址", "Address": "地址",
"Address - Tooltip": "居住地址", "Address - Tooltip": "居住地址",
"Address line": "地址",
"Affiliation": "工作单位", "Affiliation": "工作单位",
"Affiliation - Tooltip": "工作单位,如公司、组织名称", "Affiliation - Tooltip": "工作单位,如公司、组织名称",
"Bio": "自我介绍", "Bio": "自我介绍",
@@ -1125,6 +1130,8 @@
"Managed accounts": "托管账户", "Managed accounts": "托管账户",
"Modify password...": "编辑密码...", "Modify password...": "编辑密码...",
"Multi-factor authentication": "多因素认证", "Multi-factor authentication": "多因素认证",
"Need update password": "需要更新密码",
"Need update password - Tooltip": "强制用户在登录后更新密码",
"New Email": "新邮箱", "New Email": "新邮箱",
"New Password": "新密码", "New Password": "新密码",
"New User": "添加用户", "New User": "添加用户",
@@ -1171,6 +1178,7 @@
"input password": "输入密码" "input password": "输入密码"
}, },
"verification": { "verification": {
"Is used": "已使用",
"Receiver": "接收者" "Receiver": "接收者"
}, },
"webhook": { "webhook": {

View File

@@ -102,6 +102,7 @@ class AccountTable extends React.Component {
{name: "Is admin", label: i18next.t("user:Is admin")}, {name: "Is admin", label: i18next.t("user:Is admin")},
{name: "Is forbidden", label: i18next.t("user:Is forbidden")}, {name: "Is forbidden", label: i18next.t("user:Is forbidden")},
{name: "Is deleted", label: i18next.t("user:Is deleted")}, {name: "Is deleted", label: i18next.t("user:Is deleted")},
{name: "Need update password", label: i18next.t("user:Need update password")},
{name: "Multi-factor authentication", label: i18next.t("user:Multi-factor authentication")}, {name: "Multi-factor authentication", label: i18next.t("user:Multi-factor authentication")},
{name: "WebAuthn credentials", label: i18next.t("user:WebAuthn credentials")}, {name: "WebAuthn credentials", label: i18next.t("user:WebAuthn credentials")},
{name: "Managed accounts", label: i18next.t("user:Managed accounts")}, {name: "Managed accounts", label: i18next.t("user:Managed accounts")},

View File

@@ -15,6 +15,7 @@
import React from "react"; import React from "react";
import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons"; import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Input, Row, Select, Switch, Table, Tooltip} from "antd"; import {Button, Col, Input, Row, Select, Switch, Table, Tooltip} from "antd";
import {CountryCodeSelect} from "../common/select/CountryCodeSelect";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import i18next from "i18next"; import i18next from "i18next";
import * as Provider from "../auth/Provider"; import * as Provider from "../auth/Provider";
@@ -29,6 +30,10 @@ class ProviderTable extends React.Component {
}; };
} }
getUserOrganization() {
return this.props.application?.organizationObj;
}
updateTable(table) { updateTable(table) {
this.props.onUpdateTable(table); this.props.onUpdateTable(table);
} }
@@ -79,7 +84,7 @@ class ProviderTable extends React.Component {
// If the provider is email or SMS, set the rule to "all" instead of the default "None" // If the provider is email or SMS, set the rule to "all" instead of the default "None"
if (provider.category === "Email" || provider.category === "SMS") { if (provider.category === "Email" || provider.category === "SMS") {
this.updateField(table, index, "rule", "all"); this.updateField(table, index, "rule", "All");
} }
}} > }} >
{ {
@@ -109,6 +114,30 @@ class ProviderTable extends React.Component {
return Provider.getProviderLogoWidget(provider); return Provider.getProviderLogoWidget(provider);
}, },
}, },
{
title: i18next.t("user:Country/Region"),
dataIndex: "countryCodes",
key: "countryCodes",
width: "140px",
render: (text, record, index) => {
if (record.provider?.category !== "SMS") {
return null;
}
return (
<CountryCodeSelect
style={{width: "100%"}}
hasDefault={true}
mode={"multiple"}
initValue={text ? text : ["All"]}
onChange={(value) => {
this.updateField(table, index, "countryCodes", value);
}}
countryCodes={this.getUserOrganization()?.countryCodes}
/>
);
},
},
{ {
title: i18next.t("provider:Can signup"), title: i18next.t("provider:Can signup"),
dataIndex: "canSignUp", dataIndex: "canSignUp",
@@ -198,7 +227,7 @@ class ProviderTable extends React.Component {
title: i18next.t("application:Rule"), title: i18next.t("application:Rule"),
dataIndex: "rule", dataIndex: "rule",
key: "rule", key: "rule",
width: "120px", width: "160px",
render: (text, record, index) => { render: (text, record, index) => {
if (record.provider?.type === "Google") { if (record.provider?.type === "Google") {
if (text === "None") { if (text === "None") {
@@ -230,12 +259,12 @@ class ProviderTable extends React.Component {
); );
} else if (record.provider?.category === "SMS" || record.provider?.category === "Email") { } else if (record.provider?.category === "SMS" || record.provider?.category === "Email") {
if (text === "None") { if (text === "None") {
text = "all"; text = "All";
} }
return ( return (
<Select virtual={false} style={{width: "100%"}} <Select virtual={false} style={{width: "100%"}}
value={text} value={text}
defaultValue="all" defaultValue="All"
onChange={value => { onChange={value => {
this.updateField(table, index, "rule", value); this.updateField(table, index, "rule", value);
}}> }}>

View File

@@ -134,7 +134,7 @@ class SigninTable extends React.Component {
value={getItemDisplayName(text)} value={getItemDisplayName(text)}
onChange={value => { onChange={value => {
this.updateField(table, index, "name", value); this.updateField(table, index, "name", value);
this.updateField(table, index, "label", SigninTableDefaultCssMap[value]); this.updateField(table, index, "customCss", SigninTableDefaultCssMap[value]);
}} > }} >
{ {
Setting.getDeduplicatedArray(items, table, "name").map((item, index) => <Option key={index} value={item.name}>{item.displayName}</Option>) Setting.getDeduplicatedArray(items, table, "name").map((item, index) => <Option key={index} value={item.name}>{item.displayName}</Option>)
@@ -166,7 +166,7 @@ class SigninTable extends React.Component {
}, },
}, },
{ {
title: i18next.t("signup:Label HTML"), title: i18next.t("signup:Label"),
dataIndex: "label", dataIndex: "label",
key: "label", key: "label",
width: "200px", width: "200px",
@@ -188,14 +188,18 @@ class SigninTable extends React.Component {
}} /> }} />
</Popover> </Popover>
); );
} else if (["Username", "Password", "Signup link", "Forgot password?", "Login button"].includes(record.name)) {
return <Input value={text} style={{marginBottom: "10px"}} onChange={e => {
this.updateField(table, index, "label", e.target.value);
}} />;
} }
return null; return null;
}, },
}, },
{ {
title: i18next.t("application:Custom CSS"), title: i18next.t("application:Custom CSS"),
dataIndex: "label", dataIndex: "customCss",
key: "label", key: "customCss",
width: "200px", width: "200px",
render: (text, record, index) => { render: (text, record, index) => {
if (!record.name.startsWith("Text ") && !record?.isCustom) { if (!record.name.startsWith("Text ") && !record?.isCustom) {
@@ -205,13 +209,13 @@ class SigninTable extends React.Component {
<CodeMirror value={text?.replaceAll("<style>", "").replaceAll("</style>", "")} <CodeMirror value={text?.replaceAll("<style>", "").replaceAll("</style>", "")}
options={{mode: "css", theme: "material-darker"}} options={{mode: "css", theme: "material-darker"}}
onBeforeChange={(editor, data, value) => { onBeforeChange={(editor, data, value) => {
this.updateField(table, index, "label", value); this.updateField(table, index, "customCss", value);
}} }}
/> />
</div> </div>
} title={i18next.t("application:CSS style")} trigger="click"> } title={i18next.t("application:CSS style")} trigger="click">
<Input value={text?.replaceAll("<style>", "").replaceAll("</style>", "")} onChange={e => { <Input value={text?.replaceAll("<style>", "").replaceAll("</style>", "")} onChange={e => {
this.updateField(table, index, "label", e.target.value); this.updateField(table, index, "customCss", e.target.value);
}} /> }} />
</Popover> </Popover>
); );