mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
updated logout feature
Signed-off-by: Andrii Podriez <andrey5577990@gmail.com>
This commit is contained in:
parent
7ae0deee87
commit
41c5066c0c
8 changed files with 270 additions and 240 deletions
71
src/app.js
71
src/app.js
|
@ -20,27 +20,22 @@ import { DndProvider } from 'react-dnd';
|
|||
import { HTML5Backend }from 'react-dnd-html5-backend';
|
||||
import NotificationSystem from 'react-notification-system';
|
||||
import { Redirect, Route } from 'react-router-dom';
|
||||
import jwt from 'jsonwebtoken'
|
||||
|
||||
import AppDispatcher from './common/app-dispatcher';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import NotificationsDataManager from './common/data-managers/notifications-data-manager';
|
||||
|
||||
import Home from './common/home';
|
||||
import Header from './common/header';
|
||||
import Menu from './common/menu';
|
||||
|
||||
import InfrastructureComponent from './pages/infrastructure/ic';
|
||||
import Dashboard from './dashboard/dashboard';
|
||||
import Scenarios from './scenario/scenarios';
|
||||
import Scenario from './scenario/scenario';
|
||||
import Users from './user/users';
|
||||
import User from './user/user';
|
||||
import Scenarios from './pages/scenarios/scenarios';
|
||||
import APIBrowser from './common/api-browser';
|
||||
|
||||
|
||||
import Scenario from './pages/scenarios/scenario';
|
||||
import Users from './pages/users/users';
|
||||
import Dashboard from './pages/dashboards/dashboard';
|
||||
import Account from './pages/account/account';
|
||||
import './styles/app.css';
|
||||
import './styles/login.css';
|
||||
import branding from './branding/branding';
|
||||
import Logout from './pages/login/logout';
|
||||
|
||||
import Infrastructure from './pages/infrastructure/infrastructure'
|
||||
|
||||
|
@ -54,31 +49,7 @@ class App extends React.Component {
|
|||
|
||||
componentDidMount() {
|
||||
NotificationsDataManager.setSystem(this.refs.notificationSystem);
|
||||
|
||||
AppDispatcher.dispatch({
|
||||
type: 'config/load',
|
||||
});
|
||||
|
||||
// if token stored locally, we are already logged-in
|
||||
let token = localStorage.getItem("token");
|
||||
if (token != null && token !== '') {
|
||||
|
||||
let isExpired = this.tokenIsExpired(token);
|
||||
if (isExpired) {
|
||||
console.log("Token expired")
|
||||
AppDispatcher.dispatch({
|
||||
type: 'users/logout'
|
||||
});
|
||||
} else {
|
||||
let currentUser = JSON.parse(localStorage.getItem("currentUser"));
|
||||
console.log("Logged-in as user ", currentUser.username)
|
||||
AppDispatcher.dispatch({
|
||||
type: 'users/logged-in',
|
||||
token: token,
|
||||
currentUser: currentUser
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tokenIsExpired(token){
|
||||
|
@ -114,10 +85,21 @@ class App extends React.Component {
|
|||
<Route exact path="/" component={Home} />
|
||||
{ pages.home ? <Route path="/home" component={Home} /> : '' }
|
||||
{ pages.scenarios ? <>
|
||||
<Route exact path="/scenarios" component={Scenarios}/>
|
||||
<Route path="/scenarios/:scenario" component={Scenario} />
|
||||
<Route path="/dashboards/:dashboard" component={Dashboard} />
|
||||
<Route path="/dashboards-new/:dashboard" component={Dashboard} />
|
||||
<Route exact path="/scenarios">
|
||||
<Scenarios />
|
||||
</Route>
|
||||
<Route exact path="/logout">
|
||||
<Logout />
|
||||
</Route>
|
||||
<Route path="/scenarios/:scenario">
|
||||
<Scenario />
|
||||
</Route>
|
||||
<Route path="/dashboards/:dashboard">
|
||||
<Dashboard />
|
||||
</Route>
|
||||
<Route path="/dashboards-new/:dashboard">
|
||||
<Dashboard />
|
||||
</Route>
|
||||
</>
|
||||
: '' }
|
||||
{ currentUser.role === "Admin" || pages.infrastructure ? <>
|
||||
|
@ -129,9 +111,11 @@ class App extends React.Component {
|
|||
</Route>
|
||||
</>
|
||||
: '' }
|
||||
{ pages.account ? <Route path="/account" component={User} /> : '' }
|
||||
{ pages.account ? <Route path="/account"><Account /></Route> : '' }
|
||||
{ currentUser.role === "Admin" ?
|
||||
<Route path="/users" component={Users} />
|
||||
<Route path="/users">
|
||||
<Users />
|
||||
</Route>
|
||||
: '' }
|
||||
{ currentUser.role === "Admin" || pages.api ?
|
||||
<Route path="/api" component={APIBrowser} />
|
||||
|
@ -145,5 +129,4 @@ class App extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
export default App
|
||||
export default App;
|
||||
|
|
91
src/common/api/login-store.js
Normal file
91
src/common/api/login-store.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* This file is part of VILLASweb.
|
||||
*
|
||||
* VILLASweb is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* VILLASweb is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with VILLASweb. If not, see <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
import { ReduceStore } from 'flux/utils';
|
||||
|
||||
import AppDispatcher from '../common/app-dispatcher';
|
||||
import UsersDataManager from './users-data-manager';
|
||||
import ICDataDataManager from '../ic/ic-data-data-manager';
|
||||
import ConfigReader from '../config-reader';
|
||||
|
||||
class LoginStore extends ReduceStore {
|
||||
constructor() {
|
||||
super(AppDispatcher);
|
||||
}
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
currentUser: null,
|
||||
token: null,
|
||||
loginMessage: null,
|
||||
config: null,
|
||||
};
|
||||
}
|
||||
|
||||
reduce(state, action) {
|
||||
switch (action.type) {
|
||||
case 'config/load':
|
||||
ConfigReader.loadConfig();
|
||||
return state;
|
||||
|
||||
case 'config/loaded':
|
||||
return Object.assign({}, state, { config: action.data });
|
||||
|
||||
case 'config/load-error':
|
||||
return Object.assign({}, state, { config: null});
|
||||
|
||||
case 'users/login':
|
||||
UsersDataManager.login(action.username, action.password);
|
||||
return Object.assign({}, state, { loginMessage: null });
|
||||
|
||||
case 'users/extlogin':
|
||||
UsersDataManager.login();
|
||||
return Object.assign({}, state, { loginMessage: null });
|
||||
|
||||
case 'users/logout':
|
||||
// disconnect from all infrastructure components
|
||||
ICDataDataManager.closeAll();
|
||||
//remove token and current user from local storage
|
||||
localStorage.clear();
|
||||
|
||||
// delete user, token and loginMessage
|
||||
return Object.assign({}, state, { token: null, currentUser: null, loginMessage: null});
|
||||
|
||||
case 'users/logged-in':
|
||||
// save login data in local storage and loginStore
|
||||
let newState = state
|
||||
if (action.token != null){
|
||||
localStorage.setItem('token', action.token);
|
||||
newState = Object.assign({}, state, {token: action.token})
|
||||
}
|
||||
localStorage.setItem('currentUser', JSON.stringify(action.currentUser));
|
||||
return Object.assign({}, newState, { currentUser: action.currentUser});
|
||||
|
||||
case 'users/login-error':
|
||||
if (action.error && !action.error.handled) {
|
||||
// If it was an error and hasn't been handled, the credentials must have been wrong.
|
||||
state = Object.assign({}, state, { loginMessage: 'Wrong credentials! Please try again.' });
|
||||
}
|
||||
return state;
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new LoginStore();
|
|
@ -16,210 +16,187 @@
|
|||
******************************************************************************/
|
||||
|
||||
import React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import branding from '../branding/branding';
|
||||
import { Container } from 'flux/utils';
|
||||
import LoginStore from '../user/login-store';
|
||||
import AppDispatcher from './app-dispatcher';
|
||||
import { useGetConfigQuery } from '../store/apiSlice';
|
||||
import { currentUser } from '../localStorage';
|
||||
|
||||
const SideBarMenu = (props) => {
|
||||
|
||||
class SidebarMenu extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const values = branding.values;
|
||||
const [isExternalAuth, setIsExternalAuth] = useState(false);
|
||||
const [logoutLink, setLogoutLink] = useState('');
|
||||
|
||||
this.state = {
|
||||
externalAuth: false,
|
||||
logoutLink: "",
|
||||
const {data: configRes} = useGetConfigQuery();
|
||||
|
||||
useEffect(() => {
|
||||
if(configRes) {
|
||||
setLogoutLink(configRes.authentication.logout_url);
|
||||
}
|
||||
}, [configRes])
|
||||
|
||||
const logout = async () => {
|
||||
|
||||
}
|
||||
|
||||
static getStores() {
|
||||
return [LoginStore]
|
||||
}
|
||||
const getLinks = () => {
|
||||
let links = [];
|
||||
|
||||
static calculateState(prevState, props) {
|
||||
let config = LoginStore.getState().config;
|
||||
let externalauth = _.get(config, ['authentication', 'external', 'enabled']);
|
||||
let logout_url = _.get(config, ['authentication', 'logout_url']);
|
||||
|
||||
if (externalauth && logout_url) {
|
||||
return {
|
||||
externalAuth: true,
|
||||
logoutLink: logout_url,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
externalAuth: false,
|
||||
logoutLink: "/logout",
|
||||
}
|
||||
}
|
||||
|
||||
logout() {
|
||||
AppDispatcher.dispatch({
|
||||
type: 'users/logout'
|
||||
});
|
||||
// The Login Store and local storage are deleted automatically
|
||||
}
|
||||
|
||||
render() {
|
||||
const values = branding.values;
|
||||
let links = []
|
||||
if (values.links) {
|
||||
Object.keys(values.links).forEach(key => {
|
||||
links.push(<li key={key}><a href={values.links[key]} title={key}>{key}</a></li>);
|
||||
})
|
||||
}
|
||||
var logoStyle = { width: 110, margin: 'auto' };
|
||||
var logo = branding.getLogo(logoStyle);
|
||||
|
||||
return (
|
||||
<div className="menucontainer">
|
||||
{ logo ?
|
||||
<div className="menulogo">
|
||||
{logo}
|
||||
</div>
|
||||
: ''
|
||||
}
|
||||
<div className="menu">
|
||||
<h2>Menu</h2>
|
||||
return links;
|
||||
}
|
||||
|
||||
{this.state.externalAuth ?
|
||||
<ul>
|
||||
<li hidden={!values.pages.home}>
|
||||
return (
|
||||
<div className="menucontainer">
|
||||
{ branding.getLogo() ?
|
||||
<div className="menulogo">
|
||||
{branding.getLogo({ width: 110, margin: 'auto' })}
|
||||
</div>
|
||||
: ''
|
||||
}
|
||||
<div className="menu">
|
||||
<h2>Menu</h2>
|
||||
|
||||
{isExternalAuth ?
|
||||
<ul>
|
||||
<li hidden={!values.pages.home}>
|
||||
<NavLink
|
||||
to="/home"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Home">
|
||||
Home
|
||||
</NavLink>
|
||||
</li>
|
||||
<li hidden={!values.pages.scenarios}>
|
||||
<NavLink
|
||||
to="/scenarios"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Scenarios">
|
||||
Scenarios
|
||||
</NavLink>
|
||||
</li>
|
||||
{currentUser.role === 'Admin' || values.pages.infrastructure ?
|
||||
<li>
|
||||
<NavLink
|
||||
to="/home"
|
||||
to="/infrastructure"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Home">
|
||||
Home
|
||||
title="Infrastructure">
|
||||
Infrastructure
|
||||
</NavLink>
|
||||
</li>
|
||||
<li hidden={!values.pages.scenarios}>
|
||||
</li> : ''
|
||||
}
|
||||
{currentUser.role === 'Admin' ?
|
||||
<li>
|
||||
<NavLink
|
||||
to="/scenarios"
|
||||
to="/users"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Scenarios">
|
||||
Scenarios
|
||||
title="Users">
|
||||
Users
|
||||
</NavLink>
|
||||
</li>
|
||||
{this.props.currentRole === 'Admin' || values.pages.infrastructure ?
|
||||
<li>
|
||||
<NavLink
|
||||
to="/infrastructure"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Infrastructure">
|
||||
Infrastructure
|
||||
</NavLink>
|
||||
</li> : ''
|
||||
}
|
||||
{this.props.currentRole === 'Admin' ?
|
||||
<li>
|
||||
<NavLink
|
||||
to="/users"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Users">
|
||||
Users
|
||||
</NavLink>
|
||||
</li> : ''
|
||||
}
|
||||
<li hidden={!values.pages.account}>
|
||||
</li> : ''
|
||||
}
|
||||
<li hidden={!values.pages.account}>
|
||||
<NavLink
|
||||
to="/account"
|
||||
title="Account">
|
||||
Account
|
||||
</NavLink>
|
||||
</li>
|
||||
<a
|
||||
onClick={logout()}
|
||||
href={''}>
|
||||
Logout
|
||||
</a>
|
||||
<li hidden={!values.pages.api}>
|
||||
<NavLink
|
||||
to="/api"
|
||||
title="API Browser">
|
||||
API Browser
|
||||
</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
: <ul>
|
||||
<li hidden={!values.pages.home}>
|
||||
<NavLink
|
||||
to="/home"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Home">
|
||||
Home
|
||||
</NavLink>
|
||||
</li>
|
||||
<li hidden={!values.pages.scenarios}>
|
||||
<NavLink
|
||||
to="/scenarios"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Scenarios">
|
||||
Scenarios
|
||||
</NavLink>
|
||||
</li>
|
||||
{currentUser.role === 'Admin' || values.pages.infrastructure ?
|
||||
<li>
|
||||
<NavLink
|
||||
to="/account"
|
||||
title="Account">
|
||||
Account
|
||||
to="/infrastructure"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Infrastructure">
|
||||
Infrastructure
|
||||
</NavLink>
|
||||
</li>
|
||||
<a
|
||||
onClick={this.logout.bind(this)}
|
||||
href={this.state.logoutLink}>
|
||||
</li> : ''
|
||||
}
|
||||
{currentUser.role === 'Admin' ?
|
||||
<li>
|
||||
<NavLink
|
||||
to="/users"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Users">
|
||||
Users
|
||||
</NavLink>
|
||||
</li> : ''
|
||||
}
|
||||
<li hidden={!values.pages.account}>
|
||||
<NavLink
|
||||
to="/account"
|
||||
title="Account">
|
||||
Account
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink
|
||||
to="/logout"
|
||||
title="Logout">
|
||||
Logout
|
||||
</a>
|
||||
<li hidden={!values.pages.api}>
|
||||
</NavLink>
|
||||
</li>
|
||||
{currentUser.role === 'Admin' || values.pages.api ?
|
||||
<li>
|
||||
<NavLink
|
||||
to="/api"
|
||||
title="API Browser">
|
||||
API Browser
|
||||
</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
: <ul>
|
||||
<li hidden={!values.pages.home}>
|
||||
<NavLink
|
||||
to="/home"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Home">
|
||||
Home
|
||||
</NavLink>
|
||||
</li>
|
||||
<li hidden={!values.pages.scenarios}>
|
||||
<NavLink
|
||||
to="/scenarios"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Scenarios">
|
||||
Scenarios
|
||||
</NavLink>
|
||||
</li>
|
||||
{this.props.currentRole === 'Admin' || values.pages.infrastructure ?
|
||||
<li>
|
||||
<NavLink
|
||||
to="/infrastructure"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Infrastructure">
|
||||
Infrastructure
|
||||
</NavLink>
|
||||
</li> : ''
|
||||
}
|
||||
{this.props.currentRole === 'Admin' ?
|
||||
<li>
|
||||
<NavLink
|
||||
to="/users"
|
||||
className={({isActive}) => (isActive ? "active" : "")}
|
||||
title="Users">
|
||||
Users
|
||||
</NavLink>
|
||||
</li> : ''
|
||||
}
|
||||
<li hidden={!values.pages.account}>
|
||||
<NavLink
|
||||
to="/account"
|
||||
title="Account">
|
||||
Account
|
||||
</NavLink>
|
||||
</li>
|
||||
<li>
|
||||
<NavLink
|
||||
to={this.state.logoutLink}
|
||||
title="Logout">
|
||||
Logout
|
||||
</NavLink>
|
||||
</li>
|
||||
{this.props.currentRole === 'Admin' || values.pages.api ?
|
||||
<li>
|
||||
<NavLink
|
||||
to="/api"
|
||||
title="API Browser">
|
||||
API Browser
|
||||
</NavLink>
|
||||
</li > : ''
|
||||
}
|
||||
</ul>}
|
||||
|
||||
{
|
||||
links.length > 0 ?
|
||||
<div>
|
||||
<br></br>
|
||||
<h4> Links</h4>
|
||||
<ul> {links} </ul>
|
||||
</div>
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
|
||||
</li> : ''
|
||||
}
|
||||
</ul>}
|
||||
|
||||
{
|
||||
getLinks().length > 0 ?
|
||||
<div>
|
||||
<br></br>
|
||||
<h4> Links</h4>
|
||||
<ul> {getLinks()} </ul>
|
||||
</div>
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let fluxContainerConverter = require('../common/FluxContainerConverter');
|
||||
export default Container.create(fluxContainerConverter.convert(SidebarMenu));
|
||||
export default SideBarMenu;
|
||||
|
|
|
@ -17,13 +17,9 @@
|
|||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
import Router from "./router";
|
||||
|
||||
// Redux
|
||||
import { Provider } from "react-redux";
|
||||
import {store} from "./store/index";
|
||||
|
||||
import "bootstrap/dist/css/bootstrap.css";
|
||||
import "./styles/index.css";
|
||||
|
||||
|
|
|
@ -17,8 +17,7 @@
|
|||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Form, Button, Col } from 'react-bootstrap';
|
||||
import RecoverPassword from '../../user/recover-password'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { login } from '../../store/userSlice';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
@ -84,7 +83,7 @@ const LoginForm = (props) => {
|
|||
</Col>
|
||||
</Form.Group>
|
||||
|
||||
<RecoverPassword show={forgottenPassword} onClose={() => setForgottenPassword(false)} sessionToken={props.sessionToken} />
|
||||
{/* <RecoverPassword show={forgottenPassword} onClose={() => setForgottenPassword(false)} sessionToken={props.sessionToken} /> */}
|
||||
</Form>
|
||||
);
|
||||
|
||||
|
|
|
@ -19,30 +19,18 @@ import React, {useEffect, useRef} from 'react';
|
|||
import { NavbarBrand } from 'react-bootstrap';
|
||||
import NotificationSystem from 'react-notification-system';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import LoginForm from './login-form';
|
||||
import Header from '../../common/header';
|
||||
import NotificationsDataManager from '../../common/data-managers/notifications-data-manager';
|
||||
import AppDispatcher from '../../common/app-dispatcher';
|
||||
import branding from '../../branding/branding';
|
||||
|
||||
const Login = (props) => {
|
||||
|
||||
const notificationSystem = useRef()
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
NotificationsDataManager.setSystem(notificationSystem);
|
||||
|
||||
console.log("redirected to login", currentUser)
|
||||
|
||||
// load config in case the user goes directly to /login
|
||||
// otherwise it will be loaded in app constructor
|
||||
AppDispatcher.dispatch({
|
||||
type: 'config/load',
|
||||
});
|
||||
}, []);
|
||||
|
||||
const config = null
|
||||
|
@ -63,7 +51,6 @@ const Login = (props) => {
|
|||
|
||||
<div className="login-container">
|
||||
<NavbarBrand>Login</NavbarBrand>
|
||||
|
||||
<LoginForm loginMessage={loginMessage} config={config} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import React, {useEffect} from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { logout } from '../../store/userSlice';
|
||||
|
||||
|
|
|
@ -20,14 +20,12 @@ import { BrowserRouter, Route, Switch } from "react-router-dom";
|
|||
import App from "./app";
|
||||
import Login from "./pages/login/login";
|
||||
import Logout from "./pages/login/logout";
|
||||
import LoginComplete from "./user/login-complete";
|
||||
|
||||
class Root extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Switch>
|
||||
<Route path="/login/complete" component={LoginComplete} />
|
||||
<Route path='/login'>
|
||||
<Login />
|
||||
</Route>
|
||||
|
|
Loading…
Add table
Reference in a new issue