Skip to content
Snippets Groups Projects
Commit 0d436a47 authored by Defendi Alberto's avatar Defendi Alberto
Browse files

Merge branch 'feature/privateroute/role' into 'dev'

New route type (RestrictedRoute) and better api calls.

See merge request !44
parents 1de11a10 e8f5de30
No related branches found
No related tags found
2 merge requests!56Refined auth flow and new website pages.,!44New route type (RestrictedRoute) and better api calls.
Pipeline #12197 passed
Showing
with 163 additions and 109 deletions
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
import { createMuiTheme } from '@material-ui/core/styles';
export const muiTheme = createMuiTheme({
palette: {
primary: {
main: '#5e5ce4',
},
secondary: {
main: '#e2e45c',
},
},
typography: {
fontSize: 16,
},
overrides: {
MuiTabs: {
indicator: {
backgroundColor: 'white',
},
},
MuiTab: {
wrapper: {
flexDirection: 'row',
},
},
},
});
import React, { FC, useEffect, useState } from 'react';
import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
import { ThemeProvider } from '@material-ui/core/styles';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { AuthUser } from 'components/AuthUser/AuthUser';
import { LandingPage } from 'components/NonAuthUser/LandingPage/LandingPage';
import { PrivateRoute } from 'api/PrivateRoute/PrivateRoute';
import { PrivateRoute } from 'components/PrivateRoute/PrivateRoute';
import { AuthRoutes, NonAuthRoutes } from 'api/routes';
import { NotFound } from 'components/NonAuthUser/NotFound/NotFound';
import { ProfilePage } from 'components/AuthUser/ProfilePage/ProfilePage';
import { ProfilePage } from 'components/AuthUser/Dashboard/ProfilePage/ProfilePage';
import { Roles } from 'api/userRoles';
import { Unauthorized } from 'components/NonAuthUser/Unauthorized/Unauthorized';
import { ReservationPage } from 'components/AuthUser/ReservationPage/ReservationPage';
import { HomePage } from 'components/AuthUser/HomePage/HomePage';
import { SignInForm } from 'components/AuthUser/SignInForm/SignInForm';
import { HomePage } from 'components/AuthUser/Dashboard/HomePage/HomePage';
import { AuthContext } from 'components/AuthUser/AuthContext';
import { configDjangoCookieName } from 'api/configDjangoCookieName';
import { Dashboard } from 'components/AuthUser/Dashboard/Dashboard';
import { isAuthenticated } from 'api/isAuthenticated';
import { muiTheme } from 'App.style';
import { getRole } from 'api/getRole';
const theme = createMuiTheme({
palette: {
primary: {
main: '#5e5ce4',
},
secondary: {
main: '#e2e45c',
},
},
typography: {
fontSize: 16,
},
overrides: {
MuiTabs: {
indicator: {
backgroundColor: 'white',
},
},
MuiTab: {
wrapper: {
flexDirection: 'row',
},
},
},
});
/**
* Entry point of the app.
*/
export const App: FC = () => {
configDjangoCookieName();
const [role, setRole] = useState('');
const value = { role, setRole };
const [isAuth, setIsAuth] = useState<boolean>(false);
const value = { role, setRole, isAuth, setIsAuth };
useEffect(() => {
// Initialize asking the server if this session is already logged in.
isAuthenticated().then((responseState) => setIsAuth(responseState));
getRole().then((responseRole) => setRole(responseRole));
}, [isAuth, role]);
return (
<ThemeProvider theme={theme}>
<Router>
<div data-testid="App">
<Switch>
<Route exact path={NonAuthRoutes.home} component={LandingPage} />
<AuthContext.Provider value={value}>
<div data-testid="App">
<ThemeProvider theme={muiTheme}>
<AuthContext.Provider value={value}>
<Router>
<Switch>
<Route exact path={NonAuthRoutes.home} component={LandingPage} />
<Route path={NonAuthRoutes.auth} component={AuthUser} />
<PrivateRoute
Component={Dashboard}
path={AuthRoutes.dashboard}
requiredRoles={[Roles.admin, Roles.operator, Roles.senior]}
/>
<PrivateRoute
Component={HomePage}
path={AuthRoutes.dashboard}
requiredRoles={[Roles.admin, Roles.operator, Roles.senior]}
/>
<PrivateRoute
path={AuthRoutes.profile}
Component={ProfilePage}
path={AuthRoutes.profile}
requiredRoles={[Roles.admin, Roles.operator, Roles.senior]}
/>
</AuthContext.Provider>
<Route path={AuthRoutes.reservation} component={ReservationPage} />
<Route path={NonAuthRoutes.unauthorized} component={Unauthorized} />
<Route component={NotFound} />
</Switch>
</div>
</Router>
</ThemeProvider>
<Route
path={NonAuthRoutes.unauthorized}
component={Unauthorized}
/>
<Route component={NotFound} />
</Switch>
</Router>
</AuthContext.Provider>
</ThemeProvider>
</div>
);
};
/**
* Contains server api entrypoints.
* Every server api call must be defined here and then used in the code.
*/
export enum EntryPoint {
login = '/api/web/login',
seniors = 'api/web/seniors/',
}
import axios from 'axios';
/**
* Config for django compatibility.
*/
export const configDjangoCookieName = (): void => {
axios.defaults.xsrfHeaderName = 'X-CSRFTOKEN';
axios.defaults.xsrfCookieName = 'csrftoken';
axios.defaults.withCredentials = true;
};
import axios from 'axios';
/**
* Call for csrf cookie. This cookie is the user session identifier and
* must be sent during the login process.
* @returns csrf cookie
*/
export const fetchCookie = async (): Promise<string> =>
axios('/api/web/csrf').then((res) => res.data.token);
import axios from 'axios';
/**
* Asks for the current set role.
* @returns empty if the role is not set or a name between those defined
* in {@see userRoles}
*/
export const getRole = async (): Promise<string> =>
axios('/api/web/login/get_role').then((res) => res.data.role);
import axios from 'axios';
/**
* Ask the server if the user is authenticated.
* @returns true or false if the user is authenticated.
*/
export const isAuthenticated = async (): Promise<boolean> =>
axios('/api/web/login/is_authenticated').then(
(res) => res.data.is_authenticated,
);
......@@ -5,6 +5,7 @@
*/
export enum AuthRoutes {
dashboard = '/dashboard',
home = '/home',
profile = '/profile',
reservation = '/reservation',
choseRole = '/choseRole',
......
export enum Roles {
visitor = 'visitor',
/** Website visitor, is not logged. Can access to NonAuthRoutes */
visitor = '',
/** Can access to private and restricted routes depending on their permissions level. */
senior = 'senior',
admin = 'admin',
operator = 'operator',
......
......@@ -3,12 +3,18 @@ import { createContext } from 'react';
export type AuthData = {
role: string;
isAuth: boolean;
setRole: (role: string) => void;
setIsAuth: (state: boolean) => void;
};
export const AuthContext = createContext<AuthData>({
role: Roles.visitor,
isAuth: false,
setRole: () => {
// Do nothing
},
setIsAuth: () => {
// Do nothing
},
});
import React, { FC } from 'react';
import Container from '@material-ui/core/Container';
import { Route, useRouteMatch } from 'react-router-dom';
import { NonAuthRoutes } from 'api/routes';
import { AuthRoutes, NonAuthRoutes } from 'api/routes';
import { SignInForm } from 'components/AuthUser/SignInForm/SignInForm';
import { SignUpForm } from 'components/AuthUser/SignUpForm/SignUpForm';
import { ChoseRole } from 'components/AuthUser/ChoseRole/ChoseRole';
import { RestrictedRoute } from 'components/RestrictedRoute/RestrictedRoute';
export const AuthUser: FC = () => {
const { path } = useRouteMatch();
return (
<Container maxWidth="sm">
<Route path={`${path}${NonAuthRoutes.signIn}`} component={SignInForm} />
<RestrictedRoute
path={`${path}${NonAuthRoutes.signIn}`}
Component={SignInForm}
/>
<Route path={`${path}${NonAuthRoutes.signUp}`} component={SignUpForm} />
<Route path={`${path}${AuthRoutes.choseRole}`} component={ChoseRole} />
</Container>
);
};
import React, { FC } from 'react';
import Container from '@material-ui/core/Container';
import { Route, useRouteMatch } from 'react-router-dom';
import { AuthRoutes } from 'api/routes';
import { HomePage } from 'components/AuthUser/Dashboard/HomePage/HomePage';
import { ProfilePage } from 'components/AuthUser/Dashboard/ProfilePage/ProfilePage';
export const Dashboard: FC = () => {
const { path } = useRouteMatch();
return (
<Container maxWidth="sm">
<Route exact path={`${path}${AuthRoutes.home}`} component={HomePage} />
<Route path={`${path}${AuthRoutes.profile}`} component={ProfilePage} />
</Container>
);
};
import React, { FC } from 'react';
import { createStyles, Theme, makeStyles } from '@material-ui/core/styles';
import Drawer from '@material-ui/core/Drawer';
import AppBar from '@material-ui/core/AppBar';
import CssBaseline from '@material-ui/core/CssBaseline';
import Toolbar from '@material-ui/core/Toolbar';
import List from '@material-ui/core/List';
import Typography from '@material-ui/core/Typography';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import { AuthRoutes } from 'api/routes';
import {
AppBar,
Toolbar,
Typography,
Button,
Drawer,
List,
ListItem,
ListItemText,
} from '@material-ui/core';
import { Link } from 'react-router-dom';
const drawerWidth = 240;
......@@ -41,12 +46,14 @@ export const HomePage: FC = () => {
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
<Toolbar>
<Typography variant="h6" noWrap>
Moveaid
</Typography>
<Button variant="contained">
<Link to={`/dashboard${AuthRoutes.profile}`}>Profile</Link>
</Button>
</Toolbar>
</AppBar>
<Drawer
......
import React, { FC } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { Tabs, Tab, AppBar } from '@material-ui/core';
import { Tabs, Tab } from '@material-ui/core';
import HomeIcon from '@material-ui/icons/Home';
import ImportContactsIcon from '@material-ui/icons/ImportContacts';
import AccountCircleIcon from '@material-ui/icons/AccountCircle';
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment