1
0
Fork 0
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:
Andrii Podriez 2024-07-25 13:51:34 +02:00 committed by al3xa23
parent 7ae0deee87
commit 41c5066c0c
8 changed files with 270 additions and 240 deletions

View file

@ -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;

View 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();

View file

@ -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;

View file

@ -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";

View file

@ -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>
);

View file

@ -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>

View file

@ -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';

View file

@ -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>