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