Compare commits

..

15 Commits

Author SHA1 Message Date
1e0b709c73 feat: pass signin method to CAS login to fix bug (#3313) 2024-10-24 14:56:12 +08:00
c0800b7fb3 feat: add util.IsValidOrigin() to improve CORS filter (#3301)
* fix: CORS check issue

* fix: promote format

* fix: promote format

* fix: promote format

* fix: promote format

* Update application.go

* Update cors_filter.go

* Update validation.go

---------

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2024-10-20 20:09:21 +08:00
6fcdad2100 feat: fix bug that fails to login when PasswordObfuscator is enabled (#3299) 2024-10-19 23:09:59 +08:00
69d26d5c21 feat: add-user/update-user API should check if username/id/email/phone has duplicated with existing user (#3295) 2024-10-18 22:18:37 +08:00
94e6b5ecb8 feat: fix bug in SetPassword() API (#3296) 2024-10-18 20:50:43 +08:00
95e8bdcd36 feat: add initDataNewOnly to app.conf to skip overriding existing data in initDataFromFile() (#3294)
* feat: support control whether overwrite existing data during initDataFromFile

* feat: change conf var name

* feat: change conf var name
2024-10-18 00:08:08 +08:00
6f1f93725e feat: fix GetAllActions()'s bug (#3289) 2024-10-16 21:55:06 +08:00
7ae067e369 feat: only admin can specify user in BuyProduct() (#3287)
* fix: balance can be used without login

* fix: balance can be used without login

* fix: fix bug

* fix: fix bug
2024-10-16 00:02:04 +08:00
dde936e935 feat: fix null application crash in CheckEntryIp() 2024-10-15 22:11:15 +08:00
fb561a98c8 feat: fix null user crash in RefreshToken() 2024-10-15 21:38:33 +08:00
7cd8f030ee feat: support IP limitation for user entry pages (#3267)
* feat: support IP limitation for user entry pages

* fix: error message, ip whiteList, check_entry_ip

* fix: perform checks on the backend

* fix: change the implementation of checking IpWhitelist

* fix: add entryIpCheck in SetPassword and remove it from VerifyCode

* fix: remove additional error message pop-ups

* fix: add isRestricted and show ip error in EntryPage.js

* fix: error message

* Update auth.go

* Update check_ip.go

* Update check_ip.go

* fix: update return value of the check function from string to error

* fix: remoteAddress position

* fix: IP whitelist

* fix: clientIp

* fix:add util.GetClientIpFromRequest

* fix: remove duplicate IP and port separation codes and remove extra special characters after clientIp

* fix: gofumpt

* fix: getIpInfo and localhost

---------

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2024-10-15 20:40:14 +08:00
a3f8ded10c feat: refactor util.GetClientIpFromRequest() 2024-10-15 12:22:38 +08:00
e3d135bc6e feat: improve MFA desc text (#3284)
* fix: fix i18n error for mfa

* fix: fix i18n error for mfa

* fix: promote translate
2024-10-14 18:31:48 +08:00
fc864b0de4 feat: support ".login-panel-dark" CSS for signup/login pages (#3269)
* feat: add custom dark mode CSS for login and registration forms.

* refactor: extract dark theme check to Setting.js
2024-10-13 22:31:54 +08:00
3211bcc777 feat: add getCaptchaRule() to fix bug (#3281)
* feat: update captcha rule when the login page component is mounted

* fix: remove enableCaptchaModel from the state of the login page to avoid inconsistency issues

* fix: use this.getApplicationObj() instead of this.props.application
2024-10-12 10:02:45 +08:00
57 changed files with 531 additions and 139 deletions

View File

@ -29,5 +29,6 @@ radiusServerPort = 1812
radiusSecret = "secret"
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}
logConfig = {"filename": "logs/casdoor.log", "maxdays":99999, "perm":"0770"}
initDataNewOnly = false
initDataFile = "./init_data.json"
frontendBaseDir = "../casdoor"
frontendBaseDir = "../cc_0"

View File

@ -116,6 +116,13 @@ func (c *ApiController) Signup() {
return
}
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
err = object.CheckEntryIp(clientIp, nil, application, organization, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
msg := object.CheckUserSignup(application, organization, &authForm, c.GetAcceptLanguage())
if msg != "" {
c.ResponseError(msg)

View File

@ -110,6 +110,9 @@ func (c *ApiController) GetApplication() {
}
}
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
object.CheckEntryIp(clientIp, nil, application, nil, c.GetAcceptLanguage())
c.ResponseOk(object.GetMaskedApplication(application, userId))
}
@ -229,6 +232,11 @@ func (c *ApiController) UpdateApplication() {
return
}
if err = object.CheckIpWhitelist(application.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateApplication(id, &application))
c.ServeJSON()
}
@ -259,6 +267,11 @@ func (c *ApiController) AddApplication() {
return
}
if err = object.CheckIpWhitelist(application.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddApplication(&application))
c.ServeJSON()
}

View File

@ -55,6 +55,13 @@ func tokenToResponse(token *object.Token) *Response {
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *form.AuthForm) (resp *Response) {
userId := user.GetId()
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
err := object.CheckEntryIp(clientIp, user, application, application.OrganizationObj, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
allowed, err := object.CheckLoginPermission(userId, application)
if err != nil {
c.ResponseError(err.Error(), nil)
@ -256,6 +263,9 @@ func (c *ApiController) GetApplicationLogin() {
}
}
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
object.CheckEntryIp(clientIp, nil, application, nil, c.GetAcceptLanguage())
application = object.GetMaskedApplication(application, "")
if msg != "" {
c.ResponseError(msg, application)

View File

@ -119,6 +119,11 @@ func (c *ApiController) UpdateOrganization() {
return
}
if err = object.CheckIpWhitelist(organization.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateOrganization(id, &organization))
c.ServeJSON()
}
@ -149,6 +154,11 @@ func (c *ApiController) AddOrganization() {
return
}
if err = object.CheckIpWhitelist(organization.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddOrganization(&organization))
c.ServeJSON()
}

View File

@ -182,6 +182,10 @@ func (c *ApiController) BuyProduct() {
paidUserName := c.Input().Get("userName")
owner, _ := util.GetOwnerAndNameFromId(id)
userId := util.GetId(owner, paidUserName)
if paidUserName != "" && !c.IsAdmin() {
c.ResponseError(c.T("general:Only admin user can specify user"))
return
}
if paidUserName == "" {
userId = c.GetSessionUsername()
}

View File

@ -364,7 +364,8 @@ func (c *ApiController) AddUser() {
return
}
msg := object.CheckUsername(user.Name, c.GetAcceptLanguage())
emptyUser := object.User{}
msg := object.CheckUpdateUser(&emptyUser, &user, c.GetAcceptLanguage())
if msg != "" {
c.ResponseError(msg)
return
@ -489,7 +490,12 @@ func (c *ApiController) SetPassword() {
c.ResponseError(c.T("general:Missing parameter"))
return
}
if userId != c.GetSession("verifiedUserId") {
c.ResponseError(c.T("general:Wrong userId"))
return
}
c.SetSession("verifiedCode", "")
c.SetSession("verifiedUserId", "")
}
targetUser, err := object.GetUser(userId)
@ -535,6 +541,23 @@ func (c *ApiController) SetPassword() {
return
}
application, err := object.GetApplicationByUser(targetUser)
if err != nil {
c.ResponseError(err.Error())
return
}
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:the application for user %s is not found"), userId))
return
}
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
err = object.CheckEntryIp(clientIp, targetUser, application, organization, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
targetUser.Password = newPassword
targetUser.UpdateUserPassword(organization)
targetUser.NeedUpdatePassword = false

View File

@ -132,7 +132,8 @@ func (c *ApiController) SendVerificationCode() {
c.ResponseError(err.Error())
return
}
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
if msg := vform.CheckParameter(form.SendVerifyCode, c.GetAcceptLanguage()); msg != "" {
c.ResponseError(msg)
@ -259,7 +260,7 @@ func (c *ApiController) SendVerificationCode() {
return
}
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, vform.Dest)
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, clientIp, vform.Dest)
case object.VerifyTypePhone:
if vform.Method == LoginVerification || vform.Method == ForgetVerification {
if user != nil && util.GetMaskedPhone(user.Phone) == vform.Dest {
@ -309,7 +310,7 @@ func (c *ApiController) SendVerificationCode() {
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), vform.CountryCode))
return
} else {
sendResp = object.SendVerificationCodeToPhone(organization, user, provider, remoteAddr, phone)
sendResp = object.SendVerificationCodeToPhone(organization, user, provider, clientIp, phone)
}
}
@ -532,5 +533,6 @@ func (c *ApiController) VerifyCode() {
}
c.SetSession("verifiedCode", authForm.Code)
c.SetSession("verifiedUserId", user.GetId())
c.ResponseOk()
}

View File

@ -15,10 +15,10 @@
"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 face is not enabled for the application": "The login method: login with face is not enabled for the application",
"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": "Метод входа: вход с помощью SMS не включен для приложения",
"The login method: login with email is not enabled for the application": "Метод входа: вход с помощью электронной почты не включен для приложения",
"The login method: login with face is not enabled for the application": "Метод входа: вход с помощью лица не включен для приложения",
"The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения",
"The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
@ -53,16 +53,16 @@
"Phone already exists": "Телефон уже существует",
"Phone cannot be empty": "Телефон не может быть пустым",
"Phone number is invalid": "Номер телефона является недействительным",
"Please register using the email corresponding to the invitation code": "Please register using the email corresponding to the invitation code",
"Please register using the phone corresponding to the invitation code": "Please register using the phone corresponding to the invitation code",
"Please register using the username corresponding to the invitation code": "Please register using the username corresponding to the invitation code",
"Please register using the email corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь, используя электронную почту, соответствующую коду приглашения",
"Please register using the phone corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь по телефону, соответствующему коду приглашения",
"Please register using the username corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь, используя имя пользователя, соответствующее коду приглашения",
"Session outdated, please login again": "Сессия устарела, пожалуйста, войдите снова",
"The invitation code has already been used": "The invitation code has already been used",
"The user is forbidden to sign in, please contact the administrator": "Пользователю запрещен вход, пожалуйста, обратитесь к администратору",
"The user: %s doesn't exist in LDAP server": "Пользователь %s не существует на LDAP сервере",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "Имя пользователя может состоять только из буквенно-цифровых символов, нижних подчеркиваний или дефисов, не может содержать последовательные дефисы или подчеркивания, а также не может начинаться или заканчиваться на дефис или подчеркивание.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Значение \\\"%s\\\" для поля аккаунта \\\"%s\\\" не соответствует регулярному значению",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Значение \\\"%s\\\" поля регистрации \\\"%s\\\" не соответствует регулярному выражению приложения \\\"%s\\\"",
"Username already exists": "Имя пользователя уже существует",
"Username cannot be an email address": "Имя пользователя не может быть адресом электронной почты",
"Username cannot contain white spaces": "Имя пользователя не может содержать пробелы",
@ -78,11 +78,11 @@
"general": {
"Missing parameter": "Отсутствующий параметр",
"Please login first": "Пожалуйста, сначала войдите в систему",
"The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The organization: %s should have one application at least": "Организация: %s должна иметь хотя бы одно приложение",
"The user: %s doesn't exist": "Пользователь %s не существует",
"don't support captchaProvider: ": "неподдерживаемый captchaProvider: ",
"this operation is not allowed in demo mode": "эта операция не разрешена в демо-режиме",
"this operation requires administrator to perform": "this operation requires administrator to perform"
"this operation requires administrator to perform": "для выполнения этой операции требуется администратор"
},
"ldap": {
"Ldap server exist": "LDAP-сервер существует"
@ -101,11 +101,11 @@
"Unknown modify rule %s.": "Неизвестное изменение правила %s."
},
"permission": {
"The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
"The permission: \\\"%s\\\" doesn't exist": "Разрешение: \\\"%s\\\" не существует"
},
"provider": {
"Invalid application id": "Неверный идентификатор приложения",
"the provider: %s does not exist": "провайдер: %s не существует"
"the provider: %s does not exist": "Провайдер: %s не существует"
},
"resource": {
"User is nil for tag: avatar": "Пользователь равен нулю для тега: аватар",
@ -115,7 +115,7 @@
"Application %s not found": "Приложение %s не найдено"
},
"saml_sp": {
"provider %s's category is not SAML": "категория провайдера %s не является SAML"
"provider %s's category is not SAML": "Категория провайдера %s не является SAML"
},
"service": {
"Empty parameters for emailForm: %v": "Пустые параметры для emailForm: %v",
@ -148,7 +148,7 @@
"verification": {
"Invalid captcha provider.": "Недействительный поставщик CAPTCHA.",
"Phone number is invalid in your region %s": "Номер телефона недействителен в вашем регионе %s",
"The verification code has not been sent yet!": "The verification code has not been sent yet!",
"The verification code has not been sent yet!": "Код проверки еще не отправлен!",
"The verification code has not been sent yet, or has already been used!": "The verification code has not been sent yet, or has already been used!",
"Turing test failed.": "Тест Тьюринга не удался.",
"Unable to get the email modify rule.": "Невозможно получить правило изменения электронной почты.",
@ -156,8 +156,8 @@
"Unknown type": "Неизвестный тип",
"Wrong verification code!": "Неправильный код подтверждения!",
"You should verify your code in %d min!": "Вы должны проверить свой код через %d минут!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "Пожалуйста, добавьте поставщика SMS в список \\\"Провайдеры\\\" для приложения: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "Пожалуйста, добавьте поставщика электронной почты в список \\\"Провайдеры\\\" для приложения: %s",
"the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь"
},
"webauthn": {

View File

@ -95,6 +95,7 @@ type Application struct {
Tags []string `xorm:"mediumtext" json:"tags"`
SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
IsShared bool `json:"isShared"`
IpRestriction string `json:"ipRestriction"`
ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
@ -108,6 +109,7 @@ type Application struct {
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"`
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"`
SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
@ -721,8 +723,15 @@ func (application *Application) GetId() string {
}
func (application *Application) IsRedirectUriValid(redirectUri string) bool {
redirectUris := append([]string{"http://localhost:", "https://localhost:", "http://127.0.0.1:", "http://casdoor-app", ".chromiumapp.org"}, application.RedirectUris...)
for _, targetUri := range redirectUris {
isValid, err := util.IsValidOrigin(redirectUri)
if err != nil {
panic(err)
}
if isValid {
return true
}
for _, targetUri := range application.RedirectUris {
targetUriRegex := regexp.MustCompile(targetUri)
if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) {
return true

View File

@ -539,6 +539,11 @@ func CheckUpdateUser(oldUser, user *User, lang string) string {
return i18n.Translate(lang, "check:Phone already exists")
}
}
if oldUser.IpWhitelist != user.IpWhitelist {
if err := CheckIpWhitelist(user.IpWhitelist, lang); err != nil {
return err.Error()
}
}
return ""
}

100
object/check_ip.go Normal file
View File

@ -0,0 +1,100 @@
// Copyright 2024 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"fmt"
"net"
"strings"
"github.com/casdoor/casdoor/i18n"
)
func CheckEntryIp(clientIp string, user *User, application *Application, organization *Organization, lang string) error {
entryIp := net.ParseIP(clientIp)
if entryIp == nil {
return fmt.Errorf(i18n.Translate(lang, "check:Failed to parse client IP: %s"), clientIp)
} else if entryIp.IsLoopback() {
return nil
}
var err error
if user != nil {
err = isEntryIpAllowd(user.IpWhitelist, entryIp, lang)
if err != nil {
return fmt.Errorf(err.Error() + user.Name)
}
}
if application != nil {
err = isEntryIpAllowd(application.IpWhitelist, entryIp, lang)
if err != nil {
application.IpRestriction = err.Error() + application.Name
return fmt.Errorf(err.Error() + application.Name)
}
if organization == nil && application.OrganizationObj != nil {
organization = application.OrganizationObj
}
}
if organization != nil {
err = isEntryIpAllowd(organization.IpWhitelist, entryIp, lang)
if err != nil {
organization.IpRestriction = err.Error() + organization.Name
return fmt.Errorf(err.Error() + organization.Name)
}
}
return nil
}
func isEntryIpAllowd(ipWhitelistStr string, entryIp net.IP, lang string) error {
if ipWhitelistStr == "" {
return nil
}
ipWhitelist := strings.Split(ipWhitelistStr, ",")
for _, ip := range ipWhitelist {
_, ipNet, err := net.ParseCIDR(ip)
if err != nil {
return err
}
if ipNet == nil {
return fmt.Errorf(i18n.Translate(lang, "check:CIDR for IP: %s should not be empty"), entryIp.String())
}
if ipNet.Contains(entryIp) {
return nil
}
}
return fmt.Errorf(i18n.Translate(lang, "check:Your IP address: %s has been banned according to the configuration of: "), entryIp.String())
}
func CheckIpWhitelist(ipWhitelistStr string, lang string) error {
if ipWhitelistStr == "" {
return nil
}
ipWhiteList := strings.Split(ipWhitelistStr, ",")
for _, ip := range ipWhiteList {
if _, _, err := net.ParseCIDR(ip); err != nil {
return fmt.Errorf(i18n.Translate(lang, "check:%s does not meet the CIDR format requirements: %s"), ip, err.Error())
}
}
return nil
}

View File

@ -48,12 +48,16 @@ type InitData struct {
Transactions []*Transaction `json:"transactions"`
}
var initDataNewOnly bool
func InitFromFile() {
initDataFile := conf.GetConfigString("initDataFile")
if initDataFile == "" {
return
}
initDataNewOnly = conf.GetConfigBool("initDataNewOnly")
initData, err := readInitDataFromFile(initDataFile)
if err != nil {
panic(err)
@ -269,6 +273,9 @@ func initDefinedOrganization(organization *Organization) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := deleteOrganization(organization)
if err != nil {
panic(err)
@ -295,6 +302,9 @@ func initDefinedApplication(application *Application) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := deleteApplication(application)
if err != nil {
panic(err)
@ -316,6 +326,9 @@ func initDefinedUser(user *User) {
panic(err)
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := deleteUser(user)
if err != nil {
panic(err)
@ -342,6 +355,9 @@ func initDefinedCert(cert *Cert) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteCert(cert)
if err != nil {
panic(err)
@ -364,6 +380,9 @@ func initDefinedLdap(ldap *Ldap) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteLdap(ldap)
if err != nil {
panic(err)
@ -385,6 +404,9 @@ func initDefinedProvider(provider *Provider) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteProvider(provider)
if err != nil {
panic(err)
@ -406,6 +428,9 @@ func initDefinedModel(model *Model) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteModel(model)
if err != nil {
panic(err)
@ -428,6 +453,9 @@ func initDefinedPermission(permission *Permission) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := deletePermission(permission)
if err != nil {
panic(err)
@ -450,6 +478,9 @@ func initDefinedPayment(payment *Payment) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeletePayment(payment)
if err != nil {
panic(err)
@ -472,6 +503,9 @@ func initDefinedProduct(product *Product) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteProduct(product)
if err != nil {
panic(err)
@ -494,6 +528,9 @@ func initDefinedResource(resource *Resource) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteResource(resource)
if err != nil {
panic(err)
@ -516,6 +553,9 @@ func initDefinedRole(role *Role) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := deleteRole(role)
if err != nil {
panic(err)
@ -538,6 +578,9 @@ func initDefinedSyncer(syncer *Syncer) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteSyncer(syncer)
if err != nil {
panic(err)
@ -560,6 +603,9 @@ func initDefinedToken(token *Token) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteToken(token)
if err != nil {
panic(err)
@ -582,6 +628,9 @@ func initDefinedWebhook(webhook *Webhook) {
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteWebhook(webhook)
if err != nil {
panic(err)
@ -603,6 +652,9 @@ func initDefinedGroup(group *Group) {
panic(err)
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := deleteGroup(group)
if err != nil {
panic(err)
@ -624,6 +676,9 @@ func initDefinedAdapter(adapter *Adapter) {
panic(err)
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteAdapter(adapter)
if err != nil {
panic(err)
@ -645,6 +700,9 @@ func initDefinedEnforcer(enforcer *Enforcer) {
panic(err)
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteEnforcer(enforcer)
if err != nil {
panic(err)
@ -666,6 +724,9 @@ func initDefinedPlan(plan *Plan) {
panic(err)
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeletePlan(plan)
if err != nil {
panic(err)
@ -687,6 +748,9 @@ func initDefinedPricing(pricing *Pricing) {
panic(err)
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeletePricing(pricing)
if err != nil {
panic(err)
@ -708,6 +772,9 @@ func initDefinedInvitation(invitation *Invitation) {
panic(err)
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteInvitation(invitation)
if err != nil {
panic(err)
@ -743,6 +810,9 @@ func initDefinedSubscription(subscription *Subscription) {
panic(err)
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteSubscription(subscription)
if err != nil {
panic(err)
@ -764,6 +834,9 @@ func initDefinedTransaction(transaction *Transaction) {
panic(err)
}
if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteTransaction(transaction)
if err != nil {
panic(err)

View File

@ -71,11 +71,13 @@ type Organization struct {
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
DefaultPassword string `xorm:"varchar(100)" json:"defaultPassword"`
MasterVerificationCode string `xorm:"varchar(100)" json:"masterVerificationCode"`
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
InitScore int `json:"initScore"`
EnableSoftDeletion bool `json:"enableSoftDeletion"`
IsProfilePublic bool `json:"isProfilePublic"`
UseEmailAsUsername bool `json:"useEmailAsUsername"`
EnableTour bool `json:"enableTour"`
IpRestriction string `json:"ipRestriction"`
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`

View File

@ -364,7 +364,7 @@ func GetAllActions(userId string) ([]string, error) {
res := []string{}
for _, enforcer := range enforcers {
items := enforcer.GetAllObjects()
items := enforcer.GetAllActions()
res = append(res, items...)
}
return res, nil

View File

@ -50,7 +50,7 @@ func maskPassword(recordString string) string {
}
func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1)
clientIp := strings.Replace(util.GetClientIpFromRequest(ctx.Request), ": ", "", -1)
action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1)
requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"})
if len(requestUri) > 1000 {
@ -83,7 +83,7 @@ func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
record := casvisorsdk.Record{
Name: util.GenerateId(),
CreatedTime: util.GetCurrentTime(),
ClientIp: ip,
ClientIp: clientIp,
User: "",
Method: ctx.Request.Method,
RequestUri: requestUri,

View File

@ -332,6 +332,9 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
if err != nil {
return nil, err
}
if user == nil {
return "", fmt.Errorf("The user: %s doesn't exist", util.GetId(application.Organization, token.User))
}
if user.IsForbidden {
return &TokenError{

View File

@ -206,6 +206,7 @@ type User struct {
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
MfaAccounts []MfaAccount `xorm:"mfaAccounts blob" json:"mfaAccounts"`
NeedUpdatePassword bool `json:"needUpdatePassword"`
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
}
type Userinfo struct {
@ -696,7 +697,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
"spotify", "strava", "stripe", "type", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo",
"yammer", "yandex", "zoom", "custom", "need_update_password",
"yammer", "yandex", "zoom", "custom", "need_update_password", "ip_whitelist",
}
}
if isAdmin {

View File

@ -557,6 +557,14 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
itemsChanged = append(itemsChanged, item)
}
}
if oldUser.IpWhitelist != newUser.IpWhitelist {
item := GetAccountItemByName("IP whitelist", organization)
if item == nil {
newUser.IpWhitelist = oldUser.IpWhitelist
} else {
itemsChanged = append(itemsChanged, item)
}
}
if oldUser.Balance != newUser.Balance {
item := GetAccountItemByName("Balance", organization)

View File

@ -16,11 +16,11 @@ package routers
import (
"net/http"
"strings"
"github.com/beego/beego/context"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
)
const (
@ -52,7 +52,13 @@ func CorsFilter(ctx *context.Context) {
origin = ""
}
if strings.HasPrefix(origin, "http://localhost") || strings.HasPrefix(origin, "https://localhost") || strings.HasPrefix(origin, "http://127.0.0.1") || strings.HasPrefix(origin, "http://casdoor-app") || strings.Contains(origin, ".chromiumapp.org") {
isValid, err := util.IsValidOrigin(origin)
if err != nil {
ctx.ResponseWriter.WriteHeader(http.StatusForbidden)
responseError(ctx, err.Error())
return
}
if isValid {
setCorsHeaders(ctx, origin)
return
}

View File

@ -23,50 +23,50 @@ import (
"github.com/beego/beego/logs"
)
func GetIPInfo(clientIP string) string {
if clientIP == "" {
func getIpInfo(clientIp string) string {
if clientIp == "" {
return ""
}
ips := strings.Split(clientIP, ",")
res := ""
for i := range ips {
ip := strings.TrimSpace(ips[i])
// desc := GetDescFromIP(ip)
ipstr := fmt.Sprintf("%s: %s", ip, "")
if i != len(ips)-1 {
res += ipstr + " -> "
} else {
res += ipstr
}
}
ips := strings.Split(clientIp, ",")
res := strings.TrimSpace(ips[0])
//res := ""
//for i := range ips {
// ip := strings.TrimSpace(ips[i])
// ipstr := fmt.Sprintf("%s: %s", ip, "")
// if i != len(ips)-1 {
// res += ipstr + " -> "
// } else {
// res += ipstr
// }
//}
return res
}
func GetIPFromRequest(req *http.Request) string {
clientIP := req.Header.Get("x-forwarded-for")
if clientIP == "" {
func GetClientIpFromRequest(req *http.Request) string {
clientIp := req.Header.Get("x-forwarded-for")
if clientIp == "" {
ipPort := strings.Split(req.RemoteAddr, ":")
if len(ipPort) >= 1 && len(ipPort) <= 2 {
clientIP = ipPort[0]
clientIp = ipPort[0]
} else if len(ipPort) > 2 {
idx := strings.LastIndex(req.RemoteAddr, ":")
clientIP = req.RemoteAddr[0:idx]
clientIP = strings.TrimLeft(clientIP, "[")
clientIP = strings.TrimRight(clientIP, "]")
clientIp = req.RemoteAddr[0:idx]
clientIp = strings.TrimLeft(clientIp, "[")
clientIp = strings.TrimRight(clientIp, "]")
}
}
return GetIPInfo(clientIP)
return getIpInfo(clientIp)
}
func LogInfo(ctx *context.Context, f string, v ...interface{}) {
ipString := fmt.Sprintf("(%s) ", GetIPFromRequest(ctx.Request))
ipString := fmt.Sprintf("(%s) ", GetClientIpFromRequest(ctx.Request))
logs.Info(ipString+f, v...)
}
func LogWarning(ctx *context.Context, f string, v ...interface{}) {
ipString := fmt.Sprintf("(%s) ", GetIPFromRequest(ctx.Request))
ipString := fmt.Sprintf("(%s) ", GetClientIpFromRequest(ctx.Request))
logs.Warning(ipString+f, v...)
}

View File

@ -17,6 +17,7 @@ package util
import (
"fmt"
"net/mail"
"net/url"
"regexp"
"strings"
@ -100,3 +101,21 @@ func GetCountryCode(prefix string, phone string) (string, error) {
func FilterField(field string) bool {
return ReFieldWhiteList.MatchString(field)
}
func IsValidOrigin(origin string) (bool, error) {
urlObj, err := url.Parse(origin)
if err != nil {
return false, err
}
if urlObj == nil {
return false, nil
}
originHostOnly := ""
if urlObj.Host != "" {
originHostOnly = fmt.Sprintf("%s://%s", urlObj.Scheme, urlObj.Hostname())
}
res := originHostOnly == "http://localhost" || originHostOnly == "https://localhost" || originHostOnly == "http://127.0.0.1" || originHostOnly == "http://casdoor-app" || strings.HasSuffix(originHostOnly, ".chromiumapp.org")
return res, nil
}

View File

@ -46,12 +46,18 @@ require("codemirror/mode/css/css");
const {Option} = Select;
const template = `<style>
.login-panel{
.login-panel {
padding: 40px 70px 0 70px;
border-radius: 10px;
background-color: #ffffff;
box-shadow: 0 0 30px 20px rgba(0, 0, 0, 0.20);
}
}
.login-panel-dark {
padding: 40px 70px 0 70px;
border-radius: 10px;
background-color: #333333;
box-shadow: 0 0 30px 20px rgba(255, 255, 255, 0.20);
}
</style>`;
const previewGrid = Setting.isMobile() ? 22 : 11;
@ -592,6 +598,16 @@ class ApplicationEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:IP whitelist"), i18next.t("general:IP whitelist - Tooltip"))} :
</Col>
<Col span={22} >
<Input placeholder = {this.state.application.organizationObj?.ipWhitelist} value={this.state.application.ipWhiteList} onChange={e => {
this.updateApplicationField("ipWhitelist", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("signup:Terms of Use"), i18next.t("signup:Terms of Use - Tooltip"))} :

View File

@ -34,6 +34,7 @@ import PaymentResultPage from "./PaymentResultPage";
import QrCodePage from "./QrCodePage";
import CaptchaPage from "./CaptchaPage";
import CustomHead from "./basic/CustomHead";
import * as Util from "./auth/Util";
class EntryPage extends React.Component {
constructor(props) {
@ -94,6 +95,14 @@ class EntryPage extends React.Component {
});
};
if (this.state.application?.ipRestriction) {
return Util.renderMessageLarge(this, this.state.application.ipRestriction);
}
if (this.state.application?.organizationObj?.ipRestriction) {
return Util.renderMessageLarge(this, this.state.application.organizationObj.ipRestriction);
}
const isDarkMode = this.props.themeAlgorithm.includes("dark");
return (

View File

@ -452,6 +452,16 @@ class OrganizationEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:IP whitelist"), i18next.t("general:IP whitelist - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.organization.ipWhitelist} onChange={e => {
this.updateOrganizationField("ipWhitelist", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("organization:Init score"), i18next.t("organization:Init score - Tooltip"))} :

View File

@ -1557,3 +1557,7 @@ export function getCurrencyText(product) {
return "(Unknown currency)";
}
}
export function isDarkTheme(themeAlgorithm) {
return themeAlgorithm && themeAlgorithm.includes("dark");
}

View File

@ -1070,6 +1070,19 @@ class UserEditPage extends React.Component {
</Col>
</Row>
);
} else if (accountItem.name === "IP whitelist") {
return (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:IP whitelist"), i18next.t("general:IP whitelist - Tooltip"))} :
</Col>
<Col span={22}>
<Input value={this.state.user.ipWhitelist} onChange={e => {
this.updateUserField("ipWhitelist", e.target.value);
}} />
</Col>
</Row>
);
}
}

View File

@ -52,7 +52,6 @@ class LoginPage extends React.Component {
username: null,
validEmailOrPhone: false,
validEmail: false,
enableCaptchaModal: CaptchaRule.Never,
openCaptchaModal: false,
openFaceRecognitionModal: false,
verifyCaptcha: undefined,
@ -93,17 +92,6 @@ class LoginPage extends React.Component {
}
if (prevProps.application !== this.props.application) {
this.setState({loginMethod: this.getDefaultLoginMethod(this.props.application)});
const captchaProviderItems = this.getCaptchaProviderItems(this.props.application);
if (captchaProviderItems) {
if (captchaProviderItems.some(providerItem => providerItem.rule === "Always")) {
this.setState({enableCaptchaModal: CaptchaRule.Always});
} else if (captchaProviderItems.some(providerItem => providerItem.rule === "Dynamic")) {
this.setState({enableCaptchaModal: CaptchaRule.Dynamic});
} else {
this.setState({enableCaptchaModal: CaptchaRule.Never});
}
}
}
if (prevProps.account !== this.props.account && this.props.account !== undefined) {
@ -133,6 +121,19 @@ class LoginPage extends React.Component {
}
}
getCaptchaRule(application) {
const captchaProviderItems = this.getCaptchaProviderItems(application);
if (captchaProviderItems) {
if (captchaProviderItems.some(providerItem => providerItem.rule === "Always")) {
return CaptchaRule.Always;
} else if (captchaProviderItems.some(providerItem => providerItem.rule === "Dynamic")) {
return CaptchaRule.Dynamic;
} else {
return CaptchaRule.Never;
}
}
}
checkCaptchaStatus(values) {
AuthBackend.getCaptchaStatus(values)
.then((res) => {
@ -226,6 +227,22 @@ class LoginPage extends React.Component {
return "password";
}
getCurrentLoginMethod() {
if (this.state.loginMethod === "password") {
return "Password";
} else if (this.state.loginMethod?.includes("verificationCode")) {
return "Verification code";
} else if (this.state.loginMethod === "webAuthn") {
return "WebAuthn";
} else if (this.state.loginMethod === "ldap") {
return "LDAP";
} else if (this.state.loginMethod === "faceId") {
return "Face ID";
} else {
return "Password";
}
}
getPlaceholder() {
switch (this.state.loginMethod) {
case "verificationCode": return i18next.t("login:Email or phone");
@ -261,17 +278,7 @@ 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";
} else if (this.state.loginMethod === "faceId") {
values["signinMethod"] = "Face ID";
}
values["signinMethod"] = this.getCurrentLoginMethod();
const oAuthParams = Util.getOAuthGetParameters();
values["type"] = oAuthParams?.responseType ?? this.state.type;
@ -388,13 +395,14 @@ class LoginPage extends React.Component {
} else {
values["password"] = passwordCipher;
}
if (this.state.enableCaptchaModal === CaptchaRule.Always) {
const captchaRule = this.getCaptchaRule(this.getApplicationObj());
if (captchaRule === CaptchaRule.Always) {
this.setState({
openCaptchaModal: true,
values: values,
});
return;
} else if (this.state.enableCaptchaModal === CaptchaRule.Dynamic) {
} else if (captchaRule === CaptchaRule.Dynamic) {
this.checkCaptchaStatus(values);
return;
}
@ -407,6 +415,7 @@ class LoginPage extends React.Component {
if (this.state.type === "cas") {
// CAS
const casParams = Util.getCasParameters();
values["signinMethod"] = this.getCurrentLoginMethod();
values["type"] = this.state.type;
AuthBackend.loginCas(values, casParams).then((res) => {
const loginHandler = (res) => {
@ -911,7 +920,7 @@ class LoginPage extends React.Component {
}
renderCaptchaModal(application) {
if (this.state.enableCaptchaModal === CaptchaRule.Never) {
if (this.getCaptchaRule(this.getApplicationObj()) === CaptchaRule.Never) {
return null;
}
const captchaProviderItems = this.getCaptchaProviderItems(application);
@ -1291,7 +1300,7 @@ class LoginPage extends React.Component {
<div className="login-content" style={{margin: this.props.preview ?? this.parseOffset(application.formOffset)}}>
{Setting.inIframe() || Setting.isMobile() ? null : <div dangerouslySetInnerHTML={{__html: application.formCss}} />}
{Setting.inIframe() || !Setting.isMobile() ? null : <div dangerouslySetInnerHTML={{__html: application.formCssMobile}} />}
<div className="login-panel">
<div className={Setting.isDarkTheme(this.props.themeAlgorithm) ? "login-panel-dark" : "login-panel"}>
<div className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}>
<div dangerouslySetInnerHTML={{__html: application.formSideHtml}} />
</div>

View File

@ -14,6 +14,7 @@
import CryptoJS from "crypto-js";
import i18next from "i18next";
import {Buffer} from "buffer";
export function getRandomKeyForObfuscator(obfuscatorType) {
if (obfuscatorType === "DES") {

View File

@ -842,7 +842,7 @@ class SignupPage extends React.Component {
<div className="login-content" style={{margin: this.props.preview ?? this.parseOffset(application.formOffset)}}>
{Setting.inIframe() || Setting.isMobile() ? null : <div dangerouslySetInnerHTML={{__html: application.formCss}} />}
{Setting.inIframe() || !Setting.isMobile() ? null : <div dangerouslySetInnerHTML={{__html: application.formCssMobile}} />}
<div className="login-panel" >
<div className={Setting.isDarkTheme(this.props.themeAlgorithm) ? "login-panel-dark" : "login-panel"}>
<div className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}>
<div dangerouslySetInnerHTML={{__html: application.formSideHtml}} />
</div>

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import React, {useState} from "react";
import React, {Fragment, useState} from "react";
import i18next from "i18next";
import {Button, Input} from "antd";
import * as AuthBackend from "../AuthBackend";
@ -67,24 +67,32 @@ export function MfaAuthVerifyForm({formValues, authParams, mfaProps, application
if (mfaType !== RecoveryMfaType) {
return (
<div style={{width: 300, height: 350}}>
<div style={{width: 320, height: 350}}>
<div style={{marginBottom: 24, textAlign: "center", fontSize: "24px"}}>
{i18next.t("mfa:Multi-factor authentication")}
</div>
<div style={{marginBottom: 24}}>
{i18next.t("mfa:You have enabled multi-factor authentication, please enter the authentication code")}
</div>
{mfaType === SmsMfaType || mfaType === EmailMfaType ? (
<MfaVerifySmsForm
mfaProps={mfaProps}
method={mfaAuth}
onFinish={verify}
application={application}
/>) : (
<MfaVerifyTotpForm
mfaProps={mfaProps}
onFinish={verify}
/>
<Fragment>
<div style={{marginBottom: 24}}>
{i18next.t("mfa:You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue")}
</div>
<MfaVerifySmsForm
mfaProps={mfaProps}
method={mfaAuth}
onFinish={verify}
application={application}
/>
</Fragment>
) : (
<Fragment>
<div style={{marginBottom: 24}}>
{i18next.t("mfa:You have enabled Multi-Factor Authentication, please enter the TOTP code")}
</div>
<MfaVerifyTotpForm
mfaProps={mfaProps}
onFinish={verify}
/>
</Fragment>
)}
<span style={{float: "right"}}>
{i18next.t("mfa:Have problems?")}

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Ověření selhalo",
"Verify Code": "Ověřit kód",
"Verify Password": "Ověřit heslo",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Váš email je",
"Your phone is": "Váš telefon je",
"preferred": "preferované"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Échec de la vérification",
"Verify Code": "Vérifier le code",
"Verify Password": "Confirmez le mot de passe",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Votre e-mail est",
"Your phone is": "Votre téléphone est",
"preferred": "préféré"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Проверка не удалась",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Ваш email",
"Your phone is": "Ваш телефон",
"preferred": "preferred"
@ -971,7 +972,7 @@
"Please input your affiliation!": "Пожалуйста, укажите свою принадлежность!",
"Please input your display name!": "Пожалуйста, введите своё отображаемое имя!",
"Please input your first name!": "Пожалуйста, введите свое имя!",
"Please input your invitation code!": "Please input your invitation code!",
"Please input your invitation code!": "Пожалуйста, введите код приглашения!",
"Please input your last name!": "Введите свою фамилию!",
"Please input your phone number!": "Пожалуйста, введите свой номер телефона!",
"Please input your real name!": "Пожалуйста, введите своё настоящее имя!",
@ -1162,9 +1163,9 @@
"MFA accounts": "MFA accounts",
"Managed accounts": "Управляемые аккаунты",
"Modify password...": "Изменить пароль...",
"Multi-factor authentication": "Multi-factor authentication",
"Need update password": "Need update password",
"Need update password - Tooltip": "Force user update password after login",
"Multi-factor authentication": "Многофакторная аутентификация",
"Need update password": "Необходимо обновить пароль",
"Need update password - Tooltip": "Заставить пользователя обновить пароль после входа в систему",
"New Email": "Новое электронное письмо",
"New Password": "Новый пароль",
"New User": "Новый пользователь",
@ -1188,26 +1189,26 @@
"Set password...": "Установить пароль...",
"Tag": "Метка",
"Tag - Tooltip": "Тег пользователя",
"The password must contain at least one special character": "The password must contain at least one special character",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "The password must contain at least one uppercase letter, one lowercase letter and one digit",
"The password must have at least 6 characters": "The password must have at least 6 characters",
"The password must have at least 8 characters": "The password must have at least 8 characters",
"The password must not contain any repeated characters": "The password must not contain any repeated characters",
"This field value doesn't match the pattern rule": "This field value doesn't match the pattern rule",
"The password must contain at least one special character": "Пароль должен содержать хотя бы один специальный символ",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "Пароль должен содержать как минимум одну заглавную букву, одну строчную букву и одну цифру",
"The password must have at least 6 characters": "Пароль должен быть минимум 6 символов",
"The password must have at least 8 characters": "Пароль должен быть минимум 8 символов",
"The password must not contain any repeated characters": "Пароль не должен содержать повторяющиеся символы",
"This field value doesn't match the pattern rule": "Значение поля не соответствует шаблону",
"Title": "Заголовок",
"Title - Tooltip": "Положение в аффилиации",
"Two passwords you typed do not match.": "Два введенных вами пароля не совпадают.",
"Unlink": "Отсоединить",
"Upload (.xlsx)": "Загрузить (.xlsx)",
"Upload ID card back picture": "Upload ID card back picture",
"Upload ID card front picture": "Upload ID card front picture",
"Upload ID card with person picture": "Upload ID card with person picture",
"Upload ID card back picture": "Загрузите заднюю сторону удостоверения личности",
"Upload ID card front picture": "Загрузите переднюю сторону удостоверения личности",
"Upload ID card with person picture": "Загрузите удостоверение личности с фотографией",
"Upload a photo": "Загрузить фото",
"User Profile": "User Profile",
"User Profile": "Профиль пользователя",
"Values": "Значения",
"Verification code sent": "Код подтверждения отправлен",
"WebAuthn credentials": "WebAuthn удостоверения",
"You have changed the username, please save your change first before modifying the password": "You have changed the username, please save your change first before modifying the password",
"You have changed the username, please save your change first before modifying the password": "Имя было изменено, сохраните изменения перед сменой пароля",
"input password": "введите пароль"
},
"verification": {

View File

@ -562,7 +562,8 @@
"Verification failed": "Overenie zlyhalo",
"Verify Code": "Overiť kód",
"Verify Password": "Overiť heslo",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Váš email je",
"Your phone is": "Váš telefón je",
"preferred": "preferované"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "Doğrulama başarısız",
"Verify Code": "Kodu doğrula",
"Verify Password": "Parolayı Doğrula",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "E-postanız",
"Your phone is": "Telefon numaranız",
"preferred": "tercih edilen"

View File

@ -562,7 +562,8 @@
"Verification failed": "Не вдалося перевірити",
"Verify Code": "Підтвердити код",
"Verify Password": "Підтвердіть пароль",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Ваша електронна адреса",
"Your phone is": "Ваш телефон",
"preferred": "бажаний"

View File

@ -562,7 +562,8 @@
"Verification failed": "Verification failed",
"Verify Code": "Verify Code",
"Verify Password": "Verify Password",
"You have enabled multi-factor authentication, please enter the authentication code": "You have enabled multi-factor authentication, please enter the authentication code",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "You have enabled Multi-Factor Authentication, please enter the TOTP code",
"Your email is": "Your email is",
"Your phone is": "Your phone is",
"preferred": "preferred"

View File

@ -562,7 +562,8 @@
"Verification failed": "验证失败",
"Verify Code": "验证码",
"Verify Password": "验证密码",
"You have enabled multi-factor authentication, please enter the authentication code": "您已经启用多因素认证,请输入认证码",
"You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue": "您已经启用多因素认证, 请点击 '发送验证码' 继续",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "您已经启用多因素认证请输入TOTP认证码",
"Your email is": "你的电子邮件",
"Your phone is": "你的手机号",
"preferred": "首选"

View File

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