Compare commits

..

9 Commits

Author SHA1 Message Date
liuaiolos
6f1f93725e feat: fix GetAllActions()'s bug (#3289) 2024-10-16 21:55:06 +08:00
DacongDA
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
Yang Luo
dde936e935 feat: fix null application crash in CheckEntryIp() 2024-10-15 22:11:15 +08:00
Yang Luo
fb561a98c8 feat: fix null user crash in RefreshToken() 2024-10-15 21:38:33 +08:00
ZhaoYP 2001
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
Yang Luo
a3f8ded10c feat: refactor util.GetClientIpFromRequest() 2024-10-15 12:22:38 +08:00
DacongDA
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
ZhaoYP 2001
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
51 changed files with 366 additions and 90 deletions

View File

@@ -116,6 +116,13 @@ func (c *ApiController) Signup() {
return 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()) msg := object.CheckUserSignup(application, organization, &authForm, c.GetAcceptLanguage())
if msg != "" { if msg != "" {
c.ResponseError(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)) c.ResponseOk(object.GetMaskedApplication(application, userId))
} }
@@ -229,6 +232,11 @@ func (c *ApiController) UpdateApplication() {
return 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.Data["json"] = wrapActionResponse(object.UpdateApplication(id, &application))
c.ServeJSON() c.ServeJSON()
} }
@@ -259,6 +267,11 @@ func (c *ApiController) AddApplication() {
return return
} }
if err = object.CheckIpWhitelist(application.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddApplication(&application)) c.Data["json"] = wrapActionResponse(object.AddApplication(&application))
c.ServeJSON() 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) { func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *form.AuthForm) (resp *Response) {
userId := user.GetId() 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) allowed, err := object.CheckLoginPermission(userId, application)
if err != nil { if err != nil {
c.ResponseError(err.Error(), 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, "") application = object.GetMaskedApplication(application, "")
if msg != "" { if msg != "" {
c.ResponseError(msg, application) c.ResponseError(msg, application)

View File

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

View File

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

View File

@@ -370,6 +370,11 @@ func (c *ApiController) AddUser() {
return return
} }
if err = object.CheckIpWhitelist(user.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddUser(&user)) c.Data["json"] = wrapActionResponse(object.AddUser(&user))
c.ServeJSON() c.ServeJSON()
} }
@@ -535,6 +540,23 @@ func (c *ApiController) SetPassword() {
return 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.Password = newPassword
targetUser.UpdateUserPassword(organization) targetUser.UpdateUserPassword(organization)
targetUser.NeedUpdatePassword = false targetUser.NeedUpdatePassword = false

View File

@@ -132,7 +132,8 @@ func (c *ApiController) SendVerificationCode() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
if msg := vform.CheckParameter(form.SendVerifyCode, c.GetAcceptLanguage()); msg != "" { if msg := vform.CheckParameter(form.SendVerifyCode, c.GetAcceptLanguage()); msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
@@ -259,7 +260,7 @@ func (c *ApiController) SendVerificationCode() {
return return
} }
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, vform.Dest) sendResp = object.SendVerificationCodeToEmail(organization, user, provider, clientIp, vform.Dest)
case object.VerifyTypePhone: case object.VerifyTypePhone:
if vform.Method == LoginVerification || vform.Method == ForgetVerification { if vform.Method == LoginVerification || vform.Method == ForgetVerification {
if user != nil && util.GetMaskedPhone(user.Phone) == vform.Dest { 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)) c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), vform.CountryCode))
return return
} else { } else {
sendResp = object.SendVerificationCodeToPhone(organization, user, provider, remoteAddr, phone) sendResp = object.SendVerificationCodeToPhone(organization, user, provider, clientIp, phone)
} }
} }

View File

@@ -95,6 +95,7 @@ type Application struct {
Tags []string `xorm:"mediumtext" json:"tags"` Tags []string `xorm:"mediumtext" json:"tags"`
SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"` SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
IsShared bool `json:"isShared"` IsShared bool `json:"isShared"`
IpRestriction string `json:"ipRestriction"`
ClientId string `xorm:"varchar(100)" json:"clientId"` ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"` ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
@@ -108,6 +109,7 @@ type Application struct {
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"` SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"` ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"` AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"`
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"` TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"`
SignupHtml string `xorm:"mediumtext" json:"signupHtml"` SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
SigninHtml string `xorm:"mediumtext" json:"signinHtml"` SigninHtml string `xorm:"mediumtext" json:"signinHtml"`

View File

@@ -539,6 +539,11 @@ func CheckUpdateUser(oldUser, user *User, lang string) string {
return i18n.Translate(lang, "check:Phone already exists") 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 "" 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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -557,6 +557,14 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
itemsChanged = append(itemsChanged, item) 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 { if oldUser.Balance != newUser.Balance {
item := GetAccountItemByName("Balance", organization) item := GetAccountItemByName("Balance", organization)

View File

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

View File

@@ -46,12 +46,18 @@ require("codemirror/mode/css/css");
const {Option} = Select; const {Option} = Select;
const template = `<style> const template = `<style>
.login-panel{ .login-panel {
padding: 40px 70px 0 70px; padding: 40px 70px 0 70px;
border-radius: 10px; border-radius: 10px;
background-color: #ffffff; background-color: #ffffff;
box-shadow: 0 0 30px 20px rgba(0, 0, 0, 0.20); 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>`; </style>`;
const previewGrid = Setting.isMobile() ? 22 : 11; const previewGrid = Setting.isMobile() ? 22 : 11;
@@ -592,6 +598,16 @@ class ApplicationEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("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"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("signup:Terms of Use"), i18next.t("signup:Terms of Use - Tooltip"))} : {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 QrCodePage from "./QrCodePage";
import CaptchaPage from "./CaptchaPage"; import CaptchaPage from "./CaptchaPage";
import CustomHead from "./basic/CustomHead"; import CustomHead from "./basic/CustomHead";
import * as Util from "./auth/Util";
class EntryPage extends React.Component { class EntryPage extends React.Component {
constructor(props) { 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"); const isDarkMode = this.props.themeAlgorithm.includes("dark");
return ( return (

View File

@@ -452,6 +452,16 @@ class OrganizationEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </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"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("organization:Init score"), i18next.t("organization:Init score - Tooltip"))} : {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)"; return "(Unknown currency)";
} }
} }
export function isDarkTheme(themeAlgorithm) {
return themeAlgorithm && themeAlgorithm.includes("dark");
}

View File

@@ -1070,6 +1070,19 @@ class UserEditPage extends React.Component {
</Col> </Col>
</Row> </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, username: null,
validEmailOrPhone: false, validEmailOrPhone: false,
validEmail: false, validEmail: false,
enableCaptchaModal: CaptchaRule.Never,
openCaptchaModal: false, openCaptchaModal: false,
openFaceRecognitionModal: false, openFaceRecognitionModal: false,
verifyCaptcha: undefined, verifyCaptcha: undefined,
@@ -93,17 +92,6 @@ class LoginPage extends React.Component {
} }
if (prevProps.application !== this.props.application) { if (prevProps.application !== this.props.application) {
this.setState({loginMethod: this.getDefaultLoginMethod(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) { 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) { checkCaptchaStatus(values) {
AuthBackend.getCaptchaStatus(values) AuthBackend.getCaptchaStatus(values)
.then((res) => { .then((res) => {
@@ -388,13 +389,14 @@ class LoginPage extends React.Component {
} else { } else {
values["password"] = passwordCipher; values["password"] = passwordCipher;
} }
if (this.state.enableCaptchaModal === CaptchaRule.Always) { const captchaRule = this.getCaptchaRule(this.getApplicationObj());
if (captchaRule === CaptchaRule.Always) {
this.setState({ this.setState({
openCaptchaModal: true, openCaptchaModal: true,
values: values, values: values,
}); });
return; return;
} else if (this.state.enableCaptchaModal === CaptchaRule.Dynamic) { } else if (captchaRule === CaptchaRule.Dynamic) {
this.checkCaptchaStatus(values); this.checkCaptchaStatus(values);
return; return;
} }
@@ -911,7 +913,7 @@ class LoginPage extends React.Component {
} }
renderCaptchaModal(application) { renderCaptchaModal(application) {
if (this.state.enableCaptchaModal === CaptchaRule.Never) { if (this.getCaptchaRule(this.getApplicationObj()) === CaptchaRule.Never) {
return null; return null;
} }
const captchaProviderItems = this.getCaptchaProviderItems(application); const captchaProviderItems = this.getCaptchaProviderItems(application);
@@ -1291,7 +1293,7 @@ class LoginPage extends React.Component {
<div className="login-content" style={{margin: this.props.preview ?? this.parseOffset(application.formOffset)}}> <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.formCss}} />}
{Setting.inIframe() || !Setting.isMobile() ? null : <div dangerouslySetInnerHTML={{__html: application.formCssMobile}} />} {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 className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}>
<div dangerouslySetInnerHTML={{__html: application.formSideHtml}} /> <div dangerouslySetInnerHTML={{__html: application.formSideHtml}} />
</div> </div>

View File

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

View File

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

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Ověření selhalo", "Verification failed": "Ověření selhalo",
"Verify Code": "Ověřit kód", "Verify Code": "Ověřit kód",
"Verify Password": "Ověřit heslo", "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 email is": "Váš email je",
"Your phone is": "Váš telefon je", "Your phone is": "Váš telefon je",
"preferred": "preferované" "preferred": "preferované"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Échec de la vérification", "Verification failed": "Échec de la vérification",
"Verify Code": "Vérifier le code", "Verify Code": "Vérifier le code",
"Verify Password": "Confirmez le mot de passe", "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 email is": "Votre e-mail est",
"Your phone is": "Votre téléphone est", "Your phone is": "Votre téléphone est",
"preferred": "préféré" "preferred": "préféré"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Проверка не удалась", "Verification failed": "Проверка не удалась",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Ваш email",
"Your phone is": "Ваш телефон", "Your phone is": "Ваш телефон",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Overenie zlyhalo", "Verification failed": "Overenie zlyhalo",
"Verify Code": "Overiť kód", "Verify Code": "Overiť kód",
"Verify Password": "Overiť heslo", "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 email is": "Váš email je",
"Your phone is": "Váš telefón je", "Your phone is": "Váš telefón je",
"preferred": "preferované" "preferred": "preferované"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Verification failed", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "preferred": "preferred"

View File

@@ -562,7 +562,8 @@
"Verification failed": "Doğrulama başarısız", "Verification failed": "Doğrulama başarısız",
"Verify Code": "Kodu doğrula", "Verify Code": "Kodu doğrula",
"Verify Password": "Parolayı 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 email is": "E-postanız",
"Your phone is": "Telefon numaranız", "Your phone is": "Telefon numaranız",
"preferred": "tercih edilen" "preferred": "tercih edilen"

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", "Verification failed": "Verification failed",
"Verify Code": "Verify Code", "Verify Code": "Verify Code",
"Verify Password": "Verify Password", "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 email is": "Your email is",
"Your phone is": "Your phone is", "Your phone is": "Your phone is",
"preferred": "preferred" "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 click 'Send Code' to continue": "您已经启用多因素认证, 请点击 '发送验证码' 继续",
"You have enabled Multi-Factor Authentication, please enter the TOTP code": "您已经启用多因素认证请输入TOTP认证码",
"Your email is": "你的电子邮件", "Your email is": "你的电子邮件",
"Your phone is": "你的手机号", "Your phone is": "你的手机号",
"preferred": "首选" "preferred": "首选"

View File

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