diff --git a/authz/authz.go b/authz/authz.go index e6af5ce7c69a..71a6e92b4ecf 100644 --- a/authz/authz.go +++ b/authz/authz.go @@ -90,6 +90,7 @@ p, *, *, GET, /api/userinfo, *, * p, *, *, GET, /api/user, *, * p, *, *, POST, /api/webhook, *, * p, *, *, GET, /api/get-webhook-event, *, * +p, *, *, GET, /api/get-captcha-status, *, * p, *, *, *, /api/login/oauth, *, * p, *, *, GET, /api/get-application, *, * p, *, *, GET, /api/get-organization-applications, *, * diff --git a/controllers/auth.go b/controllers/auth.go index b9d2b942280c..07591b150584 100644 --- a/controllers/auth.go +++ b/controllers/auth.go @@ -281,8 +281,8 @@ func (c *ApiController) Login() { c.ResponseError(c.T("auth:The login method: login with password is not enabled for the application")) return } - - if object.CheckToEnableCaptcha(application) { + var enableCaptcha bool + if enableCaptcha = object.CheckToEnableCaptcha(application, form.Organization, form.Username); enableCaptcha { isHuman, err := captcha.VerifyCaptchaByCaptchaType(form.CaptchaType, form.CaptchaToken, form.ClientSecret) if err != nil { c.ResponseError(err.Error()) @@ -296,7 +296,8 @@ func (c *ApiController) Login() { } password := form.Password - user, msg = object.CheckUserPassword(form.Organization, form.Username, password, c.GetAcceptLanguage()) + user, msg = object.CheckUserPassword(form.Organization, form.Username, password, c.GetAcceptLanguage(), enableCaptcha) + } if msg != "" { @@ -610,3 +611,21 @@ func (c *ApiController) GetWebhookEventType() { wechatScanType = "" c.ServeJSON() } + +// GetCaptchaStatus +// @Title GetCaptchaStatus +// @Tag Token API +// @Description Get Login Error Counts +// @Param id query string true "The id ( owner/name ) of user" +// @Success 200 {object} controllers.Response The Response object +// @router /api/get-captcha-status [get] +func (c *ApiController) GetCaptchaStatus() { + organization := c.Input().Get("organization") + userId := c.Input().Get("user_id") + user := object.GetUserByFields(organization, userId) + var captchaEnabled bool + if user != nil && user.SigninWrongTimes >= object.SigninWrongTimesLimit { + captchaEnabled = true + } + c.ResponseOk(captchaEnabled) +} diff --git a/i18n/locales/de/data.json b/i18n/locales/de/data.json index bdd7316f6d67..52c141564e65 100644 --- a/i18n/locales/de/data.json +++ b/i18n/locales/de/data.json @@ -52,6 +52,7 @@ "Username must have at least 2 characters": "Benutzername muss mindestens 2 Zeichen lang sein", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Sie haben zu oft das falsche Passwort oder den falschen Code eingegeben. Bitte warten Sie %d Minuten und versuchen Sie es erneut", "Your region is not allow to signup by phone": "Ihre Region ist nicht berechtigt, sich telefonisch anzumelden", + "password or code is incorrect": "password or code is incorrect", "password or code is incorrect, you have %d remaining chances": "Das Passwort oder der Code ist falsch. Du hast noch %d Versuche übrig", "unsupported password type: %s": "Nicht unterstützter Passworttyp: %s" }, diff --git a/i18n/locales/en/data.json b/i18n/locales/en/data.json index e63c6c5e7424..a3589661b059 100644 --- a/i18n/locales/en/data.json +++ b/i18n/locales/en/data.json @@ -52,6 +52,7 @@ "Username must have at least 2 characters": "Username must have at least 2 characters", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again", "Your region is not allow to signup by phone": "Your region is not allow to signup by phone", + "password or code is incorrect": "password or code is incorrect", "password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances", "unsupported password type: %s": "unsupported password type: %s" }, diff --git a/i18n/locales/es/data.json b/i18n/locales/es/data.json index d067cb8f1ff2..573331b7fd34 100644 --- a/i18n/locales/es/data.json +++ b/i18n/locales/es/data.json @@ -52,6 +52,7 @@ "Username must have at least 2 characters": "Nombre de usuario debe tener al menos 2 caracteres", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Has ingresado la contraseña o código incorrecto demasiadas veces, por favor espera %d minutos e intenta de nuevo", "Your region is not allow to signup by phone": "Tu región no está permitida para registrarse por teléfono", + "password or code is incorrect": "password or code is incorrect", "password or code is incorrect, you have %d remaining chances": "Contraseña o código incorrecto, tienes %d intentos restantes", "unsupported password type: %s": "Tipo de contraseña no compatible: %s" }, diff --git a/i18n/locales/fr/data.json b/i18n/locales/fr/data.json index d95af79e9a01..f33a1582b2e1 100644 --- a/i18n/locales/fr/data.json +++ b/i18n/locales/fr/data.json @@ -52,6 +52,7 @@ "Username must have at least 2 characters": "Le nom d'utilisateur doit comporter au moins 2 caractères", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Vous avez entré le mauvais mot de passe ou code plusieurs fois, veuillez attendre %d minutes et réessayer", "Your region is not allow to signup by phone": "Votre région n'est pas autorisée à s'inscrire par téléphone", + "password or code is incorrect": "password or code is incorrect", "password or code is incorrect, you have %d remaining chances": "Le mot de passe ou le code est incorrect, il vous reste %d chances", "unsupported password type: %s": "Type de mot de passe non pris en charge : %s" }, diff --git a/i18n/locales/id/data.json b/i18n/locales/id/data.json index f27cbf5a2016..cf41b0bb260e 100644 --- a/i18n/locales/id/data.json +++ b/i18n/locales/id/data.json @@ -52,6 +52,7 @@ "Username must have at least 2 characters": "Nama pengguna harus memiliki setidaknya 2 karakter", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Anda telah memasukkan kata sandi atau kode yang salah terlalu banyak kali, mohon tunggu selama %d menit dan coba lagi", "Your region is not allow to signup by phone": "Wilayah Anda tidak diizinkan untuk mendaftar melalui telepon", + "password or code is incorrect": "password or code is incorrect", "password or code is incorrect, you have %d remaining chances": "Kata sandi atau kode salah, Anda memiliki %d kesempatan tersisa", "unsupported password type: %s": "jenis sandi tidak didukung: %s" }, diff --git a/i18n/locales/ja/data.json b/i18n/locales/ja/data.json index 74ae61bc3d8f..6306daabb5ba 100644 --- a/i18n/locales/ja/data.json +++ b/i18n/locales/ja/data.json @@ -52,6 +52,7 @@ "Username must have at least 2 characters": "ユーザー名は少なくとも2文字必要です", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "あなたは間違ったパスワードまたはコードを何度も入力しました。%d 分間待ってから再度お試しください", "Your region is not allow to signup by phone": "あなたの地域は電話でサインアップすることができません", + "password or code is incorrect": "password or code is incorrect", "password or code is incorrect, you have %d remaining chances": "パスワードまたはコードが間違っています。あと%d回の試行機会があります", "unsupported password type: %s": "サポートされていないパスワードタイプ:%s" }, diff --git a/i18n/locales/ko/data.json b/i18n/locales/ko/data.json index f473a1169699..c9fb1862d9f3 100644 --- a/i18n/locales/ko/data.json +++ b/i18n/locales/ko/data.json @@ -52,6 +52,7 @@ "Username must have at least 2 characters": "사용자 이름은 적어도 2개의 문자가 있어야 합니다", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "올바르지 않은 비밀번호나 코드를 여러 번 입력했습니다. %d분 동안 기다리신 후 다시 시도해주세요", "Your region is not allow to signup by phone": "당신의 지역은 전화로 가입할 수 없습니다", + "password or code is incorrect": "password or code is incorrect", "password or code is incorrect, you have %d remaining chances": "암호 또는 코드가 올바르지 않습니다. %d번의 기회가 남아 있습니다", "unsupported password type: %s": "지원되지 않는 암호 유형: %s" }, diff --git a/i18n/locales/ru/data.json b/i18n/locales/ru/data.json index d0cfa8efcea3..eccabaedb716 100644 --- a/i18n/locales/ru/data.json +++ b/i18n/locales/ru/data.json @@ -52,6 +52,7 @@ "Username must have at least 2 characters": "Имя пользователя должно содержать не менее 2 символов", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Вы ввели неправильный пароль или код слишком много раз, пожалуйста, подождите %d минут и попробуйте снова", "Your region is not allow to signup by phone": "Ваш регион не разрешает регистрацию по телефону", + "password or code is incorrect": "password or code is incorrect", "password or code is incorrect, you have %d remaining chances": "Неправильный пароль или код, у вас осталось %d попыток", "unsupported password type: %s": "неподдерживаемый тип пароля: %s" }, diff --git a/i18n/locales/vi/data.json b/i18n/locales/vi/data.json index f83ccffde3e7..26ec1d0dee12 100644 --- a/i18n/locales/vi/data.json +++ b/i18n/locales/vi/data.json @@ -52,6 +52,7 @@ "Username must have at least 2 characters": "Tên đăng nhập phải có ít nhất 2 ký tự", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Bạn đã nhập sai mật khẩu hoặc mã quá nhiều lần, vui lòng đợi %d phút và thử lại", "Your region is not allow to signup by phone": "Vùng của bạn không được phép đăng ký bằng điện thoại", + "password or code is incorrect": "password or code is incorrect", "password or code is incorrect, you have %d remaining chances": "Mật khẩu hoặc mã không chính xác, bạn còn %d lần cơ hội", "unsupported password type: %s": "Loại mật khẩu không được hỗ trợ: %s" }, diff --git a/i18n/locales/zh/data.json b/i18n/locales/zh/data.json index 7f5332bca6ab..84e6b49d3d17 100644 --- a/i18n/locales/zh/data.json +++ b/i18n/locales/zh/data.json @@ -52,6 +52,7 @@ "Username must have at least 2 characters": "用户名至少要有2个字符", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "密码错误次数已达上限,请在 %d 分后重试", "Your region is not allow to signup by phone": "所在地区不支持手机号注册", + "password or code is incorrect": "密码错误", "password or code is incorrect, you have %d remaining chances": "密码错误,您还有 %d 次尝试的机会", "unsupported password type: %s": "不支持的密码类型: %s" }, diff --git a/object/check.go b/object/check.go index a4359d3c534d..4a121a91748e 100644 --- a/object/check.go +++ b/object/check.go @@ -157,10 +157,16 @@ func checkSigninErrorTimes(user *User, lang string) string { return "" } -func CheckPassword(user *User, password string, lang string) string { +func CheckPassword(user *User, password string, lang string, options ...bool) string { + enableCaptcha := false + if len(options) > 0 { + enableCaptcha = options[0] + } // check the login error times - if msg := checkSigninErrorTimes(user, lang); msg != "" { - return msg + if !enableCaptcha { + if msg := checkSigninErrorTimes(user, lang); msg != "" { + return msg + } } organization := GetOrganizationByUser(user) @@ -182,7 +188,7 @@ func CheckPassword(user *User, password string, lang string) string { return "" } - return recordSigninErrorInfo(user, lang) + return recordSigninErrorInfo(user, lang, enableCaptcha) } else { return fmt.Sprintf(i18n.Translate(lang, "check:unsupported password type: %s"), organization.PasswordType) } @@ -231,7 +237,11 @@ func checkLdapUserPassword(user *User, password string, lang string) string { return "" } -func CheckUserPassword(organization string, username string, password string, lang string) (*User, string) { +func CheckUserPassword(organization string, username string, password string, lang string, options ...bool) (*User, string) { + enableCaptcha := false + if len(options) > 0 { + enableCaptcha = options[0] + } user := GetUserByFields(organization, username) if user == nil || user.IsDeleted == true { return nil, fmt.Sprintf(i18n.Translate(lang, "general:The user: %s doesn't exist"), util.GetId(organization, username)) @@ -250,7 +260,7 @@ func CheckUserPassword(organization string, username string, password string, la return nil, msg } } else { - if msg := CheckPassword(user, password, lang); msg != "" { + if msg := CheckPassword(user, password, lang, enableCaptcha); msg != "" { return nil, msg } } @@ -380,7 +390,7 @@ func CheckUpdateUser(oldUser, user *User, lang string) string { return "" } -func CheckToEnableCaptcha(application *Application) bool { +func CheckToEnableCaptcha(application *Application, organization, username string) bool { if len(application.Providers) == 0 { return false } @@ -390,6 +400,10 @@ func CheckToEnableCaptcha(application *Application) bool { continue } if providerItem.Provider.Category == "Captcha" { + if providerItem.Rule == "Dynamic" { + user := GetUserByFields(organization, username) + return user != nil && user.SigninWrongTimes >= SigninWrongTimesLimit + } return providerItem.Rule == "Always" } } diff --git a/object/check_util.go b/object/check_util.go index 25637d7cafb9..85d7935edb2a 100644 --- a/object/check_util.go +++ b/object/check_util.go @@ -45,9 +45,15 @@ func resetUserSigninErrorTimes(user *User) { UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin) } -func recordSigninErrorInfo(user *User, lang string) string { +func recordSigninErrorInfo(user *User, lang string, options ...bool) string { + enableCaptcha := false + if len(options) > 0 { + enableCaptcha = options[0] + } // increase failed login count - user.SigninWrongTimes++ + if user.SigninWrongTimes < SigninWrongTimesLimit { + user.SigninWrongTimes++ + } if user.SigninWrongTimes >= SigninWrongTimesLimit { // record the latest failed login time @@ -57,10 +63,11 @@ func recordSigninErrorInfo(user *User, lang string) string { // update user UpdateUser(user.GetId(), user, []string{"signin_wrong_times", "last_signin_wrong_time"}, user.IsGlobalAdmin) leftChances := SigninWrongTimesLimit - user.SigninWrongTimes - if leftChances > 0 { + if leftChances == 0 && enableCaptcha { + return fmt.Sprint(i18n.Translate(lang, "check:password or code is incorrect")) + } else if leftChances >= 0 { return fmt.Sprintf(i18n.Translate(lang, "check:password or code is incorrect, you have %d remaining chances"), leftChances) } - // don't show the chance error message if the user has no chance left return fmt.Sprintf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), int(LastSignWrongTimeDuration.Minutes())) } diff --git a/routers/router.go b/routers/router.go index c66fad41a2ac..24eb160d9be1 100644 --- a/routers/router.go +++ b/routers/router.go @@ -57,6 +57,7 @@ func initAPI() { beego.Router("/api/saml/metadata", &controllers.ApiController{}, "GET:GetSamlMeta") beego.Router("/api/webhook", &controllers.ApiController{}, "POST:HandleOfficialAccountEvent") beego.Router("/api/get-webhook-event", &controllers.ApiController{}, "GET:GetWebhookEventType") + beego.Router("/api/get-captcha-status", &controllers.ApiController{}, "GET:GetCaptchaStatus") beego.Router("/api/get-organizations", &controllers.ApiController{}, "GET:GetOrganizations") beego.Router("/api/get-organization", &controllers.ApiController{}, "GET:GetOrganization") diff --git a/swagger/swagger.json b/swagger/swagger.json index 772b3b659fa5..435b06b6d28d 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -552,6 +552,14 @@ } }, "/api/api/get-webhook-event": { + "get": { + "tags": [ + "GetCaptchaStatus API" + ], + "operationId": "ApiController.GetCaptchaStatus" + } + }, + "/api/api/get-captcha-status": { "get": { "tags": [ "GetWebhookEventType API" diff --git a/swagger/swagger.yml b/swagger/swagger.yml index 06e1d5b9544d..6ab2847b6756 100644 --- a/swagger/swagger.yml +++ b/swagger/swagger.yml @@ -360,6 +360,11 @@ paths: tags: - GetWebhookEventType API operationId: ApiController.GetWebhookEventType + /api/api/get-captcha-status: + get: + tags: + - GetCaptchaStatus API + operationId: ApiController.GetCaptchaStatus /api/api/reset-email-or-phone: post: tags: diff --git a/web/src/auth/AuthBackend.js b/web/src/auth/AuthBackend.js index b856172a2986..3f6e260756ad 100644 --- a/web/src/auth/AuthBackend.js +++ b/web/src/auth/AuthBackend.js @@ -139,3 +139,13 @@ export function getWechatMessageEvent() { }, }).then(res => res.json()); } + +export function getCaptchaStatus(values) { + return fetch(`${Setting.ServerUrl}/api/get-captcha-status?organization=${values["organization"]}&user_id=${values["username"]}`, { + method: "GET", + credentials: "include", + headers: { + "Accept-Language": Setting.getAcceptLanguage(), + }, + }).then(res => res.json()); +} diff --git a/web/src/auth/LoginPage.js b/web/src/auth/LoginPage.js index 68f5b5c90081..0c27abe85450 100644 --- a/web/src/auth/LoginPage.js +++ b/web/src/auth/LoginPage.js @@ -31,6 +31,7 @@ import CustomGithubCorner from "../common/CustomGithubCorner"; import {SendCodeInput} from "../common/SendCodeInput"; import LanguageSelect from "../common/select/LanguageSelect"; import {CaptchaModal} from "../common/modal/CaptchaModal"; +import {CaptchaRule} from "../common/modal/CaptchaModal"; import RedirectForm from "../common/RedirectForm"; class LoginPage extends React.Component { @@ -47,7 +48,7 @@ class LoginPage extends React.Component { validEmailOrPhone: false, validEmail: false, loginMethod: "password", - enableCaptchaModal: false, + enableCaptchaModal: CaptchaRule.Never, openCaptchaModal: false, verifyCaptcha: undefined, samlResponse: "", @@ -81,7 +82,13 @@ class LoginPage extends React.Component { if (prevProps.application !== this.props.application) { const captchaProviderItems = this.getCaptchaProviderItems(this.props.application); if (captchaProviderItems) { - this.setState({enableCaptchaModal: captchaProviderItems.some(providerItem => providerItem.rule === "Always")}); + 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 (this.props.account && this.props.account.owner === this.props.application?.organization) { @@ -110,6 +117,22 @@ class LoginPage extends React.Component { } } + checkCaptchaStatus(values) { + AuthBackend.getCaptchaStatus(values) + .then((res) => { + if (res.status === "ok") { + if (res.data) { + this.setState({ + openCaptchaModal: true, + values: values, + }); + return null; + } + } + this.login(values); + }); + } + getApplicationLogin() { const oAuthParams = Util.getOAuthGetParameters(); AuthBackend.getApplicationLogin(oAuthParams) @@ -255,15 +278,19 @@ class LoginPage extends React.Component { this.signInWithWebAuthn(username, values); return; } - - if (this.state.loginMethod === "password" && this.state.enableCaptchaModal) { - this.setState({ - openCaptchaModal: true, - values: values, - }); - } else { - this.login(values); + if (this.state.loginMethod === "password") { + if (this.state.enableCaptchaModal === CaptchaRule.Always) { + this.setState({ + openCaptchaModal: true, + values: values, + }); + return; + } else if (this.state.enableCaptchaModal === CaptchaRule.Dynamic) { + this.checkCaptchaStatus(values); + return; + } } + this.login(values); } login(values) { @@ -544,13 +571,15 @@ class LoginPage extends React.Component { } renderCaptchaModal(application) { - if (!this.state.enableCaptchaModal) { + if (this.state.enableCaptchaModal === CaptchaRule.Never) { return null; } - - const provider = this.getCaptchaProviderItems(application) - .filter(providerItem => providerItem.rule === "Always") - .map(providerItem => providerItem.provider)[0]; + const captchaProviderItems = this.getCaptchaProviderItems(application); + const alwaysProviderItems = captchaProviderItems.filter(providerItem => providerItem.rule === "Always"); + const dynamicProviderItems = captchaProviderItems.filter(providerItem => providerItem.rule === "Dynamic"); + const provider = alwaysProviderItems.length > 0 + ? alwaysProviderItems[0].provider + : dynamicProviderItems[0].provider; return { ); }; + +export const CaptchaRule = { + Always: "Always", + Never: "Never", + Dynamic: "Dynamic", +}; diff --git a/web/src/locales/de/data.json b/web/src/locales/de/data.json index 270120f772b4..7e070f883ec3 100644 --- a/web/src/locales/de/data.json +++ b/web/src/locales/de/data.json @@ -25,6 +25,7 @@ "Copy prompt page URL": "URL der Prompt-Seite kopieren", "Copy signin page URL": "URL der Anmeldeseite kopieren", "Copy signup page URL": "URL der Anmeldeseite kopieren", + "Dynamic": "Dynamic", "Edit Application": "Anwendung bearbeiten", "Enable Email linking": "E-Mail-Verknüpfung aktivieren", "Enable Email linking - Tooltip": "Bei der Verwendung von Drittanbietern zur Anmeldung wird, wenn es in der Organisation einen Benutzer mit der gleichen E-Mail gibt, automatisch die Drittanbieter-Anmelde-Methode mit diesem Benutzer verbunden", diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json index 1037a1b87d80..9a23fbf05639 100644 --- a/web/src/locales/en/data.json +++ b/web/src/locales/en/data.json @@ -25,6 +25,7 @@ "Copy prompt page URL": "Copy prompt page URL", "Copy signin page URL": "Copy signin page URL", "Copy signup page URL": "Copy signup page URL", + "Dynamic": "Dynamic", "Edit Application": "Edit Application", "Enable Email linking": "Enable Email linking", "Enable Email linking - Tooltip": "When using 3rd-party providers to log in, if there is a user in the organization with the same Email, the 3rd-party login method will be automatically associated with that user", diff --git a/web/src/locales/es/data.json b/web/src/locales/es/data.json index 6cd6c92fdd68..f04970d81f92 100644 --- a/web/src/locales/es/data.json +++ b/web/src/locales/es/data.json @@ -25,6 +25,7 @@ "Copy prompt page URL": "Copiar URL de la página del prompt", "Copy signin page URL": "Copiar la URL de la página de inicio de sesión", "Copy signup page URL": "Copiar URL de la página de registro", + "Dynamic": "Dynamic", "Edit Application": "Editar solicitud", "Enable Email linking": "Habilitar enlace de correo electrónico", "Enable Email linking - Tooltip": "Cuando se utilizan proveedores externos de inicio de sesión, si hay un usuario en la organización con el mismo correo electrónico, el método de inicio de sesión externo se asociará automáticamente con ese usuario", diff --git a/web/src/locales/fr/data.json b/web/src/locales/fr/data.json index b25968b38c26..e6f582d65fcb 100644 --- a/web/src/locales/fr/data.json +++ b/web/src/locales/fr/data.json @@ -25,6 +25,7 @@ "Copy prompt page URL": "Copier l'URL de la page de l'invite", "Copy signin page URL": "Copier l'URL de la page de connexion", "Copy signup page URL": "Copiez l'URL de la page d'inscription", + "Dynamic": "Dynamic", "Edit Application": "Modifier l'application", "Enable Email linking": "Autoriser la liaison de courrier électronique", "Enable Email linking - Tooltip": "Lorsque l'on utilise des fournisseurs tiers pour se connecter, s'il y a un utilisateur dans l'organisation avec la même adresse e-mail, la méthode de connexion tierce sera automatiquement associée à cet utilisateur", diff --git a/web/src/locales/id/data.json b/web/src/locales/id/data.json index 6700dffb91aa..9e32809ff15f 100644 --- a/web/src/locales/id/data.json +++ b/web/src/locales/id/data.json @@ -25,6 +25,7 @@ "Copy prompt page URL": "Salin URL halaman prompt", "Copy signin page URL": "Salin URL halaman masuk", "Copy signup page URL": "Salin URL halaman pendaftaran", + "Dynamic": "Dynamic", "Edit Application": "Mengedit aplikasi", "Enable Email linking": "Aktifkan pengaitan email", "Enable Email linking - Tooltip": "Ketika menggunakan penyedia layanan pihak ketiga untuk masuk, jika ada pengguna di organisasi dengan email yang sama, metode login pihak ketiga akan secara otomatis terhubung dengan pengguna tersebut", diff --git a/web/src/locales/ja/data.json b/web/src/locales/ja/data.json index d67756f48b61..67315d8eb446 100644 --- a/web/src/locales/ja/data.json +++ b/web/src/locales/ja/data.json @@ -25,6 +25,7 @@ "Copy prompt page URL": "プロンプトページのURLをコピーしてください", "Copy signin page URL": "サインインページのURLをコピーしてください", "Copy signup page URL": "サインアップページのURLをコピーしてください", + "Dynamic": "Dynamic", "Edit Application": "アプリケーションを編集する", "Enable Email linking": "イーメールリンクの有効化", "Enable Email linking - Tooltip": "組織内に同じメールアドレスを持つユーザーがいる場合、サードパーティのログイン方法は自動的にそのユーザーに関連付けられます", diff --git a/web/src/locales/ko/data.json b/web/src/locales/ko/data.json index e29ecad770b1..c7c1dc5e3715 100644 --- a/web/src/locales/ko/data.json +++ b/web/src/locales/ko/data.json @@ -25,6 +25,7 @@ "Copy prompt page URL": "프롬프트 페이지 URL을 복사하세요", "Copy signin page URL": "사인인 페이지 URL 복사", "Copy signup page URL": "가입 페이지 URL을 복사하세요", + "Dynamic": "Dynamic", "Edit Application": "앱 편집하기", "Enable Email linking": "이메일 링크 사용 가능하도록 설정하기", "Enable Email linking - Tooltip": "3rd-party 로그인 공급자를 사용할 때, 만약 조직 내에 동일한 이메일을 사용하는 사용자가 있다면, 3rd-party 로그인 방법은 자동으로 해당 사용자와 연동됩니다", diff --git a/web/src/locales/ru/data.json b/web/src/locales/ru/data.json index f41c9fe972b7..8cd4c01872ff 100644 --- a/web/src/locales/ru/data.json +++ b/web/src/locales/ru/data.json @@ -25,6 +25,7 @@ "Copy prompt page URL": "Скопируйте URL страницы предложения", "Copy signin page URL": "Скопируйте URL-адрес страницы входа", "Copy signup page URL": "Скопируйте URL страницы регистрации", + "Dynamic": "Dynamic", "Edit Application": "Изменить приложение", "Enable Email linking": "Включить связывание электронной почты", "Enable Email linking - Tooltip": "При использовании сторонних провайдеров для входа, если в организации есть пользователь с такой же электронной почтой, то способ входа через стороннего провайдера автоматически будет связан с этим пользователем", diff --git a/web/src/locales/vi/data.json b/web/src/locales/vi/data.json index 8ee76367d363..e24ee4d96381 100644 --- a/web/src/locales/vi/data.json +++ b/web/src/locales/vi/data.json @@ -25,6 +25,7 @@ "Copy prompt page URL": "Sao chép URL của trang nhắc nhở", "Copy signin page URL": "Sao chép URL trang đăng nhập", "Copy signup page URL": "Sao chép URL trang đăng ký", + "Dynamic": "Dynamic", "Edit Application": "Chỉnh sửa ứng dụng", "Enable Email linking": "Cho phép liên kết Email", "Enable Email linking - Tooltip": "Khi sử dụng nhà cung cấp bên thứ ba để đăng nhập, nếu có người dùng trong tổ chức có cùng địa chỉ Email, phương pháp đăng nhập bên thứ ba sẽ tự động được liên kết với người dùng đó", diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json index 3ced090dddc8..72eb7932ecd5 100644 --- a/web/src/locales/zh/data.json +++ b/web/src/locales/zh/data.json @@ -25,6 +25,7 @@ "Copy prompt page URL": "复制提醒页面URL", "Copy signin page URL": "复制登录页面URL", "Copy signup page URL": "复制注册页面URL", + "Dynamic": "动态开启", "Edit Application": "编辑应用", "Enable Email linking": "自动关联邮箱相同的账号", "Enable Email linking - Tooltip": "使用第三方授权登录时,如果组织中存在与授权用户邮箱相同的用户,会自动关联该第三方登录方式到该用户", diff --git a/web/src/table/ProviderTable.js b/web/src/table/ProviderTable.js index c9a9296e5a68..a346b68b2f68 100644 --- a/web/src/table/ProviderTable.js +++ b/web/src/table/ProviderTable.js @@ -189,6 +189,7 @@ class ProviderTable extends React.Component { this.updateField(table, index, "rule", value); }} > + );