Compare commits

...

36 Commits

Author SHA1 Message Date
79b393afee feat: add regex to SignupTable 2024-01-13 16:08:49 +08:00
5bb12a30d4 Don't show two errors in verificationCode login page 2024-01-13 16:01:22 +08:00
fdb68bf9c8 Rename to SigninMethodTable 2024-01-13 15:53:01 +08:00
37748850c8 Fix nameFormat in SamlItem 2024-01-13 15:32:49 +08:00
8968396ae5 Fix bug in getDefaultLoginMethod() 2024-01-13 12:13:09 +08:00
f5395f15f9 feat: fix isSigninMethodEnabled() bug in frontend 2024-01-13 11:35:06 +08:00
73e44df867 Improve GetAllRoles() error handling 2024-01-13 10:06:08 +08:00
0b575ccf84 Refactor getAllValues() 2024-01-13 09:58:55 +08:00
9b7f465a47 Fix failedSigninFrozenTime typo 2024-01-13 02:12:29 +08:00
b1fe28fb83 Refactor application.FailedSigninLimit code 2024-01-13 02:09:18 +08:00
530d054adb feat: ci should commit index.yaml and push to docker hub (#2600) 2024-01-11 16:10:08 +08:00
a2b9f9baaf feat: support "JWT-Custom" to customize user properties inside access token (#2594)
* feat: add custom attribute to access token

* Update token_jwt.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2024-01-10 00:59:02 +08:00
a2d20fcb63 Update i18n 2024-01-09 22:16:17 +08:00
b118a3bb76 Add TokenFields to application 2024-01-09 22:09:21 +08:00
280867d0cb Add checkSigninErrorTimes() for LDAP signin 2024-01-09 21:53:44 +08:00
30fa2f7d81 Disable LDAP login method by default 2024-01-09 21:36:09 +08:00
518288691d fix(ci): fix the helm publish step (#2593)
fixes https://github.com/casdoor/casdoor-helm/issues/3
2024-01-09 17:48:01 +08:00
ffa54247cd feat: add LDAP signin method (#2591)
Add support for LDAP login methods
Add option to control LDAP user in password login method.
2024-01-08 21:07:34 +08:00
0199ad9aaa fix: missing table prefix in get user group (#2590)
- Sort field and order field are missing table name prefix

Co-authored-by: xgenvn <brian7.ng@gmail.com>
2024-01-08 21:07:13 +08:00
b9d171718f chore(helm): move to dedicated helm-repo (#2587)
* chore(helm): move to dedicated helm-repo: https://github.com/casdoor/casdoor-helm

* feat(actions): explicit checkout helm repo

* chore: feedback from pr comment
2024-01-08 02:02:05 +08:00
e841d0ba8e feat: fix /api/send-email API for app user 2024-01-07 21:11:22 +08:00
e5a9594f90 Hide Google OneTap in iframe 2024-01-07 10:33:25 +08:00
c542929835 fix: add vscode local debugging support (#2585) 2024-01-07 09:26:33 +08:00
86dea71efd ci: update helm index.yaml 2024-01-06 19:31:07 +00:00
9e536850fd feat(helm): support for extra volume mounts (#2584)
* feat(helm): support for extraVolumes and extraVolumeMounts

* ci(helm): run helm unittests
2024-01-07 03:30:44 +08:00
fddd4a12b8 chore: update helm version to v1.492.0 (#2582) 2024-01-07 00:14:53 +08:00
2d6fae32be feat: support custom config path via "config" 2024-01-06 14:09:48 +08:00
741cff99df Remove isCreateDatabaseDefined 2024-01-06 14:08:34 +08:00
cad9c28e92 feat: helm hpa yaml must reference correct apiVersion (#2581) 2024-01-06 08:55:59 +08:00
524cf4dda5 feat: fix update application failed for permissions with the same name (#2579)
* fixed: update application failed where have two same permission in different organization

* Update application.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2024-01-05 20:45:55 +08:00
077a1cb8b7 fix: support owner parameter in enforce API (#2578) 2024-01-05 15:12:59 +08:00
00efdf1d03 Fix EmailVerified in UserInfo() 2024-01-05 09:37:42 +08:00
aa543f1abb feat: more RFC like LDAP server behaviour (#2574)
* feat: more RFC like LDAP server behaviour

* Extend FieldRelationMap to support case insensitive mapping, add more fields definition

* feat: Add group syncing for LDAP server
2024-01-05 09:24:12 +08:00
1d1d3049bd feat: fix dropped getAffiliationMap error in object (#2576) 2024-01-05 09:03:39 +08:00
4f497d44a5 Enable at least password login in extendApplicationWithSigninMethods() 2024-01-03 22:19:43 +08:00
369de36987 feat: add users with correct application (#2570) 2024-01-02 23:49:04 +08:00
97 changed files with 1067 additions and 810 deletions

View File

@ -1,6 +1,6 @@
name: Build
on: [push, pull_request]
on: [ push, pull_request ]
jobs:
@ -167,10 +167,8 @@ jobs:
elif [ ${old_array[1]} != ${new_array[1]} ]
then
echo ::set-output name=push::'true'
else
echo ::set-output name=push::'false'
fi
- name: Set up QEMU
@ -208,3 +206,36 @@ jobs:
platforms: linux/amd64
push: true
tags: casbin/casdoor-all-in-one:${{steps.get-current-tag.outputs.tag }},casbin/casdoor-all-in-one:latest
- uses: actions/checkout@v3
if: steps.should_push.outputs.push=='true'
with:
repository: casdoor/casdoor-helm
ref: 'master'
token: ${{ secrets.GH_BOT_TOKEN }}
- name: Update Helm Chart
if: steps.should_push.outputs.push=='true'
run: |
# Set the appVersion of the chart to the current tag
sed -i "s/appVersion: .*/appVersion: ${{steps.get-current-tag.outputs.tag }}/g" ./charts/casdoor/Chart.yaml
# increase the patch version of the chart
currentChartVersion=$(cat ./charts/casdoor/Chart.yaml | grep ^version | awk '{print $2}')
newChartVersion=$(echo $currentChartVersion | awk -F. -v OFS=. '{$NF++;print}')
sed -i "s/version: .*/version: $newChartVersion/g" ./charts/casdoor/Chart.yaml
REGISTRY=oci://registry-1.docker.io/casbin
cd charts/casdoor
helm package .
PKG_NAME=$(ls *.tgz)
helm repo index . --url $REGISTRY --merge index.yaml
helm push $PKG_NAME $REGISTRY
rm $PKG_NAME
# Commit and push the changes back to the repository
git config --global user.name "casbin-bot"
git config --global user.email "casbin-bot@github.com"
git add Chart.yaml index.yaml
git commit -m "chore(helm): bump helm charts appVersion to ${{steps.get-current-tag.outputs.tag }}"
git push origin HEAD:master

View File

@ -1,40 +0,0 @@
name: Helm Release
on:
push:
branches:
- master
paths:
- 'manifests/casdoor/Chart.yaml'
jobs:
release-helm-chart:
name: Release Helm Chart
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Helm
uses: azure/setup-helm@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Release Helm Chart
run: |
cd manifests/casdoor
REGISTRY=oci://registry-1.docker.io/casbin
helm package .
PKG_NAME=$(ls *.tgz)
helm repo index . --url $REGISTRY --merge index.yaml
helm push $PKG_NAME $REGISTRY
rm $PKG_NAME
- name: Commit updated helm index.yaml
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: 'ci: update helm index.yaml'

5
.gitignore vendored
View File

@ -18,7 +18,7 @@ bin/
.idea/
*.iml
.vscode/
.vscode/settings.json
tmp/
tmpFiles/
@ -31,3 +31,6 @@ commentsRouter*.go
# ignore build result
casdoor
server
# include helm-chart
!manifests/casdoor

15
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"cwd": "${workspaceFolder}",
"debugAdapter": "dlv-dap",
"args": ["--createDatabase=true"]
}
]
}

View File

@ -86,6 +86,9 @@ docker-build: ## Build docker image with the manager.
docker-push: ## Push docker image with the manager.
docker push ${REGISTRY}/${IMG}:${IMG_TAG}
deps: ## Run dependencies for local development
docker compose up -d db
lint-install: ## Install golangci-lint
@# The following installs a specific version of golangci-lint, which is appropriate for a CI server to avoid different results from build to build
go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.40.1

View File

@ -110,14 +110,6 @@ func (c *ApiController) GetApplication() {
}
}
// 0 as an initialization value, corresponding to the default configuration parameters
if application.FailedSigninLimit == 0 {
application.FailedSigninLimit = object.DefaultFailedSigninLimit
}
if application.FailedSigninfrozenTime == 0 {
application.FailedSigninfrozenTime = object.DefaultFailedSigninfrozenTime
}
c.ResponseOk(object.GetMaskedApplication(application, userId))
}

View File

@ -399,10 +399,14 @@ func (c *ApiController) Login() {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
return
}
if !application.IsPasswordEnabled() {
if authForm.SigninMethod == "Password" && !application.IsPasswordEnabled() {
c.ResponseError(c.T("auth:The login method: login with password is not enabled for the application"))
return
}
if authForm.SigninMethod == "LDAP" && !application.IsLdapEnabled() {
c.ResponseError(c.T("auth:The login method: login with LDAP is not enabled for the application"))
return
}
var enableCaptcha bool
if enableCaptcha, err = object.CheckToEnableCaptcha(application, authForm.Organization, authForm.Username); err != nil {
c.ResponseError(err.Error())
@ -432,7 +436,14 @@ func (c *ApiController) Login() {
}
password := authForm.Password
user, err = object.CheckUserPassword(authForm.Organization, authForm.Username, password, c.GetAcceptLanguage(), enableCaptcha)
isSigninViaLdap := authForm.SigninMethod == "LDAP"
var isPasswordWithLdapEnabled bool
if authForm.SigninMethod == "Password" {
isPasswordWithLdapEnabled = application.IsPasswordWithLdapEnabled()
} else {
isPasswordWithLdapEnabled = false
}
user, err = object.CheckUserPassword(authForm.Organization, authForm.Username, password, c.GetAcceptLanguage(), enableCaptcha, isSigninViaLdap, isPasswordWithLdapEnabled)
}
if err != nil {

View File

@ -30,6 +30,7 @@ import (
// @Param permissionId query string false "permission id"
// @Param modelId query string false "model id"
// @Param resourceId query string false "resource id"
// @Param owner query string false "owner"
// @Success 200 {object} controllers.Response The Response object
// @router /enforce [post]
func (c *ApiController) Enforce() {
@ -37,6 +38,7 @@ func (c *ApiController) Enforce() {
modelId := c.Input().Get("modelId")
resourceId := c.Input().Get("resourceId")
enforcerId := c.Input().Get("enforcerId")
owner := c.Input().Get("owner")
if len(c.Ctx.Input.RequestBody) == 0 {
c.ResponseError("The request body should not be empty")
@ -117,6 +119,8 @@ func (c *ApiController) Enforce() {
c.ResponseError(err.Error())
return
}
} else if owner != "" {
permissions, err = object.GetPermissions(owner)
} else {
c.ResponseError(c.T("general:Missing parameter"))
return
@ -152,12 +156,14 @@ func (c *ApiController) Enforce() {
// @Param body body []string true "array of casbin requests"
// @Param permissionId query string false "permission id"
// @Param modelId query string false "model id"
// @Param owner query string false "owner"
// @Success 200 {object} controllers.Response The Response object
// @router /batch-enforce [post]
func (c *ApiController) BatchEnforce() {
permissionId := c.Input().Get("permissionId")
modelId := c.Input().Get("modelId")
enforcerId := c.Input().Get("enforcerId")
owner := c.Input().Get("owner")
var requests [][]string
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
@ -227,6 +233,8 @@ func (c *ApiController) BatchEnforce() {
c.ResponseError(err.Error())
return
}
} else if owner != "" {
permissions, err = object.GetPermissions(owner)
} else {
c.ResponseError(c.T("general:Missing parameter"))
return

View File

@ -54,7 +54,7 @@ type NotificationForm struct {
// @Success 200 {object} controllers.Response The Response object
// @router /api/send-email [post]
func (c *ApiController) SendEmail() {
user, ok := c.RequireSignedInUser()
userId, ok := c.RequireSignedIn()
if !ok {
return
}
@ -116,8 +116,17 @@ func (c *ApiController) SendEmail() {
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
content := strings.Replace(provider.Content, "%s", code, 1)
if user != nil {
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
if !strings.HasPrefix(userId, "app/") {
var user *object.User
user, err = object.GetUser(userId)
if err != nil {
c.ResponseError(err.Error())
return
}
if user != nil {
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
}
}
for _, receiver := range emailForm.Receivers {

View File

@ -15,7 +15,8 @@
package form
type AuthForm struct {
Type string `json:"type"`
Type string `json:"type"`
SigninMethod string `json:"signinMethod"`
Organization string `json:"organization"`
Username string `json:"username"`

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Das Konto für den Anbieter %s und Benutzernamen %s (%s) existiert nicht und es ist nicht erlaubt, ein neues Konto anzumelden. Bitte wenden Sie sich an Ihren IT-Support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Das Konto für den Anbieter %s und Benutzernamen %s (%s) ist bereits mit einem anderen Konto verknüpft: %s (%s)",
"The application: %s does not exist": "Die Anwendung: %s existiert nicht",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "Die Anmeldeart \"Anmeldung mit Passwort\" ist für die Anwendung nicht aktiviert",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "La cuenta para el proveedor: %s y el nombre de usuario: %s (%s) no existe y no se permite registrarse como una nueva cuenta, por favor contacte a su soporte de TI",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "La cuenta para proveedor: %s y nombre de usuario: %s (%s) ya está vinculada a otra cuenta: %s (%s)",
"The application: %s does not exist": "La aplicación: %s no existe",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "El método de inicio de sesión: inicio de sesión con contraseña no está habilitado para la aplicación",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Le compte pour le fournisseur : %s et le nom d'utilisateur : %s (%s) n'existe pas et n'est pas autorisé à s'inscrire comme nouveau compte, veuillez contacter votre support informatique",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Le compte du fournisseur : %s et le nom d'utilisateur : %s (%s) sont déjà liés à un autre compte : %s (%s)",
"The application: %s does not exist": "L'application : %s n'existe pas",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "La méthode de connexion : connexion avec mot de passe n'est pas activée pour l'application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Akun untuk penyedia: %s dan nama pengguna: %s (%s) tidak ada dan tidak diizinkan untuk mendaftar sebagai akun baru, silakan hubungi dukungan IT Anda",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Akun untuk provider: %s dan username: %s (%s) sudah terhubung dengan akun lain: %s (%s)",
"The application: %s does not exist": "Aplikasi: %s tidak ada",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "Metode login: login dengan kata sandi tidak diaktifkan untuk aplikasi tersebut",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "プロバイダー名:%sとユーザー名%s%sのアカウントは存在しません。新しいアカウントとしてサインアップすることはできません。 ITサポートに連絡してください",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "プロバイダのアカウント:%s とユーザー名:%s (%s) は既に別のアカウント:%s (%s) にリンクされています",
"The application: %s does not exist": "アプリケーション: %sは存在しません",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "ログイン方法:パスワードでのログインはアプリケーションで有効になっていません",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "공급자 계정 %s과 사용자 이름 %s (%s)는 존재하지 않으며 새 계정으로 등록할 수 없습니다. IT 지원팀에 문의하십시오",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "공급자 계정 %s과 사용자 이름 %s(%s)는 이미 다른 계정 %s(%s)에 연결되어 있습니다",
"The application: %s does not exist": "해당 애플리케이션(%s)이 존재하지 않습니다",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "어플리케이션에서는 암호를 사용한 로그인 방법이 활성화되어 있지 않습니다",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Аккаунт для провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован как новый аккаунт. Пожалуйста, обратитесь в службу поддержки IT",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Аккаунт поставщика: %s и имя пользователя: %s (%s) уже связаны с другим аккаунтом: %s (%s)",
"The application: %s does not exist": "Приложение: %s не существует",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) không tồn tại và không được phép đăng ký như một tài khoản mới, vui lòng liên hệ với bộ phận hỗ trợ công nghệ thông tin của bạn",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Tài khoản cho nhà cung cấp: %s và tên người dùng: %s (%s) đã được liên kết với tài khoản khác: %s (%s)",
"The application: %s does not exist": "Ứng dụng: %s không tồn tại",
"The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with password is not enabled for the application": "Phương thức đăng nhập: đăng nhập bằng mật khẩu không được kích hoạt cho ứng dụng",

View File

@ -15,6 +15,7 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "提供商账户: %s 与用户名: %s (%s) 不存在且 不允许注册新账户, 请联系IT支持",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "提供商账户: %s与用户名: %s (%s)已经与其他账户绑定: %s (%s)",
"The application: %s does not exist": "应用%s不存在",
"The login method: login with LDAP is not enabled for the application": "该应用禁止采用LDAP登录方式",
"The login method: login with SMS is not enabled for the application": "该应用禁止采用短信登录方式",
"The login method: login with email is not enabled for the application": "该应用禁止采用邮箱登录方式",
"The login method: login with password is not enabled for the application": "该应用禁止采用密码登录方式",

View File

@ -49,7 +49,7 @@
{
"name": "Password",
"displayName": "Password",
"rule": "None",
"rule": "All",
},
{
"name": "Verification code",
@ -123,7 +123,7 @@
"redirectUris": [""],
"expireInHours": 168,
"failedSigninLimit": 5,
"failedSigninfrozenTime": 15
"failedSigninFrozenTime": 15
}
],
"users": [

View File

@ -18,6 +18,7 @@ import (
"fmt"
"hash/fnv"
"log"
"strings"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object"
@ -49,7 +50,17 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
res := ldap.NewBindResponse(ldap.LDAPResultSuccess)
if r.AuthenticationChoice() == "simple" {
bindUsername, bindOrg, err := getNameAndOrgFromDN(string(r.Name()))
bindDN := string(r.Name())
bindPassword := string(r.AuthenticationSimple())
if bindDN == "" && bindPassword == "" {
res.SetResultCode(ldap.LDAPResultInappropriateAuthentication)
res.SetDiagnosticMessage("Anonymous bind disallowed")
w.Write(res)
return
}
bindUsername, bindOrg, err := getNameAndOrgFromDN(bindDN)
if err != nil {
log.Printf("getNameAndOrgFromDN() error: %s", err.Error())
res.SetResultCode(ldap.LDAPResultInvalidDNSyntax)
@ -58,7 +69,6 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
return
}
bindPassword := string(r.AuthenticationSimple())
bindUser, err := object.CheckUserPassword(bindOrg, bindUsername, bindPassword, "en")
if err != nil {
log.Printf("Bind failed User=%s, Pass=%#v, ErrMsg=%s", string(r.Name()), r.Authentication(), err)
@ -93,7 +103,46 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
}
r := m.GetSearchRequest()
if r.FilterString() == "(objectClass=*)" {
// case insensitive match
if strings.EqualFold(r.FilterString(), "(objectClass=*)") {
if len(r.Attributes()) == 0 {
w.Write(res)
return
}
first_attr := string(r.Attributes()[0])
if string(r.BaseObject()) == "" {
// handle special search requests
if first_attr == "namingContexts" {
orgs, code := GetFilteredOrganizations(m)
if code != ldap.LDAPResultSuccess {
res.SetResultCode(code)
w.Write(res)
return
}
e := ldap.NewSearchResultEntry(string(r.BaseObject()))
dnlist := make([]message.AttributeValue, len(orgs))
for i, org := range orgs {
dnlist[i] = message.AttributeValue(fmt.Sprintf("ou=%s", org.Name))
}
e.AddAttribute("namingContexts", dnlist...)
w.Write(e)
} else if first_attr == "subschemaSubentry" {
e := ldap.NewSearchResultEntry(string(r.BaseObject()))
e.AddAttribute("subschemaSubentry", message.AttributeValue("cn=Subschema"))
w.Write(e)
}
} else if strings.EqualFold(first_attr, "objectclasses") && string(r.BaseObject()) == "cn=Subschema" {
e := ldap.NewSearchResultEntry(string(r.BaseObject()))
e.AddAttribute("objectClasses", []message.AttributeValue{
"( 1.3.6.1.1.1.2.0 NAME 'posixAccount' DESC 'Abstraction of an account with POSIX attributes' SUP top AUXILIARY MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) MAY ( userPassword $ loginShell $ gecos $ description ) )",
"( 1.3.6.1.1.1.2.2 NAME 'posixGroup' DESC 'Abstraction of a group of accounts' SUP top STRUCTURAL MUST ( cn $ gidNumber ) MAY ( userPassword $ memberUid $ description ) )",
}...)
w.Write(e)
}
w.Write(res)
return
}
@ -106,38 +155,72 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
default:
}
users, code := GetFilteredUsers(m)
if code != ldap.LDAPResultSuccess {
res.SetResultCode(code)
w.Write(res)
return
}
for _, user := range users {
dn := fmt.Sprintf("uid=%s,cn=%s,%s", user.Id, user.Name, string(r.BaseObject()))
e := ldap.NewSearchResultEntry(dn)
uidNumberStr := fmt.Sprintf("%v", hash(user.Name))
e.AddAttribute("uidNumber", message.AttributeValue(uidNumberStr))
e.AddAttribute("gidNumber", message.AttributeValue(uidNumberStr))
e.AddAttribute("homeDirectory", message.AttributeValue("/home/"+user.Name))
e.AddAttribute("cn", message.AttributeValue(user.Name))
e.AddAttribute("uid", message.AttributeValue(user.Id))
attrs := r.Attributes()
for _, attr := range attrs {
if string(attr) == "*" {
attrs = AdditionalLdapAttributes
break
}
}
for _, attr := range attrs {
e.AddAttribute(message.AttributeDescription(attr), getAttribute(string(attr), user))
if string(attr) == "cn" {
e.AddAttribute(message.AttributeDescription(attr), getAttribute("title", user))
}
objectClass := searchFilterForEquality(r.Filter(), "objectClass", "posixAccount", "posixGroup")
switch objectClass {
case "posixAccount":
users, code := GetFilteredUsers(m)
if code != ldap.LDAPResultSuccess {
res.SetResultCode(code)
w.Write(res)
return
}
w.Write(e)
// log.Printf("Handling posixAccount filter=%s", r.FilterString())
for _, user := range users {
dn := fmt.Sprintf("uid=%s,cn=users,%s", user.Name, string(r.BaseObject()))
e := ldap.NewSearchResultEntry(dn)
attrs := r.Attributes()
for _, attr := range attrs {
if string(attr) == "*" {
attrs = AdditionalLdapUserAttributes
break
}
}
for _, attr := range attrs {
if strings.HasSuffix(string(attr), ";binary") {
// unsupported: userCertificate;binary
continue
}
field, ok := ldapUserAttributesMapping.CaseInsensitiveGet(string(attr))
if ok {
e.AddAttribute(message.AttributeDescription(attr), field.GetAttributeValues(user)...)
}
}
w.Write(e)
}
case "posixGroup":
// log.Printf("Handling posixGroup filter=%s", r.FilterString())
groups, code := GetFilteredGroups(m)
if code != ldap.LDAPResultSuccess {
res.SetResultCode(code)
w.Write(res)
return
}
for _, group := range groups {
dn := fmt.Sprintf("cn=%s,cn=groups,%s", group.Name, string(r.BaseObject()))
e := ldap.NewSearchResultEntry(dn)
attrs := r.Attributes()
for _, attr := range attrs {
if string(attr) == "*" {
attrs = AdditionalLdapGroupAttributes
break
}
}
for _, attr := range attrs {
field, ok := ldapGroupAttributesMapping.CaseInsensitiveGet(string(attr))
if ok {
e.AddAttribute(message.AttributeDescription(attr), field.GetAttributeValues(group)...)
}
}
w.Write(e)
}
case "":
log.Printf("Unmatched search request. filter=%s", r.FilterString())
}
w.Write(res)
}

View File

@ -18,6 +18,7 @@ import (
"fmt"
"log"
"strings"
"time"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
@ -28,65 +29,259 @@ import (
"github.com/xorm-io/builder"
)
type AttributeMapper func(user *object.User) message.AttributeValue
type V = message.AttributeValue
type FieldRelation struct {
type UserAttributeMapper func(user *object.User) []V
type UserFieldRelation struct {
userField string
ldapField string
notSearchable bool
hideOnStarOp bool
fieldMapper AttributeMapper
fieldMapper UserAttributeMapper
constantValue []V
}
func (rel FieldRelation) GetField() (string, error) {
func (rel UserFieldRelation) GetField() (string, error) {
if rel.notSearchable {
return "", fmt.Errorf("attribute %s not supported", rel.userField)
}
return rel.userField, nil
}
func (rel FieldRelation) GetAttributeValue(user *object.User) message.AttributeValue {
func (rel UserFieldRelation) GetAttributeValues(user *object.User) []V {
if rel.constantValue != nil && rel.fieldMapper == nil {
return rel.constantValue
}
return rel.fieldMapper(user)
}
var ldapAttributesMapping = map[string]FieldRelation{
"cn": {userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Name)
}},
"uid": {userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Name)
}},
"displayname": {userField: "displayName", fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.DisplayName)
}},
"email": {userField: "email", fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Email)
}},
"mail": {userField: "email", fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Email)
}},
"mobile": {userField: "phone", fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Phone)
}},
"title": {userField: "tag", fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(user.Tag)
}},
"userPassword": {
userField: "userPassword",
notSearchable: true,
fieldMapper: func(user *object.User) message.AttributeValue {
return message.AttributeValue(getUserPasswordWithType(user))
},
},
type UserFieldRelationMap map[string]UserFieldRelation
func (m UserFieldRelationMap) CaseInsensitiveGet(key string) (UserFieldRelation, bool) {
lowerKey := strings.ToLower(key)
ret, ok := m[lowerKey]
return ret, ok
}
var AdditionalLdapAttributes []message.LDAPString
type GroupAttributeMapper func(group *object.Group) []V
type GroupFieldRelation struct {
groupField string
ldapField string
notSearchable bool
hideOnStarOp bool
fieldMapper GroupAttributeMapper
constantValue []V
}
func (rel GroupFieldRelation) GetField() (string, error) {
if rel.notSearchable {
return "", fmt.Errorf("attribute %s not supported", rel.groupField)
}
return rel.groupField, nil
}
func (rel GroupFieldRelation) GetAttributeValues(group *object.Group) []V {
if rel.constantValue != nil && rel.fieldMapper == nil {
return rel.constantValue
}
return rel.fieldMapper(group)
}
type GroupFieldRelationMap map[string]GroupFieldRelation
func (m GroupFieldRelationMap) CaseInsensitiveGet(key string) (GroupFieldRelation, bool) {
lowerKey := strings.ToLower(key)
ret, ok := m[lowerKey]
return ret, ok
}
var ldapUserAttributesMapping = UserFieldRelationMap{
"cn": {ldapField: "cn", userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) []V {
return []V{V(user.Name)}
}},
"uid": {ldapField: "uid", userField: "name", hideOnStarOp: true, fieldMapper: func(user *object.User) []V {
return []V{V(user.Name)}
}},
"displayname": {ldapField: "displayName", userField: "displayName", fieldMapper: func(user *object.User) []V {
return []V{V(user.DisplayName)}
}},
"email": {ldapField: "email", userField: "email", fieldMapper: func(user *object.User) []V {
return []V{V(user.Email)}
}},
"mail": {ldapField: "mail", userField: "email", fieldMapper: func(user *object.User) []V {
return []V{V(user.Email)}
}},
"mobile": {ldapField: "mobile", userField: "phone", fieldMapper: func(user *object.User) []V {
return []V{V(user.Phone)}
}},
"telephonenumber": {ldapField: "telephoneNumber", userField: "phone", fieldMapper: func(user *object.User) []V {
return []V{V(user.Phone)}
}},
"postaladdress": {ldapField: "postalAddress", userField: "address", fieldMapper: func(user *object.User) []V {
return []V{V(strings.Join(user.Address, " "))}
}},
"title": {ldapField: "title", userField: "title", fieldMapper: func(user *object.User) []V {
return []V{V(user.Title)}
}},
"gecos": {ldapField: "gecos", userField: "displayName", fieldMapper: func(user *object.User) []V {
return []V{V(user.DisplayName)}
}},
"description": {ldapField: "description", userField: "displayName", fieldMapper: func(user *object.User) []V {
return []V{V(user.DisplayName)}
}},
"logindisabled": {ldapField: "loginDisabled", userField: "isForbidden", fieldMapper: func(user *object.User) []V {
if user.IsForbidden {
return []V{V("1")}
} else {
return []V{V("0")}
}
}},
"userpassword": {
ldapField: "userPassword",
userField: "userPassword",
notSearchable: true,
fieldMapper: func(user *object.User) []V {
return []V{V(getUserPasswordWithType(user))}
},
},
"uidnumber": {ldapField: "uidNumber", notSearchable: true, fieldMapper: func(user *object.User) []V {
return []V{V(fmt.Sprintf("%v", hash(user.Name)))}
}},
"gidnumber": {ldapField: "gidNumber", notSearchable: true, fieldMapper: func(user *object.User) []V {
if len(user.Groups) == 0 {
return []V{V("")}
}
group, err := object.GetGroup(user.Groups[0])
if err != nil {
log.Printf("gidnumber object.GetGroup error: %s", err)
return []V{V("")}
}
return []V{V(fmt.Sprintf("%v", hash(group.Name)))}
}},
"homedirectory": {ldapField: "homeDirectory", notSearchable: true, fieldMapper: func(user *object.User) []V {
return []V{V("/home/" + user.Name)}
}},
"loginshell": {ldapField: "loginShell", notSearchable: true, fieldMapper: func(user *object.User) []V {
if user.IsForbidden || user.IsDeleted {
return []V{V("/sbin/nologin")}
} else {
return []V{V("/bin/bash")}
}
}},
"shadowlastchange": {ldapField: "shadowLastChange", notSearchable: true, fieldMapper: func(user *object.User) []V {
// "this attribute specifies number of days between January 1, 1970, and the date that the password was last modified"
updatedTime, err := time.Parse(time.RFC3339, user.UpdatedTime)
if err != nil {
log.Printf("shadowlastchange time.Parse error: %s", err)
updatedTime = time.Now()
}
return []V{V(fmt.Sprint(updatedTime.Unix() / 86400))}
}},
"pwdchangedtime": {ldapField: "pwdChangedTime", notSearchable: true, fieldMapper: func(user *object.User) []V {
updatedTime, err := time.Parse(time.RFC3339, user.UpdatedTime)
if err != nil {
log.Printf("pwdchangedtime time.Parse error: %s", err)
updatedTime = time.Now()
}
return []V{V(updatedTime.UTC().Format("20060102030405Z"))}
}},
"shadowmin": {ldapField: "shadowMin", notSearchable: true, constantValue: []V{V("0")}},
"shadowmax": {ldapField: "shadowMax", notSearchable: true, constantValue: []V{V("99999")}},
"shadowwarning": {ldapField: "shadowWarning", notSearchable: true, constantValue: []V{V("7")}},
"shadowexpire": {ldapField: "shadowExpire", notSearchable: true, fieldMapper: func(user *object.User) []V {
if user.IsForbidden {
return []V{V("1")}
} else {
return []V{V("-1")}
}
}},
"shadowinactive": {ldapField: "shadowInactive", notSearchable: true, constantValue: []V{V("0")}},
"shadowflag": {ldapField: "shadowFlag", notSearchable: true, constantValue: []V{V("0")}},
"memberof": {ldapField: "memberOf", notSearchable: true, fieldMapper: func(user *object.User) []V {
var groupdn []V
for _, groupId := range user.Groups {
group, err := object.GetGroup(groupId)
if err != nil {
log.Printf("memberOf object.GetGroup error: %s", err)
continue
}
groupdn = append(groupdn, V(fmt.Sprintf("cn=%s,cn=groups,ou=%s", group.Name, group.Owner)))
}
return groupdn
}},
"objectclass": {ldapField: "objectClass", notSearchable: true, constantValue: []V{
V("top"),
V("posixAccount"),
V("shadowAccount"),
V("person"),
V("organizationalPerson"),
V("inetOrgPerson"),
V("apple-user"),
V("sambaSamAccount"),
V("sambaIdmapEntry"),
V("extensibleObject"),
}},
}
var ldapGroupAttributesMapping = GroupFieldRelationMap{
"cn": {ldapField: "cn", hideOnStarOp: true, fieldMapper: func(group *object.Group) []V {
return []V{V(group.Name)}
}},
"gidnumber": {ldapField: "gidNumber", hideOnStarOp: true, fieldMapper: func(group *object.Group) []V {
return []V{V(fmt.Sprintf("%v", hash(group.Name)))}
}},
"member": {ldapField: "member", fieldMapper: func(group *object.Group) []V {
users, err := object.GetGroupUsers(group.GetId())
if err != nil {
log.Printf("member object.GetGroupUsers error: %s", err)
return []V{V("")}
}
var members []V
for _, user := range users {
members = append(members, V(fmt.Sprintf("uid=%s,cn=users,ou=%s", user.Name, user.Owner)))
}
return members
}},
"memberuid": {ldapField: "memberUid", fieldMapper: func(group *object.Group) []V {
users, err := object.GetGroupUsers(group.GetId())
if err != nil {
log.Printf("member object.GetGroupUsers error: %s", err)
return []V{V("")}
}
var members []message.AttributeValue
for _, user := range users {
members = append(members, message.AttributeValue(user.Name))
}
return members
}},
"description": {ldapField: "description", hideOnStarOp: true, fieldMapper: func(group *object.Group) []V {
return []V{V(group.DisplayName)}
}},
"objectclass": {ldapField: "objectClass", hideOnStarOp: true, constantValue: []V{
V("top"),
V("posixGroup"),
}},
}
var (
AdditionalLdapUserAttributes []message.LDAPString
AdditionalLdapGroupAttributes []message.LDAPString
)
func init() {
for k, v := range ldapAttributesMapping {
for _, v := range ldapUserAttributesMapping {
if v.hideOnStarOp {
continue
}
AdditionalLdapAttributes = append(AdditionalLdapAttributes, message.LDAPString(k))
AdditionalLdapUserAttributes = append(AdditionalLdapUserAttributes, message.LDAPString(v.ldapField))
}
for _, v := range ldapGroupAttributesMapping {
if v.hideOnStarOp {
continue
}
AdditionalLdapGroupAttributes = append(AdditionalLdapGroupAttributes, message.LDAPString(v.ldapField))
}
}
@ -307,6 +502,52 @@ func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int)
}
}
func GetFilteredOrganizations(m *ldap.Message) ([]*object.Organization, int) {
if m.Client.IsGlobalAdmin {
organizations, err := object.GetOrganizations("")
if err != nil {
panic(err)
}
return organizations, ldap.LDAPResultSuccess
} else if m.Client.IsOrgAdmin {
requestUserId := util.GetId(m.Client.OrgName, m.Client.UserName)
user, err := object.GetUser(requestUserId)
if err != nil {
panic(err)
}
organization, err := object.GetOrganizationByUser(user)
if err != nil {
panic(err)
}
return []*object.Organization{organization}, ldap.LDAPResultSuccess
} else {
return nil, ldap.LDAPResultInsufficientAccessRights
}
}
func GetFilteredGroups(m *ldap.Message) ([]*object.Group, int) {
if m.Client.IsGlobalAdmin {
groups, err := object.GetGroups("")
if err != nil {
panic(err)
}
return groups, ldap.LDAPResultSuccess
} else if m.Client.IsOrgAdmin {
requestUserId := util.GetId(m.Client.OrgName, m.Client.UserName)
user, err := object.GetUser(requestUserId)
if err != nil {
panic(err)
}
groups, err := object.GetGroups(user.Owner)
if err != nil {
panic(err)
}
return groups, ldap.LDAPResultSuccess
} else {
return nil, ldap.LDAPResultInsufficientAccessRights
}
}
// get user password with hash type prefix
// TODO not handle salt yet
// @return {md5}5f4dcc3b5aa765d61d8327deb882cf99
@ -330,18 +571,49 @@ func getUserPasswordWithType(user *object.User) string {
return fmt.Sprintf("{%s}%s", prefix, user.Password)
}
func getAttribute(attributeName string, user *object.User) message.AttributeValue {
v, ok := ldapAttributesMapping[attributeName]
if !ok {
return ""
}
return v.GetAttributeValue(user)
}
func getUserFieldFromAttribute(attributeName string) (string, error) {
v, ok := ldapAttributesMapping[attributeName]
v, ok := ldapUserAttributesMapping.CaseInsensitiveGet(attributeName)
if !ok {
return "", fmt.Errorf("attribute %s not supported", attributeName)
}
return v.GetField()
}
func searchFilterForEquality(filter message.Filter, desc string, values ...string) string {
switch f := filter.(type) {
case message.FilterAnd:
for _, child := range f {
if val := searchFilterForEquality(child, desc, values...); val != "" {
return val
}
}
case message.FilterOr:
for _, child := range f {
if val := searchFilterForEquality(child, desc, values...); val != "" {
return val
}
}
case message.FilterNot:
return searchFilterForEquality(f.Filter, desc, values...)
case message.FilterSubstrings:
// Handle FilterSubstrings case if needed
case message.FilterEqualityMatch:
if strings.EqualFold(string(f.AttributeDesc()), desc) {
for _, value := range values {
if val := string(f.AssertionValue()); val == value {
return val
}
}
}
case message.FilterGreaterOrEqual:
// Handle FilterGreaterOrEqual case if needed
case message.FilterLessOrEqual:
// Handle FilterLessOrEqual case if needed
case message.FilterPresent:
// Handle FilterPresent case if needed
case message.FilterApproxMatch:
// Handle FilterApproxMatch case if needed
}
return ""
}

View File

@ -1,23 +0,0 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@ -1,24 +0,0 @@
apiVersion: v2
name: casdoor-helm-charts
description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.3.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.18.0"

View File

@ -1,22 +0,0 @@
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "casdoor.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "casdoor.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "casdoor.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "casdoor.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}

View File

@ -1,62 +0,0 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "casdoor.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "casdoor.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "casdoor.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "casdoor.labels" -}}
helm.sh/chart: {{ include "casdoor.chart" . }}
{{ include "casdoor.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "casdoor.selectorLabels" -}}
app.kubernetes.io/name: {{ include "casdoor.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "casdoor.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "casdoor.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@ -1,8 +0,0 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ printf "%s-config" (include "casdoor.fullname" .) }}
labels:
{{- include "casdoor.labels" . | nindent 4 }}
data:
app.conf: {{ tpl .Values.config . | toYaml | nindent 4 }}

View File

@ -1,86 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "casdoor.fullname" . }}
labels:
{{- include "casdoor.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "casdoor.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
checksum/config: {{ tpl .Values.config . | toYaml | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "casdoor.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "casdoor.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}/{{ .Values.image.name }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
# command: ["sleep", "100000000"]
env:
- name: RUNNING_IN_DOCKER
value: "true"
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
{{ if .Values.probe.liveness.enabled }}
livenessProbe:
httpGet:
path: /
port: http
{{ end }}
{{ if .Values.probe.readiness.enabled }}
readinessProbe:
httpGet:
path: /
port: http
{{ end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
- name: config-volume
mountPath: /conf
{{ if .Values.extraContainersEnabled }}
{{- .Values.extraContainers | nindent 8 }}
{{- end }}
volumes:
- name: config-volume
projected:
defaultMode: 420
sources:
- configMap:
items:
- key: app.conf
path: app.conf
name: {{ printf "%s-config" (include "casdoor.fullname" .) }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@ -1,28 +0,0 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "casdoor.fullname" . }}
labels:
{{- include "casdoor.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "casdoor.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View File

@ -1,61 +0,0 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "casdoor.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "casdoor.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View File

@ -1,15 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "casdoor.fullname" . }}
labels:
{{- include "casdoor.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "casdoor.selectorLabels" . | nindent 4 }}

View File

@ -1,12 +0,0 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "casdoor.serviceAccountName" . }}
labels:
{{- include "casdoor.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@ -1,15 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "casdoor.fullname" . }}-test-connection"
labels:
{{- include "casdoor.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "casdoor.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@ -1,117 +0,0 @@
# Default values for casdoor.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: casbin
name: casdoor
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
# ref: https://casdoor.org/docs/basic/server-installation#via-ini-file
config: |
appname = casdoor
httpport = {{ .Values.service.port }}
runmode = dev
SessionOn = true
copyrequestbody = true
driverName = sqlite
dataSourceName = "file:ent?mode=memory&cache=shared&_fk=1"
dbName = casdoor
redisEndpoint =
defaultStorageProvider =
isCloudIntranet = false
authState = "casdoor"
socks5Proxy = ""
verificationCodeTimeout = 10
initScore = 0
logPostOnly = true
origin =
enableGzip = true
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
probe:
readiness:
enabled: true
liveness:
enabled: true
service:
type: ClusterIP
port: 8000
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
# -- Optionally add extra sidecar containers.
extraContainersEnabled: false
extraContainers: ""
# extraContainers: |
# - name: ...
# image: ...

View File

@ -37,12 +37,13 @@ type SignupItem struct {
Prompted bool `json:"prompted"`
Label string `json:"label"`
Placeholder string `json:"placeholder"`
Regex string `json:"regex"`
Rule string `json:"rule"`
}
type SamlItem struct {
Name string `json:"name"`
NameFormat string `json:"nameformat"`
NameFormat string `json:"nameFormat"`
Value string `json:"value"`
}
@ -82,6 +83,7 @@ type Application struct {
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
ExpireInHours int `json:"expireInHours"`
RefreshExpireInHours int `json:"refreshExpireInHours"`
SignupUrl string `xorm:"varchar(200)" json:"signupUrl"`
@ -99,7 +101,7 @@ type Application struct {
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
FailedSigninLimit int `json:"failedSigninLimit"`
FailedSigninfrozenTime int `json:"failedSigninfrozenTime"`
FailedSigninFrozenTime int `json:"failedSigninFrozenTime"`
}
func GetApplicationCount(owner, field, value string) (int64, error) {
@ -201,7 +203,7 @@ func extendApplicationWithOrg(application *Application) (err error) {
func extendApplicationWithSigninMethods(application *Application) (err error) {
if len(application.SigninMethods) == 0 {
if application.EnablePassword {
signinMethod := &SigninMethod{Name: "Password", DisplayName: "Password", Rule: "None"}
signinMethod := &SigninMethod{Name: "Password", DisplayName: "Password", Rule: "All"}
application.SigninMethods = append(application.SigninMethods, signinMethod)
}
if application.EnableCodeSignin {
@ -214,6 +216,11 @@ func extendApplicationWithSigninMethods(application *Application) (err error) {
}
}
if len(application.SigninMethods) == 0 {
signinMethod := &SigninMethod{Name: "Password", DisplayName: "Password", Rule: "All"}
application.SigninMethods = append(application.SigninMethods, signinMethod)
}
return
}
@ -341,6 +348,17 @@ func GetMaskedApplication(application *Application, userId string) *Application
return nil
}
if application.TokenFields == nil {
application.TokenFields = []string{}
}
if application.FailedSigninLimit == 0 {
application.FailedSigninLimit = DefaultFailedSigninLimit
}
if application.FailedSigninFrozenTime == 0 {
application.FailedSigninFrozenTime = DefaultFailedSigninFrozenTime
}
if userId != "" {
if isUserIdGlobalAdmin(userId) {
return application
@ -539,6 +557,19 @@ func (application *Application) IsPasswordEnabled() bool {
}
}
func (application *Application) IsPasswordWithLdapEnabled() bool {
if len(application.SigninMethods) == 0 {
return application.EnablePassword
} else {
for _, signinMethod := range application.SigninMethods {
if signinMethod.Name == "Password" && signinMethod.Rule == "All" {
return true
}
}
return false
}
}
func (application *Application) IsCodeSigninViaEmailEnabled() bool {
if len(application.SigninMethods) == 0 {
return application.EnableCodeSignin
@ -565,6 +596,17 @@ func (application *Application) IsCodeSigninViaSmsEnabled() bool {
}
}
func (application *Application) IsLdapEnabled() bool {
if len(application.SigninMethods) > 0 {
for _, signinMethod := range application.SigninMethods {
if signinMethod.Name == "LDAP" {
return true
}
}
}
return false
}
func IsOriginAllowed(origin string) (bool, error) {
applications, err := GetApplications("")
if err != nil {
@ -659,7 +701,7 @@ func applicationChangeTrigger(oldName string, newName string) error {
}
}
permissions[i].Resources = permissionResoureces
_, err = session.Where("name=?", permissions[i].Name).Update(permissions[i])
_, err = session.Where("owner=?", permissions[i].Owner).Where("name=?", permissions[i].Name).Update(permissions[i])
if err != nil {
return err
}

View File

@ -28,9 +28,8 @@ import (
)
const (
DefaultFailedSigninLimit = 5
// DefaultFailedSigninfrozenTime The unit of frozen time is minutes
DefaultFailedSigninfrozenTime = 15
DefaultFailedSigninLimit = 5
DefaultFailedSigninFrozenTime = 15
)
func CheckUserSignup(application *Application, organization *Organization, form *form.AuthForm, lang string) string {
@ -144,7 +143,7 @@ func CheckUserSignup(application *Application, organization *Organization, form
}
func checkSigninErrorTimes(user *User, lang string) error {
failedSigninLimit, failedSigninfrozenTime, err := GetFailedSigninConfigByUser(user)
failedSigninLimit, failedSigninFrozenTime, err := GetFailedSigninConfigByUser(user)
if err != nil {
return err
}
@ -152,7 +151,7 @@ func checkSigninErrorTimes(user *User, lang string) error {
if user.SigninWrongTimes >= failedSigninLimit {
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
passedTime := time.Now().UTC().Sub(lastSignWrongTime)
minutes := failedSigninfrozenTime - int(passedTime.Minutes())
minutes := failedSigninFrozenTime - int(passedTime.Minutes())
// deny the login if the error times is greater than the limit and the last login time is less than the duration
if minutes > 0 {
@ -278,8 +277,12 @@ func checkLdapUserPassword(user *User, password string, lang string) error {
func CheckUserPassword(organization string, username string, password string, lang string, options ...bool) (*User, error) {
enableCaptcha := false
isSigninViaLdap := false
isPasswordWithLdapEnabled := false
if len(options) > 0 {
enableCaptcha = options[0]
isSigninViaLdap = options[1]
isPasswordWithLdapEnabled = options[2]
}
user, err := GetUserByFields(organization, username)
if err != nil {
@ -294,14 +297,33 @@ func CheckUserPassword(organization string, username string, password string, la
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user is forbidden to sign in, please contact the administrator"))
}
if isSigninViaLdap {
if user.Ldap == "" {
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user: %s doesn't exist in LDAP server"), username)
}
}
if user.Ldap != "" {
if !isSigninViaLdap && !isPasswordWithLdapEnabled {
return nil, fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect"))
}
// check the login error times
if !enableCaptcha {
err = checkSigninErrorTimes(user, lang)
if err != nil {
return nil, err
}
}
// only for LDAP users
err = checkLdapUserPassword(user, password, lang)
if err != nil {
if err.Error() == "user not exist" {
return nil, fmt.Errorf(i18n.Translate(lang, "check:The user: %s doesn't exist in LDAP server"), username)
}
return nil, err
return nil, recordSigninErrorInfo(user, lang, enableCaptcha)
}
} else {
err = CheckPassword(user, password, lang, enableCaptcha)
@ -486,12 +508,11 @@ func CheckToEnableCaptcha(application *Application, organization, username strin
return false, err
}
var failedSigninLimit int
if application.FailedSigninLimit == 0 {
failedSigninLimit = 5
} else {
failedSigninLimit = application.FailedSigninLimit
failedSigninLimit := application.FailedSigninLimit
if failedSigninLimit == 0 {
failedSigninLimit = DefaultFailedSigninLimit
}
return user != nil && user.SigninWrongTimes >= failedSigninLimit, nil
}
return providerItem.Rule == "Always", nil

View File

@ -52,18 +52,18 @@ func GetFailedSigninConfigByUser(user *User) (int, int, error) {
if err != nil {
return 0, 0, err
}
failedSigninLimit := application.FailedSigninLimit
failedSigninfrozenTime := application.FailedSigninfrozenTime
// 0 as an initialization value, corresponding to the default configuration parameters
failedSigninLimit := application.FailedSigninLimit
if failedSigninLimit == 0 {
failedSigninLimit = DefaultFailedSigninLimit
}
if failedSigninfrozenTime == 0 {
failedSigninfrozenTime = DefaultFailedSigninfrozenTime
failedSigninFrozenTime := application.FailedSigninFrozenTime
if failedSigninFrozenTime == 0 {
failedSigninFrozenTime = DefaultFailedSigninFrozenTime
}
return failedSigninLimit, failedSigninfrozenTime, nil
return failedSigninLimit, failedSigninFrozenTime, nil
}
func recordSigninErrorInfo(user *User, lang string, options ...bool) error {
@ -72,7 +72,7 @@ func recordSigninErrorInfo(user *User, lang string, options ...bool) error {
enableCaptcha = options[0]
}
failedSigninLimit, failedSigninfrozenTime, errSignin := GetFailedSigninConfigByUser(user)
failedSigninLimit, failedSigninFrozenTime, errSignin := GetFailedSigninConfigByUser(user)
if errSignin != nil {
return errSignin
}
@ -101,5 +101,5 @@ func recordSigninErrorInfo(user *User, lang string, options ...bool) error {
}
// don't show the chance error message if the user has no chance left
return fmt.Errorf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), failedSigninfrozenTime)
return fmt.Errorf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), failedSigninFrozenTime)
}

View File

@ -242,7 +242,8 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
}
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
session := ormer.Engine.Table(tableNamePrefix+"user").
prefixedUserTable := tableNamePrefix + "user"
session := ormer.Engine.Table(prefixedUserTable).
Where("owner = ?", owner).In("name", names)
if offset != -1 && limit != -1 {
@ -250,16 +251,19 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
}
if field != "" && value != "" {
session = session.And(fmt.Sprintf("user.%s like ?", util.CamelToSnakeCase(field)), "%"+value+"%")
session = session.And(fmt.Sprintf("%s.%s like ?", prefixedUserTable, util.CamelToSnakeCase(field)), "%"+value+"%")
}
if sortField == "" || sortOrder == "" {
sortField = "created_time"
}
orderQuery := fmt.Sprintf("%s.%s", prefixedUserTable, util.SnakeString(sortField))
if sortOrder == "ascend" {
session = session.Asc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
session = session.Asc(orderQuery)
} else {
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
session = session.Desc(orderQuery)
}
err = session.Find(&users)

View File

@ -197,6 +197,7 @@ func initBuiltInApplication() {
},
Tags: []string{},
RedirectUris: []string{},
TokenFields: []string{},
ExpireInHours: 168,
FormOffset: 2,
}

View File

@ -145,11 +145,14 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
if application.GrantTypes == nil {
application.GrantTypes = []string{}
}
if application.Tags == nil {
application.Tags = []string{}
}
if application.RedirectUris == nil {
application.RedirectUris = []string{}
}
if application.Tags == nil {
application.Tags = []string{}
if application.TokenFields == nil {
application.TokenFields = []string{}
}
}
for _, permission := range data.Permissions {

View File

@ -36,16 +36,14 @@ import (
)
var (
ormer *Ormer = nil
isCreateDatabaseDefined = false
createDatabase = true
ormer *Ormer = nil
createDatabase = true
configPath = "conf/app.conf"
)
func InitFlag() {
if !isCreateDatabaseDefined {
isCreateDatabaseDefined = true
createDatabase = getCreateDatabaseFlag()
}
createDatabase = getCreateDatabaseFlag()
configPath = getConfigFlag()
}
func getCreateDatabaseFlag() bool {
@ -54,6 +52,12 @@ func getCreateDatabaseFlag() bool {
return *res
}
func getConfigFlag() string {
res := flag.String("config", "conf/app.conf", "set it to \"/your/path/app.conf\" if your config file is not in: \"/conf/app.conf\"")
flag.Parse()
return *res
}
func InitConfig() {
err := beego.LoadAppConfig("ini", "../conf/app.conf")
if err != nil {
@ -68,7 +72,7 @@ func InitConfig() {
func InitAdapter() {
if conf.GetConfigString("driverName") == "" {
if !util.FileExist("conf/app.conf") {
if !util.FileExist(configPath) {
dir, err := os.Getwd()
if err != nil {
panic(err)

View File

@ -308,7 +308,7 @@ func BatchEnforce(permission *Permission, requests [][]string, permissionIds ...
return enforcer.BatchEnforce(interfaceRequests)
}
func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) ([]string, error) {
func getEnforcers(userId string) ([]*casbin.Enforcer, error) {
permissions, _, err := getPermissionsAndRolesByUser(userId)
if err != nil {
return nil, err
@ -320,7 +320,8 @@ func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) ([
}
for _, role := range allRoles {
permissionsByRole, err := GetPermissionsByRole(role)
var permissionsByRole []*Permission
permissionsByRole, err = GetPermissionsByRole(role)
if err != nil {
return nil, err
}
@ -328,29 +329,45 @@ func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) ([
permissions = append(permissions, permissionsByRole...)
}
var values []string
var enforcers []*casbin.Enforcer
for _, permission := range permissions {
enforcer, err := getPermissionEnforcer(permission)
var enforcer *casbin.Enforcer
enforcer, err = getPermissionEnforcer(permission)
if err != nil {
return nil, err
}
values = append(values, fn(enforcer)...)
enforcers = append(enforcers, enforcer)
}
return values, nil
return enforcers, nil
}
func GetAllObjects(userId string) ([]string, error) {
return getAllValues(userId, func(enforcer *casbin.Enforcer) []string {
return enforcer.GetAllObjects()
})
enforcers, err := getEnforcers(userId)
if err != nil {
return nil, err
}
res := []string{}
for _, enforcer := range enforcers {
items := enforcer.GetAllObjects()
res = append(res, items...)
}
return res, nil
}
func GetAllActions(userId string) ([]string, error) {
return getAllValues(userId, func(enforcer *casbin.Enforcer) []string {
return enforcer.GetAllActions()
})
enforcers, err := getEnforcers(userId)
if err != nil {
return nil, err
}
res := []string{}
for _, enforcer := range enforcers {
items := enforcer.GetAllObjects()
res = append(res, items...)
}
return res, nil
}
func GetAllRoles(userId string) ([]string, error) {

View File

@ -266,10 +266,9 @@ func (role *Role) GetId() string {
}
func getRolesByUserInternal(userId string) ([]*Role, error) {
roles := []*Role{}
user, err := GetUser(userId)
if err != nil {
return roles, err
return nil, err
}
if user == nil {
return nil, fmt.Errorf("The user: %s doesn't exist", userId)
@ -280,9 +279,10 @@ func getRolesByUserInternal(userId string) ([]*Role, error) {
query = query.Or("r.groups like ?", fmt.Sprintf("%%%s%%", group))
}
roles := []*Role{}
err = query.Find(&roles)
if err != nil {
return roles, err
return nil, err
}
res := []*Role{}
@ -291,14 +291,13 @@ func getRolesByUserInternal(userId string) ([]*Role, error) {
res = append(res, role)
}
}
return res, nil
}
func getRolesByUser(userId string) ([]*Role, error) {
roles, err := getRolesByUserInternal(userId)
if err != nil {
return roles, err
return nil, err
}
allRolesIds := []string{}
@ -379,15 +378,11 @@ func GetMaskedRoles(roles []*Role) []*Role {
// GetAncestorRoles returns a list of roles that contain the given roleIds
func GetAncestorRoles(roleIds ...string) ([]*Role, error) {
var (
result = []*Role{}
roleMap = make(map[string]*Role)
visited = make(map[string]bool)
)
if len(roleIds) == 0 {
return result, nil
return []*Role{}, nil
}
visited := map[string]bool{}
for _, roleId := range roleIds {
visited[roleId] = true
}
@ -399,25 +394,26 @@ func GetAncestorRoles(roleIds ...string) ([]*Role, error) {
return nil, err
}
roleMap := map[string]*Role{}
for _, r := range allRoles {
roleMap[r.GetId()] = r
}
// Second, find all the roles that contain father roles
// find all the roles that contain father roles
res := []*Role{}
for _, r := range allRoles {
isContain, ok := visited[r.GetId()]
if isContain {
result = append(result, r)
res = append(res, r)
} else if !ok {
rId := r.GetId()
visited[rId] = containsRole(r, roleMap, visited, roleIds...)
if visited[rId] {
result = append(result, r)
res = append(res, r)
}
}
}
return result, nil
return res, nil
}
// containsRole is a helper function to check if a roles is related to any role in the given list roles

View File

@ -54,6 +54,15 @@ func (syncer *Syncer) syncUsers() error {
var affiliationMap map[int]string
if syncer.AffiliationTable != "" {
_, affiliationMap, err = syncer.getAffiliationMap()
if err != nil {
line := fmt.Sprintf("[%s] %s\n", util.GetCurrentTime(), err.Error())
_, err2 := updateSyncerErrorText(syncer, line)
if err2 != nil {
panic(err2)
}
return err
}
}
key := syncer.getKey()

View File

@ -16,6 +16,7 @@ package object
import (
"fmt"
"reflect"
"time"
"github.com/casdoor/casdoor/util"
@ -270,6 +271,34 @@ func getClaimsWithoutThirdIdp(claims Claims) ClaimsWithoutThirdIdp {
return res
}
func getClaimsCustom(claims Claims, tokenField []string) jwt.MapClaims {
res := make(jwt.MapClaims)
userValue := reflect.ValueOf(claims.User).Elem()
res["iss"] = claims.RegisteredClaims.Issuer
res["sub"] = claims.RegisteredClaims.Subject
res["aud"] = claims.RegisteredClaims.Audience
res["exp"] = claims.RegisteredClaims.ExpiresAt
res["nbf"] = claims.RegisteredClaims.NotBefore
res["iat"] = claims.RegisteredClaims.IssuedAt
res["jti"] = claims.RegisteredClaims.ID
res["tokenType"] = claims.TokenType
res["nonce"] = claims.Nonce
res["tag"] = claims.Tag
res["scope"] = claims.Scope
for _, field := range tokenField {
userField := userValue.FieldByName(field)
if userField.IsValid() {
newfield := util.SnakeToCamel(util.CamelToSnakeCase(field))
res[newfield] = userField.Interface()
}
}
return res
}
func refineUser(user *User) *User {
user.Password = ""
@ -329,20 +358,30 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
var refreshToken *jwt.Token
// the JWT token length in "JWT-Empty" mode will be very short, as User object only has two properties: owner and name
if application.TokenFormat == "JWT-Empty" {
claimsShort := getShortClaims(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
claimsShort.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsShort.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
} else {
if application.TokenFormat == "JWT" {
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
claimsWithoutThirdIdp.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsWithoutThirdIdp.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
} else if application.TokenFormat == "JWT-Empty" {
claimsShort := getShortClaims(claims)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
claimsShort.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsShort.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
} else if application.TokenFormat == "JWT-Custom" {
claimsCustom := getClaimsCustom(claims, application.TokenFields)
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsCustom)
refreshClaims := getClaimsCustom(claims, application.TokenFields)
refreshClaims["exp"] = jwt.NewNumericDate(refreshExpireTime)
refreshClaims["TokenType"] = "refresh-token"
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, refreshClaims)
} else {
return "", "", "", fmt.Errorf("unknown application TokenFormat: %s", application.TokenFormat)
}
cert, err := getCertByApplication(application)

View File

@ -867,7 +867,8 @@ func GetUserInfo(user *User, scope string, aud string, host string) *Userinfo {
}
if strings.Contains(scope, "email") {
resp.Email = user.Email
resp.EmailVerified = user.EmailVerified
// resp.EmailVerified = user.EmailVerified
resp.EmailVerified = true
}
if strings.Contains(scope, "address") {
resp.Address = user.Location

View File

@ -5418,7 +5418,7 @@
"type": "integer",
"format": "int64"
},
"failedSigninfrozenTime": {
"failedSigninFrozenTime": {
"type": "integer",
"format": "int64"
},
@ -6761,7 +6761,7 @@
"name": {
"type": "string"
},
"nameformat": {
"nameFormat": {
"type": "string"
},
"value": {

View File

@ -3549,7 +3549,7 @@ definitions:
failedSigninLimit:
type: integer
format: int64
failedSigninfrozenTime:
failedSigninFrozenTime:
type: integer
format: int64
forgetUrl:
@ -4451,7 +4451,7 @@ definitions:
properties:
name:
type: string
nameformat:
nameFormat:
type: string
value:
type: string

View File

@ -392,7 +392,7 @@ class App extends Component {
</div>
</Tooltip>
<OpenTour />
{Setting.isAdminUser(this.state.account) && !Setting.isMobile() &&
{Setting.isAdminUser(this.state.account) && !Setting.isMobile() && (this.state.uri.indexOf("/trees") === -1) &&
<OrganizationSelect
initValue={Setting.getOrganization()}
withAll={true}

View File

@ -27,7 +27,7 @@ import LoginPage from "./auth/LoginPage";
import i18next from "i18next";
import UrlTable from "./table/UrlTable";
import ProviderTable from "./table/ProviderTable";
import SigninTable from "./table/SigninTable";
import SigninMethodTable from "./table/SigninMethodTable";
import SignupTable from "./table/SignupTable";
import SamlAttributeTable from "./table/SamlAttributeTable";
import PromptPage from "./auth/PromptPage";
@ -386,10 +386,22 @@ class ApplicationEditPage extends React.Component {
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenFormat} onChange={(value => {this.updateApplicationField("tokenFormat", value);})}
options={["JWT", "JWT-Empty"].map((item) => Setting.getOption(item, item))}
options={["JWT", "JWT-Empty", "JWT-Custom"].map((item) => Setting.getOption(item, item))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Token fields"), i18next.t("application:Token fields - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} disabled={this.state.application.tokenFormat !== "JWT-Custom"} mode="tags" showSearch style={{width: "100%"}} value={this.state.application.tokenFields} onChange={(value => {this.updateApplicationField("tokenFields", value);})}>
{
Setting.getUserCommonFields().map((item, index) => <Option key={index} value={item}>{item}</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Token expire"), i18next.t("application:Token expire - Tooltip"))} :
@ -425,8 +437,8 @@ class ApplicationEditPage extends React.Component {
{Setting.getLabel(i18next.t("application:Failed signin frozen time"), i18next.t("application:Failed signin frozen time - Tooltip"))} :
</Col>
<Col span={22} >
<InputNumber style={{width: "150px"}} value={this.state.application.failedSigninfrozenTime} min={1} step={1} precision={0} addonAfter="Minutes" onChange={value => {
this.updateApplicationField("failedSigninfrozenTime", value);
<InputNumber style={{width: "150px"}} value={this.state.application.failedSigninFrozenTime} min={1} step={1} precision={0} addonAfter="Minutes" onChange={value => {
this.updateApplicationField("failedSigninFrozenTime", value);
}} />
</Col>
</Row>
@ -475,7 +487,7 @@ class ApplicationEditPage extends React.Component {
{Setting.getLabel(i18next.t("application:Signin methods"), i18next.t("application:Signin methods - Tooltip"))} :
</Col>
<Col span={22} >
<SigninTable
<SigninMethodTable
title={i18next.t("application:Signin methods")}
table={this.state.application.signinMethods}
onUpdateTable={(value) => {
@ -785,7 +797,7 @@ class ApplicationEditPage extends React.Component {
</Col>
<Col span={22} >
<Row style={{marginTop: "20px"}} >
<Radio.Group onChange={e => {this.updateApplicationField("formOffset", e.target.value);}} value={this.state.application.formOffset}>
<Radio.Group buttonStyle="solid" onChange={e => {this.updateApplicationField("formOffset", e.target.value);}} value={this.state.application.formOffset}>
<Radio.Button value={1}>{i18next.t("application:Left")}</Radio.Button>
<Radio.Button value={2}>{i18next.t("application:Center")}</Radio.Button>
<Radio.Button value={3}>{i18next.t("application:Right")}</Radio.Button>
@ -825,7 +837,7 @@ class ApplicationEditPage extends React.Component {
</Col>
<Col span={22} style={{marginTop: "5px"}}>
<Row>
<Radio.Group value={this.state.application.themeData?.isEnabled ?? false} onChange={e => {
<Radio.Group buttonStyle="solid" value={this.state.application.themeData?.isEnabled ?? false} onChange={e => {
const {_, ...theme} = this.state.application.themeData ?? {...Conf.ThemeDefault, isEnabled: false};
this.updateApplicationField("themeData", {...theme, isEnabled: e.target.value});
}} >
@ -1034,7 +1046,7 @@ class ApplicationEditPage extends React.Component {
submitApplicationEdit(exitAfterSave) {
const application = Setting.deepCopy(this.state.application);
application.providers = application.providers?.filter(provider => this.state.providers.map(provider => provider.name).includes(provider.name));
application.signinMethods = application.signinMethods?.filter(signinMethod => ["Password", "Verification code", "WebAuthn"].includes(signinMethod.name));
application.signinMethods = application.signinMethods?.filter(signinMethod => ["Password", "Verification code", "WebAuthn", "LDAP"].includes(signinMethod.name));
ApplicationBackend.updateApplication("admin", this.state.applicationName, application)
.then((res) => {

View File

@ -47,7 +47,7 @@ class ApplicationListPage extends BaseListPage {
{name: "provider_captcha_default", canSignUp: false, canSignIn: false, canUnlink: false, prompted: false, signupGroup: "", rule: ""},
],
SigninMethods: [
{name: "Password", displayName: "Password", rule: "None"},
{name: "Password", displayName: "Password", rule: "All"},
{name: "Verification code", displayName: "Verification code", rule: "All"},
{name: "WebAuthn", displayName: "WebAuthn", rule: "None"},
],
@ -64,6 +64,7 @@ class ApplicationListPage extends BaseListPage {
cert: "cert-built-in",
redirectUris: ["http://localhost:9000/callback"],
tokenFormat: "JWT",
tokenFields: [],
expireInHours: 24 * 7,
refreshExpireInHours: 24 * 7,
formOffset: 2,

View File

@ -25,6 +25,7 @@ class BaseListPage extends React.Component {
super(props);
this.state = {
classes: props,
organizationName: this.props.match?.params.organizationName || Setting.getRequestOrganization(this.props.account),
data: [],
pagination: {
current: 1,
@ -39,6 +40,10 @@ class BaseListPage extends React.Component {
}
handleOrganizationChange = () => {
this.setState({
organizationName: this.props.match?.params.organizationName || Setting.getRequestOrganization(this.props.account),
});
const {pagination} = this.state;
this.fetch({pagination});
};

View File

@ -393,7 +393,7 @@ class OrganizationEditPage extends React.Component {
</Col>
<Col span={22} style={{marginTop: "5px"}}>
<Row>
<Radio.Group value={this.state.organization.themeData?.isEnabled ?? false} onChange={e => {
<Radio.Group buttonStyle="solid" value={this.state.organization.themeData?.isEnabled ?? false} onChange={e => {
const {_, ...theme} = this.state.organization.themeData ?? {...Conf.ThemeDefault, isEnabled: false};
this.updateOrganizationField("themeData", {...theme, isEnabled: e.target.value});
}} >

View File

@ -1131,28 +1131,28 @@ export function renderLogo(application) {
}
}
export function isPasswordEnabled(application) {
if (application) {
return application.signinMethods.filter(item => item.name === "Password").length > 0;
function isSigninMethodEnabled(application, signinMethod) {
if (application && application.signinMethods) {
return application.signinMethods.filter(item => item.name === signinMethod).length > 0;
} else {
return false;
}
}
export function isPasswordEnabled(application) {
return isSigninMethodEnabled(application, "Password");
}
export function isCodeSigninEnabled(application) {
if (application) {
return application.signinMethods.filter(item => item.name === "Verification code").length > 0;
} else {
return false;
}
return isSigninMethodEnabled(application, "Verification code");
}
export function isWebAuthnEnabled(application) {
if (application) {
return application.signinMethods.filter(item => item.name === "WebAuthn").length > 0;
} else {
return false;
}
return isSigninMethodEnabled(application, "WebAuthn");
}
export function isLdapEnabled(application) {
return isSigninMethodEnabled(application, "LDAP");
}
export function getLoginLink(application) {
@ -1442,6 +1442,13 @@ export function getFriendlyUserName(account) {
}
}
export function getUserCommonFields() {
return ["Owner", "Name", "CreatedTime", "UpdatedTime", "Id", "Type", "Password", "PasswordSalt", "DisplayName", "FirstName", "LastName", "Avatar", "PermanentAvatar",
"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",
"PreferredMfaType", "TotpSecret", "SignupApplication"];
}
export function getDefaultHtmlEmailContent() {
return `<!DOCTYPE html>
<html lang="en">

View File

@ -30,7 +30,6 @@ class UserListPage extends BaseListPage {
super(props);
this.state = {
...this.state,
organizationName: this.props.organizationName ?? this.props.match?.params.organizationName ?? this.props.account.owner,
organization: null,
};
}
@ -62,7 +61,7 @@ class UserListPage extends BaseListPage {
newUser() {
const randomName = Setting.getRandomName();
const owner = Setting.isDefaultOrganizationSelected(this.props.account) ? this.state.organizationName : Setting.getRequestOrganization(this.props.account);
const owner = (Setting.isDefaultOrganizationSelected(this.props.account) || this.props.groupName) ? this.state.organizationName : Setting.getRequestOrganization(this.props.account);
return {
owner: owner,
name: `user_${randomName}`,
@ -85,7 +84,7 @@ class UserListPage extends BaseListPage {
score: this.state.organization.initScore,
isDeleted: false,
properties: {},
signupApplication: "app-built-in",
signupApplication: this.state.organization.defaultApplication,
};
}

View File

@ -201,7 +201,7 @@ class LoginPage extends React.Component {
}
getDefaultLoginMethod(application) {
if (application?.signinMethods.length > 0) {
if (application?.signinMethods?.length > 0) {
switch (application?.signinMethods[0].name) {
case "Password": return "password";
case "Verification code": {
@ -213,6 +213,7 @@ class LoginPage extends React.Component {
break;
}
case "WebAuthn": return "webAuthn";
case "LDAP": return "ldap";
}
}
@ -224,6 +225,7 @@ class LoginPage extends React.Component {
case "verificationCode": return i18next.t("login:Email or phone");
case "verificationCodeEmail": return i18next.t("login:Email");
case "verificationCodePhone": return i18next.t("login:Phone");
case "ldap": return i18next.t("login:LDAP username, Email or phone");
default: return i18next.t("login:username, Email or phone");
}
}
@ -253,6 +255,15 @@ class LoginPage extends React.Component {
values["organization"] = this.getApplicationObj().organization;
}
if (this.state.loginMethod === "password") {
values["signinMethod"] = "Password";
} else if (this.state.loginMethod?.includes("verificationCode")) {
values["signinMethod"] = "Verification code";
} else if (this.state.loginMethod === "webAuthn") {
values["signinMethod"] = "WebAuthn";
} else if (this.state.loginMethod === "ldap") {
values["signinMethod"] = "LDAP";
}
const oAuthParams = Util.getOAuthGetParameters();
values["type"] = oAuthParams?.responseType ?? this.state.type;
@ -329,7 +340,7 @@ class LoginPage extends React.Component {
this.signInWithWebAuthn(username, values);
return;
}
if (this.state.loginMethod === "password") {
if (this.state.loginMethod === "password" || this.state.loginMethod === "ldap") {
if (this.state.enableCaptchaModal === CaptchaRule.Always) {
this.setState({
openCaptchaModal: true,
@ -468,6 +479,10 @@ class LoginPage extends React.Component {
}
renderOtherFormProvider(application) {
if (Setting.inIframe()) {
return null;
}
for (const providerConf of application.providers) {
if (providerConf.provider?.type === "Google" && providerConf.rule === "OneTap" && this.props.preview !== "auto") {
return (
@ -475,6 +490,8 @@ class LoginPage extends React.Component {
);
}
}
return null;
}
renderForm(application) {
@ -501,7 +518,7 @@ class LoginPage extends React.Component {
);
}
const showForm = Setting.isPasswordEnabled(application) || Setting.isCodeSigninEnabled(application) || Setting.isWebAuthnEnabled(application);
const showForm = Setting.isPasswordEnabled(application) || Setting.isCodeSigninEnabled(application) || Setting.isWebAuthnEnabled(application) || Setting.isLdapEnabled(application);
if (showForm) {
let loginWidth = 320;
if (Setting.getLanguage() === "fr") {
@ -564,12 +581,17 @@ class LoginPage extends React.Component {
switch (this.state.loginMethod) {
case "verificationCodeEmail": return i18next.t("login:Please input your Email!");
case "verificationCodePhone": return i18next.t("login:Please input your Phone!");
case "ldap": return i18next.t("login:Please input your LDAP username!");
default: return i18next.t("login:Please input your Email or Phone!");
}
},
},
{
validator: (_, value) => {
if (value === "") {
return Promise.resolve();
}
if (this.state.loginMethod === "verificationCode") {
if (!Setting.isValidEmail(value) && !Setting.isValidPhone(value)) {
this.setState({validEmailOrPhone: false});
@ -864,7 +886,7 @@ class LoginPage extends React.Component {
renderPasswordOrCodeInput() {
const application = this.getApplicationObj();
if (this.state.loginMethod === "password") {
if (this.state.loginMethod === "password" || this.state.loginMethod === "ldap") {
return (
<Col span={24}>
<Form.Item
@ -875,7 +897,7 @@ class LoginPage extends React.Component {
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder={i18next.t("general:Password")}
disabled={!Setting.isPasswordEnabled(application)}
disabled={this.state.loginMethod === "password" ? !Setting.isPasswordEnabled(application) : !Setting.isLdapEnabled(application)}
/>
</Form.Item>
</Col>
@ -910,14 +932,16 @@ class LoginPage extends React.Component {
};
const itemsMap = new Map([
[generateItemKey("Password", "None"), {label: i18next.t("general:Password"), key: "password"}],
[generateItemKey("Password", "All"), {label: i18next.t("general:Password"), key: "password"}],
[generateItemKey("Password", "Non-LDAP"), {label: i18next.t("general:Password"), key: "password"}],
[generateItemKey("Verification code", "All"), {label: i18next.t("login:Verification code"), key: "verificationCode"}],
[generateItemKey("Verification code", "Email only"), {label: i18next.t("login:Verification code"), key: "verificationCodeEmail"}],
[generateItemKey("Verification code", "Phone only"), {label: i18next.t("login:Verification code"), key: "verificationCodePhone"}],
[generateItemKey("WebAuthn", "None"), {label: i18next.t("login:WebAuthn"), key: "webAuthn"}],
[generateItemKey("LDAP", "None"), {label: i18next.t("login:LDAP"), key: "ldap"}],
]);
application?.signinMethods.forEach((signinMethod) => {
application?.signinMethods?.forEach((signinMethod) => {
const item = itemsMap.get(generateItemKey(signinMethod.name, signinMethod.rule));
if (item) {
const label = signinMethod.name === signinMethod.displayName ? item.label : signinMethod.displayName;
@ -1053,7 +1077,7 @@ class LoginPage extends React.Component {
}
const visibleOAuthProviderItems = (application.providers === null) ? [] : application.providers.filter(providerItem => this.isProviderVisible(providerItem));
if (this.props.preview !== "auto" && !Setting.isPasswordEnabled(application) && !Setting.isCodeSigninEnabled(application) && !Setting.isWebAuthnEnabled(application) && visibleOAuthProviderItems.length === 1) {
if (this.props.preview !== "auto" && !Setting.isPasswordEnabled(application) && !Setting.isCodeSigninEnabled(application) && !Setting.isWebAuthnEnabled(application) && !Setting.isLdapEnabled(application) && visibleOAuthProviderItems.length === 1) {
Setting.goToLink(Provider.getAuthUrl(application, visibleOAuthProviderItems[0].provider, "signup"));
return (
<div style={{display: "flex", justifyContent: "center", alignItems: "center", width: "100%"}}>

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "Die Anwendung erlaubt es nicht, ein neues Konto zu registrieren",
"Token expire": "Token läuft ab",
"Token expire - Tooltip": "Ablaufzeit des Access-Tokens",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token-Format",
"Token format - Tooltip": "Das Format des Access-Tokens",
"You are unexpected to see this prompt page": "Sie sind unerwartet auf diese Aufforderungsseite gelangt"
@ -265,6 +267,8 @@
"Models": "Modelle",
"Name": "Name",
"Name - Tooltip": "Eindeutige, auf Strings basierende ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth-Provider",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Passwort vergessen?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Laden",
"Logging out...": "Ausloggen...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Bitte geben Sie Ihre E-Mail oder Telefonnummer ein!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Bitte geben Sie Ihren Code ein!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Bitte geben Sie Ihren richtigen Namen ein!",
"Please select your country code!": "Bitte wählen Sie Ihren Ländercode aus!",
"Please select your country/region!": "Bitte wählen Sie Ihr Land/Ihre Region aus!",
"Regex": "Regex",
"Terms of Use": "Nutzungsbedingungen",
"Terms of Use - Tooltip": "Nutzungsbedingungen, die Benutzer während der Registrierung lesen und akzeptieren müssen",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Verwaltete Konten",
"Modify password...": "Passwort ändern...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "Neue E-Mail",
"New Password": "Neues Passwort",
"New User": "Neuer Benutzer",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Lade ein Foto hoch",
"Value": "Value",
"Values": "Werte",
"Verification code sent": "Bestätigungscode gesendet",
"WebAuthn credentials": "WebAuthn-Anmeldeinformationen",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "The user fields included in the token",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "La aplicación no permite registrarse una cuenta nueva",
"Token expire": "Token expirado",
"Token expire - Tooltip": "Tiempo de expiración del token de acceso",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Formato del token",
"Token format - Tooltip": "El formato del token de acceso",
"You are unexpected to see this prompt page": "Es inesperado ver esta página de inicio"
@ -265,6 +267,8 @@
"Models": "Modelos",
"Name": "Nombre",
"Name - Tooltip": "ID único basado en cadenas",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "Proveedores de OAuth",
"OK": "Vale",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "¿Olvidaste tu contraseña?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Cargando",
"Logging out...": "Cerrando sesión...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "¡Por favor introduzca su correo electrónico o teléfono!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "¡Por favor ingrese su código!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "¡Por favor, ingresa tu nombre real!",
"Please select your country code!": "¡Por favor seleccione su código de país!",
"Please select your country/region!": "¡Por favor seleccione su país/región!",
"Regex": "Regex",
"Terms of Use": "Términos de uso",
"Terms of Use - Tooltip": "Términos de uso que los usuarios necesitan leer y aceptar durante el registro",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Cuentas gestionadas",
"Modify password...": "Modificar contraseña...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "Nuevo correo electrónico",
"New Password": "Nueva contraseña",
"New User": "Nuevo Usuario",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Subir una foto",
"Value": "Value",
"Values": "Valores",
"Verification code sent": "Código de verificación enviado",
"WebAuthn credentials": "Credenciales de WebAuthn",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "L'application ne permet pas de créer un nouveau compte",
"Token expire": "Expiration du jeton",
"Token expire - Tooltip": "Durée avant expiration du jeton d'accès",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Format de jeton",
"Token format - Tooltip": "Le format du jeton d'accès",
"You are unexpected to see this prompt page": "Il n'était pas prévu que vous voyez cette page de saisie"
@ -265,6 +267,8 @@
"Models": "Modèles",
"Name": "Nom",
"Name - Tooltip": "Identifiant unique à base de chaîne",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "Aucun",
"OAuth providers": "Fournisseurs OAuth",
"OK": "Ok",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Échec de l'obtention de l'autorisation MetaMask",
"Failed to obtain Web3-Onboard authorization": "Échec de l'obtention de l'autorisation MetaMask",
"Forgot password?": "Mot de passe oublié ?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Chargement",
"Logging out...": "Déconnexion...",
"MetaMask plugin not detected": "Le plugin MetaMask n'a pas été détecté",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Veuillez saisir votre adresse e-mail ou votre numéro de téléphone !",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Veuillez saisir votre code !",
"Please input your organization name!": "Veuillez saisir le nom de votre organisation !",
@ -881,6 +888,7 @@
"Please input your real name!": "Veuillez saisir votre nom complet !",
"Please select your country code!": "Sélectionnez votre code de pays, s'il vous plaît !",
"Please select your country/region!": "Veuillez sélectionner votre pays/région !",
"Regex": "Regex",
"Terms of Use": "Conditions d'utilisation",
"Terms of Use - Tooltip": "Conditions d'utilisation qui doivent être lus acceptés lors de l'enregistrement du compte",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Comptes gérés",
"Modify password...": "Modifier le mot de passe...",
"Multi-factor authentication": "Authentification multifacteur",
"Name": "Name",
"Name format": "Name format",
"New Email": "Nouvelle adresse e-mail",
"New Password": "Nouveau mot de passe",
"New User": "Nouveau compte",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Télécharger une photo de l'endroit de la pièce d'identité",
"Upload ID card with person picture": "Télécharger une photo la pièce d'identité avec une personne",
"Upload a photo": "Télécharger une photo",
"Value": "Value",
"Values": "Valeurs",
"Verification code sent": "Code de vérification envoyé",
"WebAuthn credentials": "Identifiants WebAuthn",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "Aplikasi tidak memperbolehkan untuk mendaftar akun baru",
"Token expire": "Token kadaluarsa",
"Token expire - Tooltip": "Waktu kadaluwarsa token akses",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Format token",
"Token format - Tooltip": "Format dari token akses",
"You are unexpected to see this prompt page": "Anda tidak mengharapkan untuk melihat halaman prompt ini"
@ -265,6 +267,8 @@
"Models": "Model-model",
"Name": "Nama",
"Name - Tooltip": "ID unik berbasis string",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "Penyedia OAuth",
"OK": "Baik atau Oke",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Lupa kata sandi?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Memuat",
"Logging out...": "Keluar...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Silahkan masukkan email atau nomor telepon Anda!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Silakan masukkan kode Anda!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Silakan masukkan nama asli Anda!",
"Please select your country code!": "Tolong pilih kode negara Anda!",
"Please select your country/region!": "Silakan pilih negara/region Anda!",
"Regex": "Regex",
"Terms of Use": "Syarat Penggunaan",
"Terms of Use - Tooltip": "Syarat penggunaan yang harus dibaca dan disetujui oleh pengguna selama proses registrasi",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Akun yang dikelola",
"Modify password...": "Mengubah kata sandi...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "Email baru",
"New Password": "Kata Sandi Baru",
"New User": "Pengguna Baru",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Unggah foto",
"Value": "Value",
"Values": "Nilai-nilai",
"Verification code sent": "Kode verifikasi telah dikirim",
"WebAuthn credentials": "Kredensial WebAuthn",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "アプリケーションでは新しいアカウントの登録ができません",
"Token expire": "トークンの有効期限が切れました",
"Token expire - Tooltip": "アクセストークンの有効期限",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "トークン形式",
"Token format - Tooltip": "アクセストークンのフォーマット",
"You are unexpected to see this prompt page": "このプロンプトページを見ることは予期せぬことである"
@ -265,6 +267,8 @@
"Models": "モデル",
"Name": "名前",
"Name - Tooltip": "ユニークで文字列ベースのID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuthプロバイダー",
"OK": "了解",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "パスワードを忘れましたか?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "ローディング",
"Logging out...": "ログアウト中...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "あなたのメールアドレスまたは電話番号を入力してください!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "あなたのコードを入力してください!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "正しい名前を入力してください!",
"Please select your country code!": "あなたの国コードを選択してください!",
"Please select your country/region!": "あなたの国/地域を選択してください!",
"Regex": "Regex",
"Terms of Use": "利用規約",
"Terms of Use - Tooltip": "ユーザーが登録する際に読んで同意する必要がある利用規約",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "管理アカウント",
"Modify password...": "パスワードを変更する...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "新しいメール",
"New Password": "新しいパスワード",
"New User": "新しいユーザー",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "写真をアップロードしてください",
"Value": "Value",
"Values": "価値観",
"Verification code sent": "確認コードを送信しました",
"WebAuthn credentials": "WebAuthnの資格情報",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "이 어플리케이션은 새 계정 등록을 허용하지 않습니다",
"Token expire": "토큰 만료",
"Token expire - Tooltip": "액세스 토큰 만료 시간",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "토큰 형식",
"Token format - Tooltip": "접근 토큰의 형식",
"You are unexpected to see this prompt page": "당신은 이 프롬프트 페이지를 볼 것을 예상하지 못했습니다"
@ -265,6 +267,8 @@
"Models": "모델들",
"Name": "이름",
"Name - Tooltip": "고유한 문자열 기반 ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth 공급자",
"OK": "예",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "비밀번호를 잊으셨나요?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "로딩 중입니다",
"Logging out...": "로그아웃 중...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "이메일 또는 전화번호를 입력해주세요!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "코드를 입력해주세요!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "진짜 이름을 입력해주세요!",
"Please select your country code!": "국가 코드를 선택해 주세요!",
"Please select your country/region!": "국가 / 지역을 선택해주세요!",
"Regex": "Regex",
"Terms of Use": "사용 약관",
"Terms of Use - Tooltip": "등록 중 사용자가 읽어야 하고 동의해야하는 이용 약관",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "관리 계정",
"Modify password...": "비밀번호 수정하기...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "새 이메일",
"New Password": "새로운 비밀번호",
"New User": "새로운 사용자",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "사진을 업로드하세요",
"Value": "Value",
"Values": "가치들",
"Verification code sent": "인증 코드가 전송되었습니다",
"WebAuthn credentials": "웹 인증 자격증명",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "A aplicação não permite o registro de novas contas",
"Token expire": "Expiração do Token",
"Token expire - Tooltip": "Tempo de expiração do token de acesso",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Formato do token",
"Token format - Tooltip": "O formato do token de acesso",
"You are unexpected to see this prompt page": "Você não deveria ver esta página de prompt"
@ -265,6 +267,8 @@
"Models": "Modelos",
"Name": "Nome",
"Name - Tooltip": "ID único em formato de string",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "Provedores OAuth",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Esqueceu a senha?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Carregando",
"Logging out...": "Saindo...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Por favor, informe seu email ou telefone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Por favor, informe o código!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Por favor, insira seu nome real!",
"Please select your country code!": "Por favor, selecione o código do seu país!",
"Please select your country/region!": "Por favor, selecione seu país/região!",
"Regex": "Regex",
"Terms of Use": "Termos de Uso",
"Terms of Use - Tooltip": "Termos de uso que os usuários precisam ler e concordar durante o registro",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Contas gerenciadas",
"Modify password...": "Modificar senha...",
"Multi-factor authentication": "Autenticação de vários fatores",
"Name": "Name",
"Name format": "Name format",
"New Email": "Novo E-mail",
"New Password": "Nova Senha",
"New User": "Novo Usuário",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Enviar uma foto",
"Value": "Value",
"Values": "Valores",
"Verification code sent": "Código de verificação enviado",
"WebAuthn credentials": "Credenciais WebAuthn",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "Приложение не позволяет зарегистрироваться новому аккаунту",
"Token expire": "Срок действия токена истекает",
"Token expire - Tooltip": "Время истечения токена доступа",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Формат жетона",
"Token format - Tooltip": "Формат токена доступа",
"You are unexpected to see this prompt page": "Вы не ожидали увидеть эту страницу-подсказку"
@ -265,6 +267,8 @@
"Models": "Модели",
"Name": "Имя",
"Name - Tooltip": "Уникальный идентификатор на основе строки",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "Провайдеры OAuth",
"OK": "OK - Хорошо",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Забыли пароль?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Загрузка",
"Logging out...": "Выход...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Пожалуйста, введите свой адрес электронной почты или номер телефона!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Пожалуйста, введите свой код!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Пожалуйста, введите своё настоящее имя!",
"Please select your country code!": "Пожалуйста, выберите код своей страны!",
"Please select your country/region!": "Пожалуйста, выберите свою страну / регион!",
"Regex": "Regex",
"Terms of Use": "Условия использования",
"Terms of Use - Tooltip": "Условия использования, которые пользователи должны прочитать и согласиться с ними при регистрации",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Управляемые счета",
"Modify password...": "Изменить пароль...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "Новое электронное письмо",
"New Password": "Новый пароль",
"New User": "Новый пользователь",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Загрузить фото",
"Value": "Value",
"Values": "Значения",
"Verification code sent": "Код подтверждения отправлен",
"WebAuthn credentials": "WebAuthn удостоверения",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Access token expiration time",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Token format",
"Token format - Tooltip": "The format of access token",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
@ -265,6 +267,8 @@
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Unique, string-based ID",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "OAuth providers",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Forgot password?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Loading",
"Logging out...": "Logging out...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Please input your code!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Please input your real name!",
"Please select your country code!": "Please select your country code!",
"Please select your country/region!": "Please select your country/region!",
"Regex": "Regex",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of use that users need to read and agree to during registration",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "New Email",
"New Password": "New Password",
"New User": "New User",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Upload a photo",
"Value": "Value",
"Values": "Values",
"Verification code sent": "Verification code sent",
"WebAuthn credentials": "WebAuthn credentials",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "Ứng dụng không cho phép đăng ký tài khoản mới",
"Token expire": "Mã thông báo hết hạn",
"Token expire - Tooltip": "Thời gian hết hạn của mã truy cập",
"Token fields": "Token fields",
"Token fields - Tooltip": "Token fields - Tooltip",
"Token format": "Định dạng mã thông báo",
"Token format - Tooltip": "Định dạng của mã thông báo truy cập",
"You are unexpected to see this prompt page": "Bạn không mong đợi thấy trang này hiện lên"
@ -265,6 +267,8 @@
"Models": "Mô hình",
"Name": "Tên",
"Name - Tooltip": "ID duy nhất dựa trên chuỗi",
"Name format": "Name format",
"Non-LDAP": "Non-LDAP",
"None": "None",
"OAuth providers": "Nhà cung cấp OAuth",
"OK": "Được rồi",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "Failed to obtain MetaMask authorization",
"Failed to obtain Web3-Onboard authorization": "Failed to obtain Web3-Onboard authorization",
"Forgot password?": "Quên mật khẩu?",
"LDAP": "LDAP",
"LDAP username, Email or phone": "LDAP username, Email or phone",
"Loading": "Đang tải",
"Logging out...": "Đăng xuất ...",
"MetaMask plugin not detected": "MetaMask plugin not detected",
@ -439,6 +445,7 @@
"Phone": "Phone",
"Please input your Email or Phone!": "Vui lòng nhập địa chỉ Email hoặc số điện thoại của bạn!",
"Please input your Email!": "Please input your Email!",
"Please input your LDAP username!": "Please input your LDAP username!",
"Please input your Phone!": "Please input your Phone!",
"Please input your code!": "Vui lòng nhập mã của bạn!",
"Please input your organization name!": "Please input your organization name!",
@ -881,6 +888,7 @@
"Please input your real name!": "Vui lòng nhập tên thật của bạn!",
"Please select your country code!": "Vui lòng chọn mã quốc gia của bạn!",
"Please select your country/region!": "Vui lòng chọn quốc gia/vùng của bạn!",
"Regex": "Regex",
"Terms of Use": "Điều khoản sử dụng",
"Terms of Use - Tooltip": "Điều khoản sử dụng mà người dùng cần đọc và đồng ý trong quá trình đăng ký",
"Text 1": "Text 1",
@ -1047,8 +1055,6 @@
"Managed accounts": "Quản lý tài khoản",
"Modify password...": "Sửa đổi mật khẩu...",
"Multi-factor authentication": "Multi-factor authentication",
"Name": "Name",
"Name format": "Name format",
"New Email": "Email mới",
"New Password": "Mật khẩu mới",
"New User": "Người dùng mới",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload a photo": "Tải lên một bức ảnh",
"Value": "Value",
"Values": "Giá trị",
"Verification code sent": "Mã xác minh đã được gửi",
"WebAuthn credentials": "Chứng chỉ WebAuthn",

View File

@ -103,6 +103,8 @@
"The application does not allow to sign up new account": "该应用不允许注册新账户",
"Token expire": "Access Token过期",
"Token expire - Tooltip": "Access Token过期时间",
"Token fields": "Token字段",
"Token fields - Tooltip": "Token中所包含的用户字段",
"Token format": "Access Token格式",
"Token format - Tooltip": "Access Token格式",
"You are unexpected to see this prompt page": "错误:该提醒页面不应出现"
@ -265,6 +267,8 @@
"Models": "Casbin模型",
"Name": "名称",
"Name - Tooltip": "唯一的、字符串式的ID",
"Name format": "名称格式",
"Non-LDAP": "禁用LDAP",
"None": "无",
"OAuth providers": "OAuth提供方",
"OK": "OK",
@ -431,6 +435,8 @@
"Failed to obtain MetaMask authorization": "获取MetaMask授权失败",
"Failed to obtain Web3-Onboard authorization": "获取 Web3-Onboard 授权失败",
"Forgot password?": "忘记密码?",
"LDAP": "LDAP登录",
"LDAP username, Email or phone": "LDAP用户名, Email或手机号",
"Loading": "加载中",
"Logging out...": "正在退出登录...",
"MetaMask plugin not detected": "未检测到MetaMask插件",
@ -439,6 +445,7 @@
"Phone": "手机号",
"Please input your Email or Phone!": "请输入您的Email或手机号!",
"Please input your Email!": "请输入您的Email!",
"Please input your LDAP username!": "请输入您的LDAP用户名!",
"Please input your Phone!": "请输入您的手机号!",
"Please input your code!": "请输入您的验证码!",
"Please input your organization name!": "请输入组织的名字!",
@ -881,6 +888,7 @@
"Please input your real name!": "请输入您的姓名!",
"Please select your country code!": "请选择国家代码!",
"Please select your country/region!": "请选择您的国家或地区",
"Regex": "正则表达式",
"Terms of Use": "《用户协议》",
"Terms of Use - Tooltip": "用户注册时需要阅读并同意的使用条款",
"Text 1": "文本1",
@ -1047,8 +1055,6 @@
"Managed accounts": "托管账户",
"Modify password...": "编辑密码...",
"Multi-factor authentication": "多因素认证",
"Name": "Name",
"Name format": "Name format",
"New Email": "新邮箱",
"New Password": "新密码",
"New User": "添加用户",
@ -1086,7 +1092,6 @@
"Upload ID card front picture": "上传身份证正面照片",
"Upload ID card with person picture": "上传手持身份证照片",
"Upload a photo": "上传头像",
"Value": "值",
"Values": "值",
"Verification code sent": "验证码已发送",
"WebAuthn credentials": "WebAuthn凭据",

View File

@ -38,7 +38,7 @@ class SamlAttributeTable extends React.Component {
}
addRow(table) {
const row = {Name: "", nameformat: "", value: ""};
const row = {Name: "", nameFormat: "", value: ""};
if (table === undefined || table === null) {
table = [];
}
@ -64,7 +64,7 @@ class SamlAttributeTable extends React.Component {
renderTable(table) {
const columns = [
{
title: i18next.t("user:Name"),
title: i18next.t("general:Name"),
dataIndex: "name",
key: "name",
width: "200px",
@ -77,9 +77,9 @@ class SamlAttributeTable extends React.Component {
},
},
{
title: i18next.t("user:Name format"),
dataIndex: "nameformat",
key: "nameformat",
title: i18next.t("general:Name format"),
dataIndex: "nameFormat",
key: "nameFormat",
width: "200px",
render: (text, record, index) => {
return (
@ -87,7 +87,7 @@ class SamlAttributeTable extends React.Component {
value={text}
defaultValue="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"
onChange={value => {
this.updateField(table, index, "nameformat", value);
this.updateField(table, index, "nameFormat", value);
}} >
<Option key="Unspecified" value="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">Unspecified</Option>
<Option key="Basic" value="urn:oasis:names:tc:SAML:2.0:attrname-format:basic">Basic</Option>
@ -98,7 +98,7 @@ class SamlAttributeTable extends React.Component {
},
},
{
title: i18next.t("user:Value"),
title: i18next.t("webhook:Value"),
dataIndex: "value",
key: "value",
width: "200px",

View File

@ -20,7 +20,7 @@ import i18next from "i18next";
const {Option} = Select;
class SigninTable extends React.Component {
class SigninMethodTable extends React.Component {
constructor(props) {
super(props);
this.state = {
@ -70,6 +70,7 @@ class SigninTable extends React.Component {
{name: "Password", displayName: i18next.t("general:Password")},
{name: "Verification code", displayName: i18next.t("login:Verification code")},
{name: "WebAuthn", displayName: i18next.t("login:WebAuthn")},
{name: "LDAP", displayName: i18next.t("login:LDAP")},
];
const columns = [
{
@ -91,7 +92,7 @@ class SigninTable extends React.Component {
onChange={value => {
this.updateField(table, index, "name", value);
this.updateField(table, index, "displayName", value);
if (value === "Verification code") {
if (value === "Verification code" || value === "Password") {
this.updateField(table, index, "rule", "All");
} else {
this.updateField(table, index, "rule", "None");
@ -130,6 +131,11 @@ class SigninTable extends React.Component {
{id: "Email only", name: i18next.t("general:Email only")},
{id: "Phone only", name: i18next.t("general:Phone only")},
];
} else if (record.name === "Password") {
options = [
{id: "All", name: i18next.t("general:All")},
{id: "Non-LDAP", name: i18next.t("general:Non-LDAP")},
];
}
if (options.length === 0) {
@ -157,7 +163,7 @@ class SigninTable extends React.Component {
<Button style={{marginRight: "5px"}} disabled={index === table.length - 1} icon={<DownOutlined />} size="small" onClick={() => this.downRow(table, index)} />
</Tooltip>
<Tooltip placement="topLeft" title={i18next.t("general:Delete")}>
<Button icon={<DeleteOutlined />} size="small" disabled={Setting.getDeduplicatedArray(items, table, "name").length >= items.length - 1} onClick={() => this.deleteRow(items, table, index)} />
<Button icon={<DeleteOutlined />} size="small" disabled={table.length <= 1} onClick={() => this.deleteRow(items, table, index)} />
</Tooltip>
</div>
);
@ -192,4 +198,4 @@ class SigninTable extends React.Component {
}
}
export default SigninTable;
export default SigninMethodTable;

View File

@ -224,6 +224,23 @@ class SignupTable extends React.Component {
);
},
},
{
title: i18next.t("signup:Regex"),
dataIndex: "regex",
key: "regex",
width: "200px",
render: (text, record, index) => {
if (record.name.startsWith("Text ") || record.name === "Password" || record.name === "Confirm password") {
return null;
}
return (
<Input value={text} onChange={e => {
this.updateField(table, index, "regex", e.target.value);
}} />
);
},
},
{
title: i18next.t("application:Rule"),
dataIndex: "rule",

View File

@ -98,11 +98,7 @@ class SyncerTableColumnTable extends React.Component {
return (
<Select virtual={false} showSearch style={{width: "100%"}} value={text} onChange={(value => {this.updateField(table, index, "casdoorName", value);})}>
{
["Owner", "Name", "CreatedTime", "UpdatedTime", "Id", "Type", "Password", "PasswordSalt", "DisplayName", "FirstName", "LastName", "Avatar", "PermanentAvatar",
"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",
"PreferredMfaType", "TotpSecret", "SignupApplication"]
.map((item, index) => <Option key={index} value={item}>{item}</Option>)
Setting.getUserCommonFields().map((item, index) => <Option key={index} value={item}>{item}</Option>)
}
</Select>
);