From 79ea5ef0c9d34f7e5bd858a772e66e02b8d1a1bd Mon Sep 17 00:00:00 2001 From: Ramesh Kumar <ramesh.p@matriotsolutions.com> Date: Tue, 27 Jul 2021 10:27:34 +0530 Subject: [PATCH] TMSS-462: Button disabled while refreshing page issue solved. Direct access to URL when don't have permissions is restricted. --- SAS/TMSS/frontend/tmss_webapp/src/App.js | 34 +++++------- .../tmss_webapp/src/authenticate/auth.js | 6 +- .../src/components/AuthComponent.js | 52 ++++++++++++++++++ .../src/components/ProtectedRouteComponent.js | 55 +++++++++++++++++++ .../src/routes/Scheduling/index.js | 17 +++--- .../frontend/tmss_webapp/src/routes/index.js | 22 +++++--- 6 files changed, 147 insertions(+), 39 deletions(-) create mode 100644 SAS/TMSS/frontend/tmss_webapp/src/components/AuthComponent.js create mode 100644 SAS/TMSS/frontend/tmss_webapp/src/components/ProtectedRouteComponent.js diff --git a/SAS/TMSS/frontend/tmss_webapp/src/App.js b/SAS/TMSS/frontend/tmss_webapp/src/App.js index 18f8e6f2941..c7e00093629 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/App.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/App.js @@ -26,6 +26,8 @@ import { CustomDialog } from './layout/components/CustomDialog'; import AuthStore from './authenticate/auth.store'; import {Provider} from "react-redux"; +import AuthComponent from './components/AuthComponent'; + const { publish, subscribe } = pubsub(); @@ -47,14 +49,14 @@ class App extends Component { mobileMenuActive: localStorage.getItem('mobileMenuActive') === 'true' ? true : false, authenticated: Auth.isAuthenticated(), findObjectPlaceholder: 'Sub Task', - redirect: (Auth.isAuthenticated() && window.location.pathname === "/login")?"/":window.location.pathname + redirect: (Auth.isAuthenticated() && window.location.pathname === "/login")?"/":window.location.pathname, + isLogin: true }; this.onWrapperClick = this.onWrapperClick.bind(this); this.onToggleMenu = this.onToggleMenu.bind(this); this.onSidebarClick = this.onSidebarClick.bind(this); this.onMenuItemClick = this.onMenuItemClick.bind(this); this.setPageTitle = this.setPageTitle.bind(this); - this.loggedIn = this.loggedIn.bind(this); this.logout = this.logout.bind(this); this.validateAndLogout = this.validateAndLogout.bind(this); this.setSearchField = this.setSearchField.bind(this); @@ -133,17 +135,13 @@ class App extends Component { * Callback function from login page to set the authentication state to true amd redirect to the * original requested URL. */ - loggedIn() { - const redirect = this.state.redirect; - this.setState({authenticated: true, redirect: redirect==="/login"?"/":redirect}); - } /** * Logout and redirect to login page. */ logout() { Auth.logout(); - this.setState({authenticated: false, redirect:"/"}); + this.setState({ redirect:"/", isLogin: false}); } /** @@ -253,10 +251,11 @@ class App extends Component { <Provider store={AuthStore}> {/* <div className={wrapperClass} onClick={this.onWrapperClick}> */} <div className={wrapperClass}> - {/* Load main routes and application only if the application is authenticated */} - {this.state.authenticated && <> + {this.state.redirect && + // <AuthComponent isLogin = {this.state.isLogin}> + <AuthComponent> <AppTopbar onToggleMenu={this.onToggleMenu} isLoggedIn={this.state.authenticated} @@ -268,24 +267,17 @@ class App extends Component { <AppMenu model={this.menu} toggleDirtyDialog={this.toggleDirtyDialog} isEditDirty={this.state.isEditDirty} onMenuItemClick={this.onMenuItemClick} layoutMode={this.state.la} active={this.state.menuActive}/> <div className="layout-main"> {this.state.redirect && - <Redirect to={{pathname: this.state.redirect}} />} + <Redirect to={{pathname: this.state.redirect, state: {userrole: this.state.userrole}}} />} <AppBreadcrumb setPageTitle={this.setPageTitle} section={this.state.currentMenu} onBreadcrumbClick={this.onBreadcrumbClick} /> <RoutedContent /> </div> - </Router> + </Router> + <AppFooter></AppFooter> + </AuthComponent> + } </> - } - {/* If not authenticated, show only login page */} - {!this.state.authenticated && - <> - <Router basename={ this.state.currentPath }> - <Redirect to={{pathname: "/login"}} /> - <Login onLogin={this.loggedIn} /> - </Router> - </> - } <CustomDialog type="confirmation" visible={this.state.showDirtyDialog} width="40vw" header={'Confirmation'} message={'Do you want to leave this page? Your changes may not be saved.'} content={''} onClose={this.close} onCancel={this.close} onSubmit={this.cancelEdit}> diff --git a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js index 4f66abbf5e0..27e43019029 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/authenticate/auth.js @@ -7,17 +7,17 @@ const axios = require('axios'); */ const Auth = { /** To check if user already logged in and the token is available in the browser local storage */ - isAuthenticated: () => { + isAuthenticated: async () => { let user = localStorage.getItem("user"); if (user) { user = JSON.parse(user); if (user.token) { axios.defaults.headers.common['Authorization'] = `Token ${user.token}`; - PermissionStackUtil.getPermissions(true); + await PermissionStackUtil.getPermissions(true); return true; } } - PermissionStackUtil.getPermissions(false); + await PermissionStackUtil.getPermissions(false); return false; }, /** Gets user details from browser local storage */ diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/AuthComponent.js b/SAS/TMSS/frontend/tmss_webapp/src/components/AuthComponent.js new file mode 100644 index 00000000000..84ed71533f3 --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/AuthComponent.js @@ -0,0 +1,52 @@ +import React, {Component} from 'react'; +import { BrowserRouter as Router } from 'react-router-dom'; +import { Redirect} from 'react-router-dom'; +import Auth from '../authenticate/auth' +import { Login } from '../authenticate/login'; + +class AuthComponent extends Component { + + constructor(props) { + super(props) + this.state = { + authenticated: false, + isLoginProgress : true + } + this.loggedIn = this.loggedIn.bind(this); + } + + async componentDidMount() { + const authenticate = await Auth.isAuthenticated(); + this.setState({authenticated: authenticate, isLoginProgress: authenticate}); + } + + async componentDidUpdate(prevProp, prevState) { + const authenticate = await Auth.isAuthenticated(); + if(prevState.authenticated != authenticate){ + this.setState({authenticated: authenticate, isLogin: authenticate}); + } + } + + loggedIn() { + this.setState({authenticated: true, redirect: "/", isLoginProgress: true}); + } + + render() { + return( + <> + { this.state.authenticated ? this.props.children: + <> + {!this.state.isLoginProgress && + <Router basename={ "/" }> + <Redirect to={{pathname: "/login"}} /> + <Login onLogin={this.loggedIn} /> + </Router> + }; + </> + } + </> + ) + } +} + +export default AuthComponent; \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/ProtectedRouteComponent.js b/SAS/TMSS/frontend/tmss_webapp/src/components/ProtectedRouteComponent.js new file mode 100644 index 00000000000..33c2c157d7b --- /dev/null +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/ProtectedRouteComponent.js @@ -0,0 +1,55 @@ +import React, { Component } from "react"; +import { + Route, + Redirect, +} from 'react-router-dom'; +import AuthStore from '../authenticate/auth.store'; +import AuthUtil from "../utils/auth.util"; + +class ProtectedRoute extends Component{ + + constructor(props){ + super(props) + this.state={ + permission_set: AuthStore.getState() + } + } + + async componentDidMount() { + const permission = await AuthUtil.getUserRolePermission(); + this.setState({permission_set: permission}); + } + + hasPermission() { + const permission = this.props.permissions + if(typeof(permission[0]) !== undefined && permission[1] !== undefined) { + if(this.state.permission_set['userRolePermission'][permission[0]][permission[1]]) { + return true; + } else { + return false; + } + } + else { + return true; + } + } + + render() { + const { name, component, path, exact, permissions } = this.props; + if(permissions){ + if(this.hasPermission()){ + return <Route path={path} name={name} component={component} /> + } + else { + return <Redirect to={{ + pathname: '/access-denied', + state: { from: this.props.location } + }}/> + } + } else { + return <Route path={path} name={name} component={component} /> + } + } +} + +export default ProtectedRoute; \ No newline at end of file diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/index.js index ccb826e083d..c0662b20d50 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/index.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Scheduling/index.js @@ -7,9 +7,10 @@ import PageHeader from '../../layout/components/PageHeader'; import { appGrowl } from '../../layout/components/AppGrowl'; import { CustomDialog } from '../../layout/components/CustomDialog'; import AuthUtil from '../../utils/auth.util'; +import AuthStore from '../../authenticate/auth.store'; export class Scheduling extends Component { - constructor(props){ + constructor(props){ super(props); this.state = { scheduleunit: [], @@ -17,7 +18,8 @@ export class Scheduling extends Component { isLoading:false, redirect: '', dialog: {header: 'Confirm', detail: 'Do you want to create blueprints for the selected drafts?'}, - dialogVisible: false + dialogVisible: false, + userrole : AuthStore.getState() }; this.access_dined_message = "Don't have permission"; this.optionsMenu = React.createRef(); @@ -29,8 +31,7 @@ export class Scheduling extends Component { async componentDidMount() { const permission = await AuthUtil.getUserRolePermission(); - this.setState({userrole: permission}); - //this.getUserRolePermission() + this.setState({userrole: permission}) } /** @@ -72,13 +73,13 @@ export class Scheduling extends Component { <PageHeader location={this.props.location} title={'Scheduling Unit - List'} actions={[ {icon: 'fa fa-plus-square', - title: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.create?'Add New Scheduling Unit':this.access_dined_message, - disabled: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit?!this.state.userrole.userRolePermission.scheduleunit.create:true, + title: this.state.userrole.userRolePermission && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.create?'Add New Scheduling Unit':this.access_dined_message, + disabled: this.state.userrole.userRolePermission && this.state.userrole.userRolePermission.scheduleunit?!this.state.userrole.userRolePermission.scheduleunit.create:true, props: {pathname: '/schedulingunit/create'}}, {icon: 'fa fa-table', - title: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.excelview?'Add Scheduling Set':this.access_dined_message, - disabled: this.state.userrole && this.state.userrole.userRolePermission.scheduleunit?!this.state.userrole.userRolePermission.scheduleunit.excelview:true, + title: this.state.userrole.userRolePermission && this.state.userrole.userRolePermission.scheduleunit && this.state.userrole.userRolePermission.scheduleunit.excelview?'Add Scheduling Set':this.access_dined_message, + disabled: this.state.userrole.userRolePermission && this.state.userrole.userRolePermission.scheduleunit?!this.state.userrole.userRolePermission.scheduleunit.excelview:true, props: {pathname: '/schedulingset/schedulingunit/create'}}, ]} /> {this.state.scheduleunit && diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js index 78981fdaa8b..6f3fbc08ec4 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/index.js @@ -25,6 +25,7 @@ import ReportHome from './Report'; import { Growl } from 'primereact/components/growl/Growl'; import { setAppGrowl } from '../layout/components/AppGrowl'; import WorkflowList from './Workflow/workflow.list' +import ProtectedRoute from '../components/ProtectedRouteComponent'; export const routes = [ { @@ -32,7 +33,9 @@ export const routes = [ component: NotFound, },{ path: "/access-denied", - component: AccessDenied + component: AccessDenied, + name: 'Access Denied', + title: 'Access Denied' },{ path: "/dashboard", component: Dashboard, @@ -42,12 +45,13 @@ export const routes = [ path: "/schedulingunit", component: Scheduling, name: 'Scheduling Unit', - title: 'Scheduling Unit - List' + title: 'Scheduling Unit - List', },{ path: "/schedulingunit/create", component: SchedulingUnitCreate, name: 'Scheduling Unit Add', - title: 'Scheduling Unit - Add' + title: 'Scheduling Unit - Add', + permissions: ['scheduleunit', 'create'] },{ path: "/task", component: TaskList, @@ -82,7 +86,9 @@ export const routes = [ path: "/schedulingunit/edit/:id", component: EditSchedulingUnit, name: 'Scheduling Edit', - title: 'Scheduling Unit - Edit' + title: 'Scheduling Unit - Edit', + permissions: ['scheduleunit', 'edit'] + },{ path: "/schedulingunit/view/:type/:id", component: ViewSchedulingUnit, @@ -96,7 +102,8 @@ export const routes = [ path: "/project/create", component: ProjectCreate, name: 'Project Add', - title: 'Project - Add' + title: 'Project - Add', + permissions: ['project', 'create'] },{ path: "/project/view/:id", component: ProjectView, @@ -107,7 +114,8 @@ export const routes = [ path: "/project/edit/:id", component: ProjectEdit, name: 'Project Edit', - title: 'Project Edit' + title: 'Project Edit', + permissions:['project', 'edit'] },{ path: "/project/:project/schedulingunit/create", component: SchedulingUnitCreate, @@ -225,7 +233,7 @@ export const RoutedContent = () => { <Growl ref={(el) => setAppGrowl(el)} /> <Switch> {/* <Redirect from="/" to="/" exact /> */} - {routes.map(routeProps => <Route {...routeProps} exact key={routeProps.path} />)} + {routes.map(routeProps => <ProtectedRoute {...routeProps} exact key={routeProps.path} />)} </Switch> </> ); -- GitLab