Compare commits

...

2 Commits

Author SHA1 Message Date
Wind Li
19f62a461b feat: fix SAML's redirectUrl and POST ProtocolBinding (#3498) 2025-01-13 20:55:37 +08:00
DacongDA
7ddc2778c0 feat: show error message when organization doesn't have default application in invitation edit page (#3495)
* fix: inform user when organization haven't default application in signup page

* fix: include org name in the error message
2025-01-12 22:48:21 +08:00
4 changed files with 23 additions and 22 deletions

View File

@@ -338,6 +338,9 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
} else if authnRequest.AssertionConsumerServiceURL == "" {
return "", "", "", fmt.Errorf("err: SAML request don't has attribute 'AssertionConsumerServiceURL' in <samlp:AuthnRequest>")
}
if authnRequest.ProtocolBinding == "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" {
method = "POST"
}
_, originBackend := getOriginFromHost(host)

View File

@@ -106,6 +106,22 @@ class InvitationEditPage extends React.Component {
});
}
copySignupLink() {
let defaultApplication;
if (this.state.invitation.owner === "built-in") {
defaultApplication = "app-built-in";
} else {
const selectedOrganization = Setting.getArrayItem(this.state.organizations, "name", this.state.invitation.owner);
defaultApplication = selectedOrganization.defaultApplication;
if (!defaultApplication) {
Setting.showMessage("error", i18next.t("invitation:You need to specify a default application for ") + selectedOrganization.name);
return;
}
}
copy(`${window.location.origin}/signup/${defaultApplication}?invitationCode=${this.state.invitation?.defaultCode}`);
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
}
renderInvitation() {
const isCreatedByPlan = this.state.invitation.tag === "auto_created_invitation_for_plan";
return (
@@ -114,16 +130,7 @@ class InvitationEditPage extends React.Component {
{this.state.mode === "add" ? i18next.t("invitation:New Invitation") : i18next.t("invitation:Edit Invitation")}&nbsp;&nbsp;&nbsp;&nbsp;
<Button onClick={() => this.submitInvitationEdit(false)}>{i18next.t("general:Save")}</Button>
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitInvitationEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
<Button style={{marginLeft: "20px"}} onClick={() => {
let defaultApplication;
if (this.state.invitation.owner === "built-in") {
defaultApplication = "app-built-in";
} else {
defaultApplication = Setting.getArrayItem(this.state.organizations, "name", this.state.invitation.owner).defaultApplication;
}
copy(`${window.location.origin}/signup/${defaultApplication}?invitationCode=${this.state.invitation?.defaultCode}`);
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
}}>
<Button style={{marginLeft: "20px"}} onClick={_ => this.copySignupLink()}>
{i18next.t("application:Copy signup page URL")}
</Button>
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteInvitation()}>{i18next.t("general:Cancel")}</Button> : null}
@@ -330,16 +337,7 @@ class InvitationEditPage extends React.Component {
<div style={{marginTop: "20px", marginLeft: "40px"}}>
<Button size="large" onClick={() => this.submitInvitationEdit(false)}>{i18next.t("general:Save")}</Button>
<Button style={{marginLeft: "20px"}} type="primary" size="large" onClick={() => this.submitInvitationEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
<Button style={{marginLeft: "20px"}} size="large" onClick={() => {
let defaultApplication;
if (this.state.invitation.owner === "built-in") {
defaultApplication = "app-built-in";
} else {
defaultApplication = Setting.getArrayItem(this.state.organizations, "name", this.state.invitation.owner).defaultApplication;
}
copy(`${window.location.origin}/signup/${defaultApplication}?invitationCode=${this.state.invitation?.defaultCode}`);
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
}}>
<Button style={{marginLeft: "20px"}} size="large" onClick={_ => this.copySignupLink()}>
{i18next.t("application:Copy signup page URL")}
</Button>
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} size="large" onClick={() => this.deleteInvitation()}>{i18next.t("general:Cancel")}</Button> : null}

View File

@@ -204,7 +204,7 @@ class AuthCallback extends React.Component {
}
const SAMLResponse = res.data;
const redirectUri = res.data2.redirectUrl;
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);
Setting.goToLink(`${redirectUri}${redirectUri.includes("?") ? "&" : "?"}SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);
}
}
} else {

View File

@@ -505,7 +505,7 @@ class LoginPage extends React.Component {
} else {
const SAMLResponse = res.data;
const redirectUri = res.data2.redirectUrl;
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);
Setting.goToLink(`${redirectUri}${redirectUri.includes("?") ? "&" : "?"}SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);
}
}
};