mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-31 08:50:31 +08:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1ee2ff1d30 | ||
![]() |
c0d9969013 | ||
![]() |
1bdee13150 | ||
![]() |
d668022af0 | ||
![]() |
e227875c2b | ||
![]() |
e473de3162 | ||
![]() |
c5ef841d3f | ||
![]() |
d46288b591 | ||
![]() |
b968bf033c | ||
![]() |
eca2527bc0 | ||
![]() |
ef836acfe9 |
@@ -47,6 +47,7 @@ func (c *ApiController) SendVerificationCode() {
|
||||
checkKey := c.Ctx.Request.Form.Get("checkKey")
|
||||
checkUser := c.Ctx.Request.Form.Get("checkUser")
|
||||
applicationId := c.Ctx.Request.Form.Get("applicationId")
|
||||
method := c.Ctx.Request.Form.Get("method")
|
||||
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
|
||||
|
||||
if destType == "" {
|
||||
@@ -119,7 +120,7 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
|
||||
userByEmail := object.GetUserByEmail(organization.Name, dest)
|
||||
if userByEmail == nil {
|
||||
if userByEmail == nil && method != "signup" && method != "reset" {
|
||||
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
|
||||
return
|
||||
}
|
||||
@@ -136,7 +137,7 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
|
||||
userByPhone := object.GetUserByPhone(organization.Name, dest)
|
||||
if userByPhone == nil {
|
||||
if userByPhone == nil && method != "signup" && method != "reset" {
|
||||
c.ResponseError(c.T("verification:the user does not exist, please sign up first"))
|
||||
return
|
||||
}
|
||||
|
@@ -256,6 +256,8 @@ func (idp *DingTalkIdProvider) isUserInOrg(unionId string) (bool, error) {
|
||||
}
|
||||
if data.ErrCode == 60121 {
|
||||
return false, fmt.Errorf("the user is not found in the organization where clientId and clientSecret belong")
|
||||
} else if data.ErrCode != 0 {
|
||||
return false, fmt.Errorf(data.ErrMessage)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
@@ -51,6 +51,7 @@ type Application struct {
|
||||
EnableCodeSignin bool `json:"enableCodeSignin"`
|
||||
EnableSamlCompress bool `json:"enableSamlCompress"`
|
||||
EnableWebAuthn bool `json:"enableWebAuthn"`
|
||||
SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
|
||||
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
|
||||
SignupItems []*SignupItem `xorm:"varchar(1000)" json:"signupItems"`
|
||||
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
|
||||
|
@@ -41,7 +41,7 @@ func getEnforcer(permission *Permission) *casbin.Enforcer {
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
p = sub, obj, act, "", "", permissionId
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
@@ -27,15 +27,16 @@ type Product struct {
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
|
||||
Image string `xorm:"varchar(100)" json:"image"`
|
||||
Detail string `xorm:"varchar(255)" json:"detail"`
|
||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
Price float64 `json:"price"`
|
||||
Quantity int `json:"quantity"`
|
||||
Sold int `json:"sold"`
|
||||
Providers []string `xorm:"varchar(100)" json:"providers"`
|
||||
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
|
||||
Image string `xorm:"varchar(100)" json:"image"`
|
||||
Detail string `xorm:"varchar(255)" json:"detail"`
|
||||
Description string `xorm:"varchar(100)" json:"description"`
|
||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
Price float64 `json:"price"`
|
||||
Quantity int `json:"quantity"`
|
||||
Sold int `json:"sold"`
|
||||
Providers []string `xorm:"varchar(100)" json:"providers"`
|
||||
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
|
||||
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
|
||||
@@ -213,6 +214,10 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
|
||||
}
|
||||
|
||||
func ExtendProductWithProviders(product *Product) {
|
||||
if product == nil {
|
||||
return
|
||||
}
|
||||
|
||||
product.ProviderObjs = []*Provider{}
|
||||
|
||||
m := getProviderMap(product.Owner)
|
||||
|
@@ -251,6 +251,11 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
|
||||
_, originBackend := getOriginFromHost(host)
|
||||
|
||||
// redirect Url (Assertion Consumer Url)
|
||||
if application.SamlReplyUrl != "" {
|
||||
authnRequest.AssertionConsumerServiceURL = application.SamlReplyUrl
|
||||
}
|
||||
|
||||
// build signedResponse
|
||||
samlResponse, _ := NewSamlResponse(user, originBackend, certificate, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer.Url, authnRequest.ID, application.RedirectUris)
|
||||
randomKeyStore := &X509Key{
|
||||
|
@@ -545,6 +545,16 @@ class ApplicationEditPage extends React.Component {
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("application:SAML Reply URL"), i18next.t("application:Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input prefix={<LinkOutlined />} value={this.state.application.samlReplyUrl} onChange={e => {
|
||||
this.updateApplicationField("samlReplyUrl", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||
{Setting.getLabel(i18next.t("application:Enable SAML compress"), i18next.t("application:Enable SAML compress - Tooltip"))} :
|
||||
|
@@ -13,7 +13,8 @@
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Descriptions, Spin} from "antd";
|
||||
import {Button, Descriptions, Modal, Spin} from "antd";
|
||||
import {CheckCircleTwoTone} from "@ant-design/icons";
|
||||
import i18next from "i18next";
|
||||
import * as ProductBackend from "./backend/ProductBackend";
|
||||
import * as Setting from "./Setting";
|
||||
@@ -26,6 +27,7 @@ class ProductBuyPage extends React.Component {
|
||||
productName: props.match?.params.productName,
|
||||
product: null,
|
||||
isPlacingOrder: false,
|
||||
qrCodeModalProvider: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,6 +36,10 @@ class ProductBuyPage extends React.Component {
|
||||
}
|
||||
|
||||
getProduct() {
|
||||
if (this.state.productName === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
ProductBackend.getProduct("admin", this.state.productName)
|
||||
.then((product) => {
|
||||
this.setState({
|
||||
@@ -75,6 +81,13 @@ class ProductBuyPage extends React.Component {
|
||||
}
|
||||
|
||||
buyProduct(product, provider) {
|
||||
if (provider.clientId.startsWith("http")) {
|
||||
this.setState({
|
||||
qrCodeModalProvider: provider,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isPlacingOrder: true,
|
||||
});
|
||||
@@ -97,6 +110,45 @@ class ProductBuyPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
renderQrCodeModal() {
|
||||
if (this.state.qrCodeModalProvider === undefined || this.state.qrCodeModalProvider === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal title={
|
||||
<div>
|
||||
<CheckCircleTwoTone twoToneColor="rgb(45,120,213)" />
|
||||
{" " + i18next.t("product:Please scan the QR code to pay")}
|
||||
</div>
|
||||
}
|
||||
open={this.state.qrCodeModalProvider !== undefined && this.state.qrCodeModalProvider !== null}
|
||||
onOk={() => {
|
||||
Setting.goToLink(this.state.product.returnUrl);
|
||||
}}
|
||||
onCancel={() => {
|
||||
this.setState({
|
||||
qrCodeModalProvider: null,
|
||||
});
|
||||
}}
|
||||
okText={i18next.t("product:I have completed the payment")}
|
||||
cancelText={i18next.t("general:Cancel")}>
|
||||
<p key={this.state.qrCodeModalProvider?.name}>
|
||||
{
|
||||
i18next.t("product:Please provide your username in the remark")
|
||||
}
|
||||
:
|
||||
{
|
||||
Setting.getTag("default", this.props.account.name)
|
||||
}
|
||||
<br />
|
||||
<br />
|
||||
<img src={this.state.qrCodeModalProvider?.clientId} alt={this.state.qrCodeModalProvider?.name} width={"472px"} style={{marginBottom: "20px"}} />
|
||||
</p>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
getPayButton(provider) {
|
||||
let text = provider.type;
|
||||
if (provider.type === "Alipay") {
|
||||
@@ -185,6 +237,9 @@ class ProductBuyPage extends React.Component {
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</Spin>
|
||||
{
|
||||
this.renderQrCodeModal()
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -153,6 +153,16 @@ class ProductEditPage extends React.Component {
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("product:Description"), i18next.t("product:Description - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.product.description} onChange={e => {
|
||||
this.updateProductField("description", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("product:Currency"), i18next.t("product:Currency - Tooltip"))} :
|
||||
|
@@ -92,6 +92,7 @@ export const ResetModal = (props) => {
|
||||
<CountDownInput
|
||||
textBefore={i18next.t("code:Code You Received")}
|
||||
onChange={setCode}
|
||||
method={"reset"}
|
||||
onButtonClickArgs={[dest, destType, Setting.getApplicationName(application)]}
|
||||
application={application}
|
||||
/>
|
||||
|
@@ -32,16 +32,7 @@ class SelectLanguageBox extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
items = [
|
||||
Setting.getItem("English", "en", flagIcon("US", "English")),
|
||||
Setting.getItem("简体中文", "zh", flagIcon("CN", "简体中文")),
|
||||
Setting.getItem("Español", "es", flagIcon("ES", "Español")),
|
||||
Setting.getItem("Français", "fr", flagIcon("FR", "Français")),
|
||||
Setting.getItem("Deutsch", "de", flagIcon("DE", "Deutsch")),
|
||||
Setting.getItem("日本語", "ja", flagIcon("JP", "日本語")),
|
||||
Setting.getItem("한국어", "ko", flagIcon("KR", "한국어")),
|
||||
Setting.getItem("Русский", "ru", flagIcon("RU", "Русский")),
|
||||
];
|
||||
items = Setting.Countries.map((country) => Setting.getItem(country.label, country.key, flagIcon(country.country, country.alt)));
|
||||
|
||||
getOrganizationLanguages(languages) {
|
||||
const select = [];
|
||||
|
@@ -42,12 +42,15 @@ class SelectRegionBox extends React.Component {
|
||||
placeholder="Please select country/region"
|
||||
onChange={(value => {this.onChange(value);})}
|
||||
filterOption={(input, option) =>
|
||||
option.label.indexOf(input) >= 0
|
||||
(option?.label ?? "").toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
filterSort={(optionA, optionB) =>
|
||||
(optionA?.label ?? "").toLowerCase().localeCompare((optionB?.label ?? "").toLowerCase())
|
||||
}
|
||||
>
|
||||
{
|
||||
Setting.CountryRegionData.map((item, index) => (
|
||||
<Option key={index} value={item.code} label={item.code} >
|
||||
<Option key={index} value={item.code} label={`${item.name} (${item.code})`} >
|
||||
<img src={`${Setting.StaticBaseUrl}/flag-icons/${item.code}.svg`} alt={item.name} height={20} style={{marginRight: 10}} />
|
||||
{`${item.name} (${item.code})`}
|
||||
</Option>
|
||||
|
@@ -33,6 +33,16 @@ export const StaticBaseUrl = "https://cdn.casbin.org";
|
||||
// https://catamphetamine.gitlab.io/country-flag-icons/3x2/index.html
|
||||
export const CountryRegionData = getCountryRegionData();
|
||||
|
||||
export const Countries = [{label: "English", key: "en", country: "US", alt: "English"},
|
||||
{label: "简体中文", key: "zh", country: "CN", alt: "简体中文"},
|
||||
{label: "Español", key: "es", country: "ES", alt: "Español"},
|
||||
{label: "Français", key: "fr", country: "FR", alt: "Français"},
|
||||
{label: "Deutsch", key: "de", country: "DE", alt: "Deutsch"},
|
||||
{label: "日本語", key: "ja", country: "JP", alt: "日本語"},
|
||||
{label: "한국어", key: "ko", country: "KR", alt: "한국어"},
|
||||
{label: "Русский", key: "ru", country: "RU", alt: "Русский"},
|
||||
];
|
||||
|
||||
export const OtherProviderInfo = {
|
||||
SMS: {
|
||||
"Aliyun SMS": {
|
||||
@@ -251,8 +261,10 @@ export function isValidPersonName(personName) {
|
||||
}
|
||||
|
||||
export function isValidIdCard(idCard) {
|
||||
const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9X]$/;
|
||||
return idCardRegex.test(idCard);
|
||||
return idCard !== "";
|
||||
|
||||
// const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9X]$/;
|
||||
// return idCardRegex.test(idCard);
|
||||
}
|
||||
|
||||
export function isValidEmail(email) {
|
||||
@@ -262,34 +274,40 @@ export function isValidEmail(email) {
|
||||
}
|
||||
|
||||
export function isValidPhone(phone) {
|
||||
if (phone === "") {
|
||||
return false;
|
||||
}
|
||||
return phone !== "";
|
||||
|
||||
// https://learnku.com/articles/31543, `^s*$` filter empty email individually.
|
||||
const phoneRegex = /^\s*$|^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/;
|
||||
return phoneRegex.test(phone);
|
||||
// if (phone === "") {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// // https://learnku.com/articles/31543, `^s*$` filter empty email individually.
|
||||
// const phoneRegex = /^\s*$|^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/;
|
||||
// return phoneRegex.test(phone);
|
||||
}
|
||||
|
||||
export function isValidInvoiceTitle(invoiceTitle) {
|
||||
if (invoiceTitle === "") {
|
||||
return false;
|
||||
}
|
||||
return invoiceTitle !== "";
|
||||
|
||||
// https://blog.css8.cn/post/14210975.html
|
||||
const invoiceTitleRegex = /^[()()\u4e00-\u9fa5]{0,50}$/;
|
||||
return invoiceTitleRegex.test(invoiceTitle);
|
||||
// if (invoiceTitle === "") {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// // https://blog.css8.cn/post/14210975.html
|
||||
// const invoiceTitleRegex = /^[()()\u4e00-\u9fa5]{0,50}$/;
|
||||
// return invoiceTitleRegex.test(invoiceTitle);
|
||||
}
|
||||
|
||||
export function isValidTaxId(taxId) {
|
||||
// https://www.codetd.com/article/8592083
|
||||
const regArr = [/^[\da-z]{10,15}$/i, /^\d{6}[\da-z]{10,12}$/i, /^[a-z]\d{6}[\da-z]{9,11}$/i, /^[a-z]{2}\d{6}[\da-z]{8,10}$/i, /^\d{14}[\dx][\da-z]{4,5}$/i, /^\d{17}[\dx][\da-z]{1,2}$/i, /^[a-z]\d{14}[\dx][\da-z]{3,4}$/i, /^[a-z]\d{17}[\dx][\da-z]{0,1}$/i, /^[\d]{6}[\da-z]{13,14}$/i];
|
||||
for (let i = 0; i < regArr.length; i++) {
|
||||
if (regArr[i].test(taxId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return taxId !== "";
|
||||
|
||||
// // https://www.codetd.com/article/8592083
|
||||
// const regArr = [/^[\da-z]{10,15}$/i, /^\d{6}[\da-z]{10,12}$/i, /^[a-z]\d{6}[\da-z]{9,11}$/i, /^[a-z]{2}\d{6}[\da-z]{8,10}$/i, /^\d{14}[\dx][\da-z]{4,5}$/i, /^\d{17}[\dx][\da-z]{1,2}$/i, /^[a-z]\d{14}[\dx][\da-z]{3,4}$/i, /^[a-z]\d{17}[\dx][\da-z]{0,1}$/i, /^[\d]{6}[\da-z]{13,14}$/i];
|
||||
// for (let i = 0; i < regArr.length; i++) {
|
||||
// if (regArr[i].test(taxId)) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
}
|
||||
|
||||
export function isAffiliationPrompted(application) {
|
||||
|
@@ -355,12 +355,14 @@ class ForgetPage extends React.Component {
|
||||
{this.state.verifyType === "email" ? (
|
||||
<CountDownInput
|
||||
disabled={this.state.username === "" || this.state.verifyType === ""}
|
||||
method={"forget"}
|
||||
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(this.state.application), this.state.name]}
|
||||
application={application}
|
||||
/>
|
||||
) : (
|
||||
<CountDownInput
|
||||
disabled={this.state.username === "" || this.state.verifyType === ""}
|
||||
method={"forget"}
|
||||
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(this.state.application), this.state.name]}
|
||||
application={application}
|
||||
/>
|
||||
|
@@ -29,6 +29,7 @@ import CustomGithubCorner from "../CustomGithubCorner";
|
||||
import {CountDownInput} from "../common/CountDownInput";
|
||||
import SelectLanguageBox from "../SelectLanguageBox";
|
||||
import {CaptchaModal} from "../common/CaptchaModal";
|
||||
import RedirectForm from "../common/RedirectForm";
|
||||
|
||||
class LoginPage extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -49,6 +50,9 @@ class LoginPage extends React.Component {
|
||||
enableCaptchaModal: false,
|
||||
openCaptchaModal: false,
|
||||
verifyCaptcha: undefined,
|
||||
samlResponse: "",
|
||||
relayState: "",
|
||||
redirectUrl: "",
|
||||
};
|
||||
|
||||
if (this.state.type === "cas" && props.match?.params.casApplicationName !== undefined) {
|
||||
@@ -69,6 +73,12 @@ class LoginPage extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
Setting.Countries.forEach((country) => {
|
||||
new Image().src = `${Setting.StaticBaseUrl}/flag-icons/${country.country}.svg`;
|
||||
});
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
if (this.state.application && !prevState.application) {
|
||||
const defaultCaptchaProviderItems = this.getDefaultCaptchaProviderItems(this.state.application);
|
||||
@@ -178,6 +188,7 @@ class LoginPage extends React.Component {
|
||||
|
||||
if (values["samlRequest"] !== null && values["samlRequest"] !== "" && values["samlRequest"] !== undefined) {
|
||||
values["type"] = "saml";
|
||||
values["relayState"] = oAuthParams.relayState;
|
||||
}
|
||||
|
||||
if (this.state.application.organization !== null && this.state.application.organization !== undefined) {
|
||||
@@ -306,7 +317,15 @@ class LoginPage extends React.Component {
|
||||
} else if (responseType === "saml") {
|
||||
const SAMLResponse = res.data;
|
||||
const redirectUri = res.data2;
|
||||
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);
|
||||
if (this.state.application.assertionConsumerUrl !== "") {
|
||||
this.setState({
|
||||
samlResponse: res.data,
|
||||
redirectUrl: res.data2,
|
||||
relayState: oAuthParams.relayState,
|
||||
});
|
||||
} else {
|
||||
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.setState({openCaptchaModal: false});
|
||||
@@ -655,7 +674,7 @@ class LoginPage extends React.Component {
|
||||
const rawId = assertion.rawId;
|
||||
const sig = assertion.response.signature;
|
||||
const userHandle = assertion.response.userHandle;
|
||||
return fetch(`${Setting.ServerUrl}/api/webauthn/signin/finish${AuthBackend.oAuthParamsToQuery(oAuthParams)}`, {
|
||||
return fetch(`${Setting.ServerUrl}/api/webauthn/signin/finish?responseType=${values["type"]}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify({
|
||||
@@ -719,6 +738,7 @@ class LoginPage extends React.Component {
|
||||
>
|
||||
<CountDownInput
|
||||
disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone}
|
||||
method={"login"}
|
||||
onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]}
|
||||
application={application}
|
||||
/>
|
||||
@@ -754,6 +774,10 @@ class LoginPage extends React.Component {
|
||||
return Util.renderMessageLarge(this, this.state.msg);
|
||||
}
|
||||
|
||||
if (this.state.samlResponse !== "") {
|
||||
return <RedirectForm samlResponse={this.state.samlResponse} redirectUrl={this.state.redirectUrl} relayState={this.state.relayState} />;
|
||||
}
|
||||
|
||||
if (application.signinHtml !== "") {
|
||||
return (
|
||||
<div dangerouslySetInnerHTML={{__html: application.signinHtml}} />
|
||||
|
@@ -373,6 +373,7 @@ class SignupPage extends React.Component {
|
||||
>
|
||||
<CountDownInput
|
||||
disabled={!this.state.validEmail}
|
||||
method={"signup"}
|
||||
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(application)]}
|
||||
application={application}
|
||||
/>
|
||||
@@ -426,6 +427,7 @@ class SignupPage extends React.Component {
|
||||
>
|
||||
<CountDownInput
|
||||
disabled={!this.state.validPhone}
|
||||
method={"signup"}
|
||||
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(application)]}
|
||||
application={application}
|
||||
/>
|
||||
|
@@ -109,11 +109,12 @@ export function setPassword(userOwner, userName, oldPassword, newPassword) {
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function sendCode(checkType, checkId, checkKey, dest, type, applicationId, checkUser) {
|
||||
export function sendCode(checkType, checkId, checkKey, method, dest, type, applicationId, checkUser) {
|
||||
const formData = new FormData();
|
||||
formData.append("checkType", checkType);
|
||||
formData.append("checkId", checkId);
|
||||
formData.append("checkKey", checkKey);
|
||||
formData.append("method", method);
|
||||
formData.append("dest", dest);
|
||||
formData.append("type", type);
|
||||
formData.append("applicationId", applicationId);
|
||||
|
@@ -125,5 +125,5 @@ export const CaptchaWidget = ({captchaType, subType, siteKey, clientSecret, onCh
|
||||
}
|
||||
}, [captchaType, subType, siteKey, clientSecret, clientId2, clientSecret2]);
|
||||
|
||||
return <div id="captcha"></div>;
|
||||
return <div id="captcha" />;
|
||||
};
|
||||
|
@@ -22,7 +22,7 @@ import {CaptchaWidget} from "./CaptchaWidget";
|
||||
const {Search} = Input;
|
||||
|
||||
export const CountDownInput = (props) => {
|
||||
const {disabled, textBefore, onChange, onButtonClickArgs, application} = props;
|
||||
const {disabled, textBefore, onChange, onButtonClickArgs, application, method} = props;
|
||||
const [visible, setVisible] = React.useState(false);
|
||||
const [key, setKey] = React.useState("");
|
||||
const [captchaImg, setCaptchaImg] = React.useState("");
|
||||
@@ -53,7 +53,7 @@ export const CountDownInput = (props) => {
|
||||
const handleOk = () => {
|
||||
setVisible(false);
|
||||
setButtonLoading(true);
|
||||
UserBackend.sendCode(checkType, checkId, key, ...onButtonClickArgs).then(res => {
|
||||
UserBackend.sendCode(checkType, checkId, key, method, ...onButtonClickArgs).then(res => {
|
||||
setKey("");
|
||||
setButtonLoading(false);
|
||||
if (res) {
|
||||
@@ -70,7 +70,7 @@ export const CountDownInput = (props) => {
|
||||
const loadCaptcha = () => {
|
||||
UserBackend.getCaptcha(application.owner, application.name, false).then(res => {
|
||||
if (res.type === "none") {
|
||||
UserBackend.sendCode("none", "", "", ...onButtonClickArgs).then(res => {
|
||||
UserBackend.sendCode("none", "", "", method, ...onButtonClickArgs).then(res => {
|
||||
if (res) {
|
||||
handleCountDown(60);
|
||||
}
|
||||
|
43
web/src/common/RedirectForm.js
Normal file
43
web/src/common/RedirectForm.js
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
import React, {useEffect} from "react";
|
||||
|
||||
export const RedirectForm = (props) => {
|
||||
|
||||
useEffect(() => {
|
||||
document.getElementById("saml").submit();
|
||||
}, []);
|
||||
|
||||
return (<>
|
||||
<p>Redirecting, please wait.</p>
|
||||
<form id="saml" method="post" action={props.redirectUrl}>
|
||||
<input
|
||||
type="hidden"
|
||||
name="SAMLResponse"
|
||||
id="samlResponse"
|
||||
value={props.samlResponse}
|
||||
/>
|
||||
<input
|
||||
type="hidden"
|
||||
name="RelayState"
|
||||
id="relayState"
|
||||
value={props.relayState}
|
||||
/>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RedirectForm;
|
@@ -58,12 +58,14 @@
|
||||
"Please select a HTML file": "Bitte wählen Sie eine HTML-Datei",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Redirect URL": "Weiterleitungs-URL",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip",
|
||||
"Redirect URLs": "Umleitungs-URLs",
|
||||
"Redirect URLs - Tooltip": "List of redirect addresses after successful login",
|
||||
"Refresh token expire": "Aktualisierungs-Token läuft ab",
|
||||
"Refresh token expire - Tooltip": "Aktualisierungs-Token läuft ab - Tooltip",
|
||||
"Right": "Right",
|
||||
"Rule": "Rule",
|
||||
"SAML Reply URL": "SAML Reply URL",
|
||||
"SAML metadata": "SAML metadata",
|
||||
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
|
||||
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
|
||||
@@ -432,9 +434,12 @@
|
||||
"CNY": "CNY",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Description": "Description",
|
||||
"Description - Tooltip": "Description - Tooltip",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail - Tooltip",
|
||||
"Edit Product": "Edit Product",
|
||||
"I have completed the payment": "I have completed the payment",
|
||||
"Image": "Image",
|
||||
"Image - Tooltip": "Image - Tooltip",
|
||||
"New Product": "New Product",
|
||||
@@ -443,6 +448,8 @@
|
||||
"Payment providers - Tooltip": "Payment providers - Tooltip",
|
||||
"Paypal": "Paypal",
|
||||
"Placing order...": "Placing order...",
|
||||
"Please provide your username in the remark": "Please provide your username in the remark",
|
||||
"Please scan the QR code to pay": "Please scan the QR code to pay",
|
||||
"Price": "Price",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Quantity": "Quantity",
|
||||
|
@@ -58,12 +58,14 @@
|
||||
"Please select a HTML file": "Please select a HTML file",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Redirect URL": "Redirect URL",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip",
|
||||
"Redirect URLs": "Redirect URLs",
|
||||
"Redirect URLs - Tooltip": "Redirect URLs - Tooltip",
|
||||
"Refresh token expire": "Refresh token expire",
|
||||
"Refresh token expire - Tooltip": "Refresh token expire - Tooltip",
|
||||
"Right": "Right",
|
||||
"Rule": "Rule",
|
||||
"SAML Reply URL": "SAML Reply URL",
|
||||
"SAML metadata": "SAML metadata",
|
||||
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
|
||||
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
|
||||
@@ -432,9 +434,12 @@
|
||||
"CNY": "CNY",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Description": "Description",
|
||||
"Description - Tooltip": "Description - Tooltip",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail - Tooltip",
|
||||
"Edit Product": "Edit Product",
|
||||
"I have completed the payment": "I have completed the payment",
|
||||
"Image": "Image",
|
||||
"Image - Tooltip": "Image - Tooltip",
|
||||
"New Product": "New Product",
|
||||
@@ -443,6 +448,8 @@
|
||||
"Payment providers - Tooltip": "Payment providers - Tooltip",
|
||||
"Paypal": "Paypal",
|
||||
"Placing order...": "Placing order...",
|
||||
"Please provide your username in the remark": "Please provide your username in the remark",
|
||||
"Please scan the QR code to pay": "Please scan the QR code to pay",
|
||||
"Price": "Price",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Quantity": "Quantity",
|
||||
|
@@ -58,12 +58,14 @@
|
||||
"Please select a HTML file": "Veuillez sélectionner un fichier HTML",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Redirect URL": "URL de redirection",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip",
|
||||
"Redirect URLs": "URL de redirection",
|
||||
"Redirect URLs - Tooltip": "List of redirect addresses after successful login",
|
||||
"Refresh token expire": "Expiration du jeton d'actualisation",
|
||||
"Refresh token expire - Tooltip": "Expiration du jeton d'actualisation - infobulle",
|
||||
"Right": "Right",
|
||||
"Rule": "Rule",
|
||||
"SAML Reply URL": "SAML Reply URL",
|
||||
"SAML metadata": "SAML metadata",
|
||||
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
|
||||
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
|
||||
@@ -432,9 +434,12 @@
|
||||
"CNY": "CNY",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Description": "Description",
|
||||
"Description - Tooltip": "Description - Tooltip",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail - Tooltip",
|
||||
"Edit Product": "Edit Product",
|
||||
"I have completed the payment": "I have completed the payment",
|
||||
"Image": "Image",
|
||||
"Image - Tooltip": "Image - Tooltip",
|
||||
"New Product": "New Product",
|
||||
@@ -443,6 +448,8 @@
|
||||
"Payment providers - Tooltip": "Payment providers - Tooltip",
|
||||
"Paypal": "Paypal",
|
||||
"Placing order...": "Placing order...",
|
||||
"Please provide your username in the remark": "Please provide your username in the remark",
|
||||
"Please scan the QR code to pay": "Please scan the QR code to pay",
|
||||
"Price": "Price",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Quantity": "Quantity",
|
||||
|
@@ -58,12 +58,14 @@
|
||||
"Please select a HTML file": "HTMLファイルを選択してください",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Redirect URL": "リダイレクトURL",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip",
|
||||
"Redirect URLs": "リダイレクトURL",
|
||||
"Redirect URLs - Tooltip": "List of redirect addresses after successful login",
|
||||
"Refresh token expire": "トークンの更新の期限が切れます",
|
||||
"Refresh token expire - Tooltip": "トークンの有効期限を更新する - ツールチップ",
|
||||
"Right": "Right",
|
||||
"Rule": "Rule",
|
||||
"SAML Reply URL": "SAML Reply URL",
|
||||
"SAML metadata": "SAML metadata",
|
||||
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
|
||||
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
|
||||
@@ -432,9 +434,12 @@
|
||||
"CNY": "CNY",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Description": "Description",
|
||||
"Description - Tooltip": "Description - Tooltip",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail - Tooltip",
|
||||
"Edit Product": "Edit Product",
|
||||
"I have completed the payment": "I have completed the payment",
|
||||
"Image": "Image",
|
||||
"Image - Tooltip": "Image - Tooltip",
|
||||
"New Product": "New Product",
|
||||
@@ -443,6 +448,8 @@
|
||||
"Payment providers - Tooltip": "Payment providers - Tooltip",
|
||||
"Paypal": "Paypal",
|
||||
"Placing order...": "Placing order...",
|
||||
"Please provide your username in the remark": "Please provide your username in the remark",
|
||||
"Please scan the QR code to pay": "Please scan the QR code to pay",
|
||||
"Price": "Price",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Quantity": "Quantity",
|
||||
|
@@ -58,12 +58,14 @@
|
||||
"Please select a HTML file": "Please select a HTML file",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Redirect URL": "Redirect URL",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip",
|
||||
"Redirect URLs": "Redirect URLs",
|
||||
"Redirect URLs - Tooltip": "List of redirect addresses after successful login",
|
||||
"Refresh token expire": "Refresh token expire",
|
||||
"Refresh token expire - Tooltip": "Refresh token expire - Tooltip",
|
||||
"Right": "Right",
|
||||
"Rule": "Rule",
|
||||
"SAML Reply URL": "SAML Reply URL",
|
||||
"SAML metadata": "SAML metadata",
|
||||
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
|
||||
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
|
||||
@@ -432,9 +434,12 @@
|
||||
"CNY": "CNY",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Description": "Description",
|
||||
"Description - Tooltip": "Description - Tooltip",
|
||||
"Detail": "Detail",
|
||||
"Detail - Tooltip": "Detail - Tooltip",
|
||||
"Edit Product": "Edit Product",
|
||||
"I have completed the payment": "I have completed the payment",
|
||||
"Image": "Image",
|
||||
"Image - Tooltip": "Image - Tooltip",
|
||||
"New Product": "New Product",
|
||||
@@ -443,6 +448,8 @@
|
||||
"Payment providers - Tooltip": "Payment providers - Tooltip",
|
||||
"Paypal": "Paypal",
|
||||
"Placing order...": "Placing order...",
|
||||
"Please provide your username in the remark": "Please provide your username in the remark",
|
||||
"Please scan the QR code to pay": "Please scan the QR code to pay",
|
||||
"Price": "Price",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Quantity": "Quantity",
|
||||
|
@@ -58,12 +58,14 @@
|
||||
"Please select a HTML file": "Пожалуйста, выберите HTML-файл",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Ссылка на страницу успешно скопирована в буфер обмена, пожалуйста, вставьте ее в окно инкогнито или другой браузер",
|
||||
"Redirect URL": "URL перенаправления",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip",
|
||||
"Redirect URLs": "Перенаправление URL",
|
||||
"Redirect URLs - Tooltip": "List of redirect addresses after successful login",
|
||||
"Refresh token expire": "Срок действия обновления токена истекает",
|
||||
"Refresh token expire - Tooltip": "Срок обновления токена истекает - Подсказка",
|
||||
"Right": "Right",
|
||||
"Rule": "правило",
|
||||
"SAML Reply URL": "SAML Reply URL",
|
||||
"SAML metadata": "Метаданные SAML",
|
||||
"SAML metadata - Tooltip": "Метаданные SAML - Подсказка",
|
||||
"SAML metadata URL copied to clipboard successfully": "Адрес метаданных SAML скопирован в буфер обмена",
|
||||
@@ -432,9 +434,12 @@
|
||||
"CNY": "CNY",
|
||||
"Currency": "Currency",
|
||||
"Currency - Tooltip": "Currency - Tooltip",
|
||||
"Description": "Description",
|
||||
"Description - Tooltip": "Description - Tooltip",
|
||||
"Detail": "Сведения",
|
||||
"Detail - Tooltip": "Detail - Tooltip",
|
||||
"Edit Product": "Редактирование продукта",
|
||||
"I have completed the payment": "I have completed the payment",
|
||||
"Image": "Изображение",
|
||||
"Image - Tooltip": "Image - Tooltip",
|
||||
"New Product": "Новый продукт",
|
||||
@@ -443,6 +448,8 @@
|
||||
"Payment providers - Tooltip": "Payment providers - Tooltip",
|
||||
"Paypal": "PayPal",
|
||||
"Placing order...": "Placing order...",
|
||||
"Please provide your username in the remark": "Please provide your username in the remark",
|
||||
"Please scan the QR code to pay": "Please scan the QR code to pay",
|
||||
"Price": "Цена",
|
||||
"Price - Tooltip": "Price - Tooltip",
|
||||
"Quantity": "Quantity",
|
||||
|
@@ -58,12 +58,14 @@
|
||||
"Please select a HTML file": "请选择一个HTML文件",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "提醒页面URL已成功复制到剪贴板,请粘贴到当前浏览器的隐身模式窗口或另一个浏览器访问",
|
||||
"Redirect URL": "重定向 URL",
|
||||
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "回复 URL (断言使用者服务 URL, 使用POST请求返回响应) - Tooltip",
|
||||
"Redirect URLs": "重定向 URLs",
|
||||
"Redirect URLs - Tooltip": "登录成功后重定向地址列表",
|
||||
"Refresh token expire": "Refresh Token过期",
|
||||
"Refresh token expire - Tooltip": "Refresh Token过期时间",
|
||||
"Right": "居右",
|
||||
"Rule": "规则",
|
||||
"SAML Reply URL": "SAML回复 URL",
|
||||
"SAML metadata": "SAML元数据",
|
||||
"SAML metadata - Tooltip": "SAML协议的元数据(Metadata)信息",
|
||||
"SAML metadata URL copied to clipboard successfully": "SAML元数据URL已成功复制到剪贴板",
|
||||
@@ -432,9 +434,12 @@
|
||||
"CNY": "人民币",
|
||||
"Currency": "币种",
|
||||
"Currency - Tooltip": "币种 - 工具提示",
|
||||
"Description": "描述",
|
||||
"Description - Tooltip": "描述 - 工具提示",
|
||||
"Detail": "详情",
|
||||
"Detail - Tooltip": "详情 - 工具提示",
|
||||
"Edit Product": "编辑商品",
|
||||
"I have completed the payment": "支付完成",
|
||||
"Image": "图片",
|
||||
"Image - Tooltip": "图片 - 工具提示",
|
||||
"New Product": "添加商品",
|
||||
@@ -443,6 +448,8 @@
|
||||
"Payment providers - Tooltip": "支付提供商 - 工具提示",
|
||||
"Paypal": "PayPal(贝宝)",
|
||||
"Placing order...": "正在下单...",
|
||||
"Please provide your username in the remark": "Please provide your username in the remark",
|
||||
"Please scan the QR code to pay": "请扫描二维码支付",
|
||||
"Price": "价格",
|
||||
"Price - Tooltip": "价格 - 工具提示",
|
||||
"Quantity": "库存",
|
||||
|
Reference in New Issue
Block a user