forked from casdoor/casdoor
feat: apply loginPage captcha rule check to SendCodeInput.js (#5369)
This commit is contained in:
@@ -252,6 +252,10 @@ func (c *ApiController) SendVerificationCode() {
|
||||
return
|
||||
}
|
||||
|
||||
if vform.CaptchaToken != "" {
|
||||
enableCaptcha = true
|
||||
}
|
||||
|
||||
// Only verify CAPTCHA if it should be enabled
|
||||
if enableCaptcha {
|
||||
captchaProvider, err := object.GetCaptchaProviderByApplication(vform.ApplicationId, "false", c.GetAcceptLanguage())
|
||||
|
||||
@@ -1489,6 +1489,43 @@ function isSigninMethodEnabled(application, signinMethod) {
|
||||
}
|
||||
}
|
||||
|
||||
export const CaptchaRule = {
|
||||
Always: "Always",
|
||||
Never: "Never",
|
||||
Dynamic: "Dynamic",
|
||||
InternetOnly: "Internet-Only",
|
||||
};
|
||||
|
||||
export function getCaptchaProviderItems(application) {
|
||||
const providers = application?.providers;
|
||||
if (!providers) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return providers.filter(providerItem => providerItem?.provider?.category === "Captcha");
|
||||
}
|
||||
|
||||
export function getCaptchaRule(application) {
|
||||
const captchaProviderItems = getCaptchaProviderItems(application);
|
||||
if (captchaProviderItems.some(providerItem => providerItem.rule === CaptchaRule.Always)) {
|
||||
return CaptchaRule.Always;
|
||||
} else if (captchaProviderItems.some(providerItem => providerItem.rule === CaptchaRule.Dynamic)) {
|
||||
return CaptchaRule.Dynamic;
|
||||
} else if (captchaProviderItems.some(providerItem => providerItem.rule === CaptchaRule.InternetOnly)) {
|
||||
return CaptchaRule.InternetOnly;
|
||||
}
|
||||
|
||||
return CaptchaRule.Never;
|
||||
}
|
||||
|
||||
export function isInlineCaptchaEnabled(application) {
|
||||
return application?.signinItems?.some(signinItem => signinItem.name === "Captcha" && signinItem.rule === "inline") || false;
|
||||
}
|
||||
|
||||
export function isCaptchaEnabled(application) {
|
||||
return getCaptchaRule(application) !== CaptchaRule.Never;
|
||||
}
|
||||
|
||||
export function isPasswordEnabled(application) {
|
||||
return isSigninMethodEnabled(application, "Password");
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import i18next from "i18next";
|
||||
import CustomGithubCorner from "../common/CustomGithubCorner";
|
||||
import {SendCodeInput} from "../common/SendCodeInput";
|
||||
import LanguageSelect from "../common/select/LanguageSelect";
|
||||
import {CaptchaModal, CaptchaRule} from "../common/modal/CaptchaModal";
|
||||
import {CaptchaModal} from "../common/modal/CaptchaModal";
|
||||
import RedirectForm from "../common/RedirectForm";
|
||||
import {RequiredMfa} from "./mfa/MfaAuthVerifyForm";
|
||||
import {GoogleOneTapLoginVirtualButton} from "./GoogleLoginButton";
|
||||
@@ -89,10 +89,6 @@ class LoginPage extends React.Component {
|
||||
this.captchaRef.current?.loadCaptcha?.();
|
||||
}
|
||||
|
||||
isInlineCaptchaEnabled(application = this.getApplicationObj()) {
|
||||
return application?.signinItems?.some(signinItem => signinItem.name === "Captcha" && signinItem.rule === "inline");
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
if (this.getApplicationObj() === undefined) {
|
||||
if (this.state.type === "login" || this.state.type === "saml") {
|
||||
@@ -144,21 +140,6 @@ 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 if (captchaProviderItems.some(providerItem => providerItem.rule === "Internet-Only")) {
|
||||
return CaptchaRule.InternetOnly;
|
||||
} else {
|
||||
return CaptchaRule.Never;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkCaptchaStatus(values) {
|
||||
AuthBackend.getCaptchaStatus(values)
|
||||
.then((res) => {
|
||||
@@ -459,20 +440,20 @@ class LoginPage extends React.Component {
|
||||
} else {
|
||||
values["password"] = passwordCipher;
|
||||
}
|
||||
const captchaRule = this.getCaptchaRule(this.getApplicationObj());
|
||||
const captchaRule = Setting.getCaptchaRule(this.getApplicationObj());
|
||||
const application = this.getApplicationObj();
|
||||
const inlineCaptchaEnabled = this.isInlineCaptchaEnabled(application);
|
||||
const inlineCaptchaEnabled = Setting.isInlineCaptchaEnabled(application);
|
||||
if (!inlineCaptchaEnabled) {
|
||||
if (captchaRule === CaptchaRule.Always) {
|
||||
if (captchaRule === Setting.CaptchaRule.Always) {
|
||||
this.setState({
|
||||
openCaptchaModal: true,
|
||||
values: values,
|
||||
});
|
||||
return;
|
||||
} else if (captchaRule === CaptchaRule.Dynamic) {
|
||||
} else if (captchaRule === Setting.CaptchaRule.Dynamic) {
|
||||
this.checkCaptchaStatus(values);
|
||||
return;
|
||||
} else if (captchaRule === CaptchaRule.InternetOnly) {
|
||||
} else if (captchaRule === Setting.CaptchaRule.InternetOnly) {
|
||||
this.checkCaptchaStatus(values);
|
||||
return;
|
||||
}
|
||||
@@ -489,7 +470,7 @@ class LoginPage extends React.Component {
|
||||
// here we are supposed to determine whether Casdoor is working as an OAuth server or CAS server
|
||||
values["language"] = this.state.userLang ?? "";
|
||||
const usedCaptcha = this.state.captchaValues !== undefined;
|
||||
const inlineCaptchaEnabled = this.isInlineCaptchaEnabled();
|
||||
const inlineCaptchaEnabled = Setting.isInlineCaptchaEnabled(this.getApplicationObj());
|
||||
const shouldRefreshCaptcha = usedCaptcha && inlineCaptchaEnabled && !this.state.loginMethod?.includes("verificationCode");
|
||||
if (this.state.type === "cas") {
|
||||
// CAS
|
||||
@@ -1108,40 +1089,24 @@ class LoginPage extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
getCaptchaProviderItems(application) {
|
||||
const providers = application?.providers;
|
||||
|
||||
if (providers === undefined || providers === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return providers.filter(providerItem => {
|
||||
if (providerItem.provider === undefined || providerItem.provider === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return providerItem.provider.category === "Captcha";
|
||||
});
|
||||
}
|
||||
|
||||
renderCaptchaModal(application, noModal) {
|
||||
if (this.getCaptchaRule(this.getApplicationObj()) === CaptchaRule.Never) {
|
||||
if (Setting.getCaptchaRule(this.getApplicationObj()) === Setting.CaptchaRule.Never) {
|
||||
return null;
|
||||
}
|
||||
const captchaProviderItems = this.getCaptchaProviderItems(application);
|
||||
const captchaProviderItems = Setting.getCaptchaProviderItems(application);
|
||||
const alwaysProviderItems = captchaProviderItems.filter(providerItem => providerItem.rule === "Always");
|
||||
const dynamicProviderItems = captchaProviderItems.filter(providerItem => providerItem.rule === "Dynamic");
|
||||
const internetOnlyProviderItems = captchaProviderItems.filter(providerItem => providerItem.rule === "Internet-Only");
|
||||
|
||||
// Select provider based on the active captcha rule, not fixed priority
|
||||
const captchaRule = this.getCaptchaRule(this.getApplicationObj());
|
||||
const captchaRule = Setting.getCaptchaRule(this.getApplicationObj());
|
||||
let provider = null;
|
||||
|
||||
if (captchaRule === CaptchaRule.Always && alwaysProviderItems.length > 0) {
|
||||
if (captchaRule === Setting.CaptchaRule.Always && alwaysProviderItems.length > 0) {
|
||||
provider = alwaysProviderItems[0].provider;
|
||||
} else if (captchaRule === CaptchaRule.Dynamic && dynamicProviderItems.length > 0) {
|
||||
} else if (captchaRule === Setting.CaptchaRule.Dynamic && dynamicProviderItems.length > 0) {
|
||||
provider = dynamicProviderItems[0].provider;
|
||||
} else if (captchaRule === CaptchaRule.InternetOnly && internetOnlyProviderItems.length > 0) {
|
||||
} else if (captchaRule === Setting.CaptchaRule.InternetOnly && internetOnlyProviderItems.length > 0) {
|
||||
provider = internetOnlyProviderItems[0].provider;
|
||||
}
|
||||
|
||||
@@ -1368,10 +1333,10 @@ class LoginPage extends React.Component {
|
||||
<SendCodeInput
|
||||
disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone}
|
||||
method={"login"}
|
||||
onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]}
|
||||
onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application), this.state.username]}
|
||||
application={application}
|
||||
captchaValue={this.state.captchaValues}
|
||||
useInlineCaptcha={this.isInlineCaptchaEnabled(application)}
|
||||
useInlineCaptcha={Setting.isInlineCaptchaEnabled(application)}
|
||||
refreshCaptcha={this.refreshInlineCaptcha}
|
||||
/>
|
||||
</Form.Item>
|
||||
@@ -1400,7 +1365,7 @@ class LoginPage extends React.Component {
|
||||
onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]}
|
||||
application={application}
|
||||
captchaValue={this.state.captchaValues}
|
||||
useInlineCaptcha={this.isInlineCaptchaEnabled(application)}
|
||||
useInlineCaptcha={Setting.isInlineCaptchaEnabled(application)}
|
||||
refreshCaptcha={this.refreshInlineCaptcha}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -106,7 +106,7 @@ export const MfaVerifySmsForm = ({mfaProps, application, onFinish, method, user}
|
||||
<SendCodeInput
|
||||
countryCode={form.getFieldValue("countryCode")}
|
||||
method={method}
|
||||
onButtonClickArgs={[mfaProps.secret || dest, isEmail() ? "email" : "phone", Setting.getApplicationName(application)]}
|
||||
onButtonClickArgs={[mfaProps.secret || dest, isEmail() ? "email" : "phone", Setting.getApplicationName(application), user?.name]}
|
||||
application={application}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {Button, Input} from "antd";
|
||||
import React from "react";
|
||||
import i18next from "i18next";
|
||||
import * as UserBackend from "../backend/UserBackend";
|
||||
import * as AuthBackend from "../auth/AuthBackend";
|
||||
import * as Setting from "../Setting";
|
||||
import {SafetyOutlined} from "@ant-design/icons";
|
||||
import {CaptchaModal} from "./modal/CaptchaModal";
|
||||
@@ -71,17 +72,66 @@ export const SendCodeInput = ({value, disabled, captchaValue, useInlineCaptcha,
|
||||
};
|
||||
|
||||
const handleSearch = () => {
|
||||
if (!useInlineCaptcha) {
|
||||
setVisible(true);
|
||||
const sendCodeWithoutCaptcha = () => {
|
||||
handleOk("none", "", "");
|
||||
};
|
||||
|
||||
const sendCodeWithCaptcha = () => {
|
||||
if (!useInlineCaptcha) {
|
||||
setVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// client secret is validated in backend
|
||||
if (!captchaValue?.captchaType || !captchaValue?.captchaToken) {
|
||||
Setting.showMessage("error", i18next.t("general:Please complete the captcha correctly"));
|
||||
return;
|
||||
}
|
||||
|
||||
handleOk(captchaValue.captchaType, captchaValue.captchaToken, captchaValue.clientSecret);
|
||||
};
|
||||
|
||||
const checkCaptchaStatusAndSend = () => {
|
||||
if (!onButtonClickArgs?.[3]) {
|
||||
return;
|
||||
}
|
||||
const values = {
|
||||
organization: application?.organization,
|
||||
username: onButtonClickArgs?.[3],
|
||||
application: application?.name,
|
||||
};
|
||||
|
||||
AuthBackend.getCaptchaStatus(values)
|
||||
.then((res) => {
|
||||
if (res.status === "ok" && res.data) {
|
||||
sendCodeWithCaptcha();
|
||||
return;
|
||||
}
|
||||
|
||||
sendCodeWithoutCaptcha();
|
||||
})
|
||||
.catch(() => {
|
||||
sendCodeWithoutCaptcha();
|
||||
});
|
||||
};
|
||||
|
||||
const captchaRule = Setting.getCaptchaRule(application);
|
||||
if (captchaRule === Setting.CaptchaRule.Never) {
|
||||
sendCodeWithoutCaptcha();
|
||||
return;
|
||||
}
|
||||
|
||||
// client secret is validated in backend
|
||||
if (!captchaValue?.captchaType || !captchaValue?.captchaToken) {
|
||||
Setting.showMessage("error", i18next.t("general:Please complete the captcha correctly"));
|
||||
if (captchaRule === Setting.CaptchaRule.Always) {
|
||||
sendCodeWithCaptcha();
|
||||
return;
|
||||
}
|
||||
handleOk(captchaValue.captchaType, captchaValue.captchaToken, captchaValue.clientSecret);
|
||||
|
||||
if (captchaRule === Setting.CaptchaRule.Dynamic || captchaRule === Setting.CaptchaRule.InternetOnly) {
|
||||
checkCaptchaStatusAndSend();
|
||||
return;
|
||||
}
|
||||
|
||||
sendCodeWithoutCaptcha();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user