mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-04 13:20:19 +08:00
feat: support CAS with organizations and applications (#621)
This commit is contained in:

committed by
GitHub

parent
2023795f3c
commit
3cf1b990be
@ -18,6 +18,22 @@ module.exports = {
|
||||
'/.well-known/openid-configuration': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/cas/serviceValidate': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/cas/proxyValidate': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/cas/proxy': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/cas/validate': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -68,6 +68,7 @@ import i18next from 'i18next';
|
||||
import PromptPage from "./auth/PromptPage";
|
||||
import OdicDiscoveryPage from "./auth/OidcDiscoveryPage";
|
||||
import SamlCallback from './auth/SamlCallback';
|
||||
import CasLogout from "./auth/CasLogout";
|
||||
|
||||
const { Header, Footer } = Layout;
|
||||
|
||||
@ -642,7 +643,8 @@ class App extends Component {
|
||||
window.location.pathname.startsWith("/login") ||
|
||||
window.location.pathname.startsWith("/callback") ||
|
||||
window.location.pathname.startsWith("/prompt") ||
|
||||
window.location.pathname.startsWith("/forget");
|
||||
window.location.pathname.startsWith("/forget") ||
|
||||
window.location.pathname.startsWith("/cas");
|
||||
}
|
||||
|
||||
renderPage() {
|
||||
@ -654,6 +656,8 @@ class App extends Component {
|
||||
<Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/signup/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signup"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account)}} />}/>
|
||||
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signin"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account)}} />}/>
|
||||
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout clearAccount={() => this.setState({account: null})} {...props} />)} />
|
||||
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage type={"cas"} mode={"signup"} account={this.state.account} {...props} />)}} />
|
||||
<Route exact path="/callback" component={AuthCallback}/>
|
||||
<Route exact path="/callback/saml" component={SamlCallback}/>
|
||||
<Route exact path="/forget" render={(props) => this.renderHomeIfLoggedIn(<SelfForgetPage {...props} />)}/>
|
||||
|
@ -62,6 +62,14 @@ export function login(values, oAuthParams) {
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function loginCas(values, params) {
|
||||
return fetch(`${authConfig.serverUrl}/api/login?service=${params.service}`, {
|
||||
method: 'POST',
|
||||
credentials: "include",
|
||||
body: JSON.stringify(values),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
return fetch(`${authConfig.serverUrl}/api/logout`, {
|
||||
method: 'POST',
|
||||
|
68
web/src/auth/CasLogout.js
Normal file
68
web/src/auth/CasLogout.js
Normal file
@ -0,0 +1,68 @@
|
||||
// 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 from "react";
|
||||
import {Spin} from "antd";
|
||||
import {withRouter} from "react-router-dom";
|
||||
import * as AuthBackend from "./AuthBackend";
|
||||
import * as Setting from "../Setting";
|
||||
import i18next from "i18next";
|
||||
|
||||
class CasLogout extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
msg: null,
|
||||
};
|
||||
if (props.match?.params.casApplicationName !== undefined) {
|
||||
this.state.owner = props.match?.params.owner
|
||||
this.state.applicationName = props.match?.params.casApplicationName
|
||||
}
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
const params = new URLSearchParams(this.props.location.search);
|
||||
|
||||
AuthBackend.logout()
|
||||
.then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
Setting.showMessage("success", `Logged out successfully`);
|
||||
this.props.clearAccount()
|
||||
let redirectUri = res.data2;
|
||||
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
|
||||
Setting.goToLink(redirectUri);
|
||||
} else if (params.has("service")) {
|
||||
Setting.goToLink(params.get("service"))
|
||||
} else {
|
||||
Setting.goToLinkSoft(this, `/cas/${this.state.owner}/${this.state.applicationName}/login`);
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", `Failed to log out: ${res.msg}`);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{textAlign: "center"}}>
|
||||
{
|
||||
<Spin size="large" tip={i18next.t("login:Logging out...")} style={{paddingTop: "10%"}} />
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default withRouter(CasLogout);
|
@ -61,11 +61,16 @@ class LoginPage extends React.Component {
|
||||
validEmailOrPhone: false,
|
||||
validEmail: false,
|
||||
validPhone: false,
|
||||
owner: null,
|
||||
};
|
||||
if (this.state.type === "cas" && props.match?.params.casApplicationName !== undefined) {
|
||||
this.state.owner = props.match?.params.owner
|
||||
this.state.applicationName = props.match?.params.casApplicationName
|
||||
}
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
if (this.state.type === "login") {
|
||||
if (this.state.type === "login" || this.state.type === "cas") {
|
||||
this.getApplication();
|
||||
} else if (this.state.type === "code") {
|
||||
this.getApplicationLogin();
|
||||
@ -120,59 +125,85 @@ class LoginPage extends React.Component {
|
||||
onFinish(values) {
|
||||
const application = this.getApplicationObj();
|
||||
const ths = this;
|
||||
const oAuthParams = Util.getOAuthGetParameters();
|
||||
if (oAuthParams !== null && oAuthParams.responseType!= null && oAuthParams.responseType !== "") {
|
||||
values["type"] = oAuthParams.responseType
|
||||
}else{
|
||||
|
||||
//here we are supposed to judge whether casdoor is working as a oauth server or CAS server
|
||||
if (this.state.type === "cas") {
|
||||
//cas
|
||||
const casParams = Util.getCasParameters()
|
||||
values["type"] = this.state.type;
|
||||
}
|
||||
values["phonePrefix"] = this.getApplicationObj()?.organizationObj.phonePrefix;
|
||||
|
||||
AuthBackend.login(values, oAuthParams)
|
||||
.then((res) => {
|
||||
AuthBackend.loginCas(values, casParams).then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
const responseType = values["type"];
|
||||
if (responseType === "login") {
|
||||
Util.showMessage("success", `Logged in successfully`);
|
||||
|
||||
const link = Setting.getFromLink();
|
||||
Setting.goToLink(link);
|
||||
} else if (responseType === "code") {
|
||||
const code = res.data;
|
||||
const concatChar = oAuthParams?.redirectUri?.includes('?') ? '&' : '?';
|
||||
|
||||
if (Setting.hasPromptPage(application)) {
|
||||
AuthBackend.getAccount("")
|
||||
.then((res) => {
|
||||
let account = null;
|
||||
if (res.status === "ok") {
|
||||
account = res.data;
|
||||
account.organization = res.data2;
|
||||
|
||||
this.onUpdateAccount(account);
|
||||
|
||||
if (Setting.isPromptAnswered(account, application)) {
|
||||
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
|
||||
} else {
|
||||
Setting.goToLinkSoft(ths, `/prompt/${application.name}?redirectUri=${oAuthParams.redirectUri}&code=${code}&state=${oAuthParams.state}`);
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", `Failed to sign in: ${res.msg}`);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
|
||||
}
|
||||
|
||||
// Util.showMessage("success", `Authorization code: ${res.data}`);
|
||||
} else if (responseType === "token" || responseType === "id_token") {
|
||||
const accessToken = res.data;
|
||||
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
|
||||
let msg = "Logged in successfully. "
|
||||
if (casParams.service == "") {
|
||||
//If service was not specified, CAS MUST display a message notifying the client that it has successfully initiated a single sign-on session.
|
||||
msg += "Now you can visit apps protected by casdoor."
|
||||
}
|
||||
Util.showMessage("success", msg);
|
||||
if (casParams.service !== "") {
|
||||
let st = res.data
|
||||
window.location.href = casParams.service + "?ticket=" + st
|
||||
}
|
||||
|
||||
} else {
|
||||
Util.showMessage("error", `Failed to log in: ${res.msg}`);
|
||||
}
|
||||
});
|
||||
})
|
||||
} else {
|
||||
//oauth
|
||||
const oAuthParams = Util.getOAuthGetParameters();
|
||||
if (oAuthParams !== null && oAuthParams.responseType != null && oAuthParams.responseType !== "") {
|
||||
values["type"] = oAuthParams.responseType
|
||||
}else{
|
||||
values["type"] = this.state.type;
|
||||
}
|
||||
values["phonePrefix"] = this.getApplicationObj()?.organizationObj.phonePrefix;
|
||||
|
||||
AuthBackend.login(values, oAuthParams)
|
||||
.then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
const responseType = values["type"];
|
||||
if (responseType === "login") {
|
||||
Util.showMessage("success", `Logged in successfully`);
|
||||
|
||||
const link = Setting.getFromLink();
|
||||
Setting.goToLink(link);
|
||||
} else if (responseType === "code") {
|
||||
const code = res.data;
|
||||
const concatChar = oAuthParams?.redirectUri?.includes('?') ? '&' : '?';
|
||||
|
||||
if (Setting.hasPromptPage(application)) {
|
||||
AuthBackend.getAccount("")
|
||||
.then((res) => {
|
||||
let account = null;
|
||||
if (res.status === "ok") {
|
||||
account = res.data;
|
||||
account.organization = res.data2;
|
||||
|
||||
this.onUpdateAccount(account);
|
||||
|
||||
if (Setting.isPromptAnswered(account, application)) {
|
||||
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
|
||||
} else {
|
||||
Setting.goToLinkSoft(ths, `/prompt/${application.name}?redirectUri=${oAuthParams.redirectUri}&code=${code}&state=${oAuthParams.state}`);
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", `Failed to sign in: ${res.msg}`);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
|
||||
}
|
||||
|
||||
// Util.showMessage("success", `Authorization code: ${res.data}`);
|
||||
} else if (responseType === "token" || responseType === "id_token") {
|
||||
const accessToken = res.data;
|
||||
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
|
||||
}
|
||||
} else {
|
||||
Util.showMessage("error", `Failed to log in: ${res.msg}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getSigninButton(type) {
|
||||
|
@ -79,6 +79,18 @@ function getRefinedValue(value){
|
||||
return (value === null)? "" : value
|
||||
}
|
||||
|
||||
export function getCasParameters(params){
|
||||
const queries = (params !== undefined) ? params : new URLSearchParams(window.location.search);
|
||||
const service = getRefinedValue(queries.get("service"))
|
||||
const renew = getRefinedValue(queries.get("renew"))
|
||||
const gateway = getRefinedValue(queries.get("gateway"))
|
||||
return {
|
||||
service: service,
|
||||
renew: renew,
|
||||
gateway: gateway,
|
||||
}
|
||||
}
|
||||
|
||||
export function getOAuthGetParameters(params) {
|
||||
const queries = (params !== undefined) ? params : new URLSearchParams(window.location.search);
|
||||
const clientId = getRefinedValue(queries.get("client_id"));
|
||||
|
Reference in New Issue
Block a user