From 121be84731743276c37574f48d2d51efd1936c53 Mon Sep 17 00:00:00 2001 From: Albin Henriksson <albhe428@student.liu.se> Date: Wed, 28 Apr 2021 15:37:48 +0200 Subject: [PATCH] Use secure route for competition --- .vscode/settings.json | 2 +- client/src/Main.tsx | 22 ++++++++++---- client/src/actions/competitionLogin.ts | 8 +++-- client/src/pages/admin/AdminPage.tsx | 1 + client/src/pages/views/ViewSelectPage.tsx | 29 +++++++++---------- .../src/reducers/competitionLoginReducer.ts | 13 +++++++-- client/src/utils/SecureRoute.tsx | 1 + .../utils/checkAuthenticationCompetition.ts | 3 +- server/app/apis/auth.py | 25 +++++++--------- 9 files changed, 61 insertions(+), 43 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b02ef900..0b692c6a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,7 +5,7 @@ "editor.tabCompletion": "on", "editor.codeActionsOnSave": { "source.fixAll.eslint": true, - "source.organizeImports": false + "source.organizeImports": true }, //python "python.venvPath": "${workspaceFolder}\\server", diff --git a/client/src/Main.tsx b/client/src/Main.tsx index deeb7a47..9e079471 100644 --- a/client/src/Main.tsx +++ b/client/src/Main.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react' -import { BrowserRouter, Route, Switch } from 'react-router-dom' +import { BrowserRouter, Switch } from 'react-router-dom' import { getTypes } from './actions/typesAction' import { useAppDispatch } from './hooks' import AdminPage from './pages/admin/AdminPage' @@ -27,21 +27,31 @@ const Main: React.FC = () => { path="/editor/competition-id=:competitionId" component={PresentationEditorPage} /> - <Route exact path="/:code" component={ViewSelectPage} /> + <SecureRoute authLevel={'competition'} exact path="/:code" component={ViewSelectPage} /> <SecureRoute authLevel={'competition'} exact - path="/participant/id=:id&code=:code" + path="/team/competition-id=:competitionId" component={ParticipantViewPage} /> <SecureRoute authLevel={'competition'} exact - path="/presenter/id=:id&code=:code" + path="/operator/competition-id=:competitionId" component={PresenterViewPage} /> - <SecureRoute authLevel={'competition'} exact path="/judge/id=:id&code=:code" component={JudgeViewPage} /> - <SecureRoute authLevel={'competition'} exact path="/audience/id=:id&code=:code" component={AudienceViewPage} /> + <SecureRoute + authLevel={'competition'} + exact + path="/judge/competition-id=:competitionId" + component={JudgeViewPage} + /> + <SecureRoute + authLevel={'competition'} + exact + path="/audience/competition-id=:competitionId" + component={AudienceViewPage} + /> </Switch> </BrowserRouter> ) diff --git a/client/src/actions/competitionLogin.ts b/client/src/actions/competitionLogin.ts index b5d87756..ed75e711 100644 --- a/client/src/actions/competitionLogin.ts +++ b/client/src/actions/competitionLogin.ts @@ -5,7 +5,6 @@ This file handles actions for the competitionLogin redux state import axios from 'axios' import { History } from 'history' import { AppDispatch } from '../store' -import { AccountLoginModel } from './../interfaces/FormModels' import Types from './types' // Action creator to attempt to login with competition code @@ -14,10 +13,13 @@ export const loginCompetition = (code: string, history: History) => async (dispa await axios .post('/api/auth/login/code', { code }) .then((res) => { - console.log(code, res.data[0]) + const token = `Bearer ${res.data.access_token}` + localStorage.setItem('competitionToken', token) //setting token to local storage + axios.defaults.headers.common['Authorization'] = token //setting authorize token to header in axios + console.log(code, res.data) dispatch({ type: Types.CLEAR_COMPETITION_LOGIN_ERRORS }) // no error // history.push('/admin') //redirecting to admin page after login success - if (res.data && res.data[0] && res.data[0].view_type_id) { + if (res.data && res.data.view_type_id) { history.push(`/${code}`) } }) diff --git a/client/src/pages/admin/AdminPage.tsx b/client/src/pages/admin/AdminPage.tsx index a8b1746a..c206cfdd 100644 --- a/client/src/pages/admin/AdminPage.tsx +++ b/client/src/pages/admin/AdminPage.tsx @@ -74,6 +74,7 @@ const AdminView: React.FC = () => { dispatch(getRoles()) dispatch(getTypes()) dispatch(getStatistics()) + axios.get('/api/competitions/1/codes').then((res) => console.log(res.data.items)) }, []) const menuAdminItems = [ diff --git a/client/src/pages/views/ViewSelectPage.tsx b/client/src/pages/views/ViewSelectPage.tsx index 686ce4f1..9a0264f3 100644 --- a/client/src/pages/views/ViewSelectPage.tsx +++ b/client/src/pages/views/ViewSelectPage.tsx @@ -1,22 +1,16 @@ -import Button from '@material-ui/core/Button' -import React, { useEffect, useState } from 'react' -import { Link, useRouteMatch } from 'react-router-dom' -import { ViewSelectButtonGroup, ViewSelectContainer } from './styled' -import { useParams } from 'react-router-dom' import { CircularProgress, Typography } from '@material-ui/core' -import ParticipantViewPage from './ParticipantViewPage' -import axios from 'axios' -import PresenterViewPage from './PresenterViewPage' -import JudgeViewPage from './JudgeViewPage' -import AudienceViewPage from './AudienceViewPage' +import React, { useEffect, useState } from 'react' +import { Redirect, useHistory, useParams } from 'react-router-dom' +import { loginCompetition } from '../../actions/competitionLogin' import { useAppDispatch, useAppSelector } from '../../hooks' -import { getPresentationCompetition, setPresentationCode } from '../../actions/presentation' +import { ViewSelectButtonGroup, ViewSelectContainer } from './styled' interface ViewSelectParams { code: string } const ViewSelectPage: React.FC = () => { const dispatch = useAppDispatch() + const history = useHistory() const [loading, setLoading] = useState(true) const [error, setError] = useState(false) const [viewTypeId, setViewTypeId] = useState(undefined) @@ -29,11 +23,13 @@ const ViewSelectPage: React.FC = () => { if (competitionId) { switch (viewType) { case 'Team': - return <ParticipantViewPage /> + return <Redirect to={`/team/${competitionId}`} /> case 'Judge': - return <JudgeViewPage code={code} competitionId={competitionId} /> + return <Redirect to={`/judge/${competitionId}`} /> case 'Audience': - return <AudienceViewPage /> + return <Redirect to={`/audience/${competitionId}`} /> + case 'Operator': + return <Redirect to={`/operator/${competitionId}`} /> default: return <Typography>Inkorrekt vy</Typography> } @@ -41,7 +37,8 @@ const ViewSelectPage: React.FC = () => { } useEffect(() => { - axios + dispatch(loginCompetition(code, history)) + /* axios .post('/api/auth/login/code', { code }) .then((response) => { setLoading(false) @@ -53,7 +50,7 @@ const ViewSelectPage: React.FC = () => { .catch(() => { setLoading(false) setError(true) - }) + }) */ }, []) return ( diff --git a/client/src/reducers/competitionLoginReducer.ts b/client/src/reducers/competitionLoginReducer.ts index 81c426a4..c8802ef7 100644 --- a/client/src/reducers/competitionLoginReducer.ts +++ b/client/src/reducers/competitionLoginReducer.ts @@ -5,25 +5,34 @@ interface UIError { message: string } -interface UserState { +interface CompetitionLoginState { loading: boolean errors: null | UIError + authenticated: boolean } -const initialState: UserState = { +const initialState: CompetitionLoginState = { loading: false, errors: null, + authenticated: false, } export default function (state = initialState, action: AnyAction) { switch (action.type) { + case Types.SET_COMPETITION_LOGIN_AUTHENTICATED: + return { + ...state, + authenticated: true, + } case Types.SET_COMPETITION_LOGIN_ERRORS: return { + ...state, errors: action.payload as UIError, loading: false, } case Types.CLEAR_COMPETITION_LOGIN_ERRORS: return { + ...state, loading: false, errors: null, } diff --git a/client/src/utils/SecureRoute.tsx b/client/src/utils/SecureRoute.tsx index 9e977c4b..cfdf52f6 100644 --- a/client/src/utils/SecureRoute.tsx +++ b/client/src/utils/SecureRoute.tsx @@ -26,6 +26,7 @@ const SecureRoute: React.FC<SecureRouteProps> = ({ } else { await CheckAuthenticationCompetition() } + console.log('initialized') setInitialized(true) } waitForAuthentication() diff --git a/client/src/utils/checkAuthenticationCompetition.ts b/client/src/utils/checkAuthenticationCompetition.ts index 533cfbfd..ef3e6c00 100644 --- a/client/src/utils/checkAuthenticationCompetition.ts +++ b/client/src/utils/checkAuthenticationCompetition.ts @@ -2,7 +2,6 @@ import axios from 'axios' import jwtDecode from 'jwt-decode' import { logoutCompetition } from '../actions/competitionLogin' import Types from '../actions/types' -import { logoutUser } from '../actions/user' import store from '../store' const UnAuthorized = async () => { @@ -10,9 +9,11 @@ const UnAuthorized = async () => { } export const CheckAuthenticationCompetition = async () => { + console.log('checkAuthComp') const authToken = localStorage.competitionToken if (authToken) { const decodedToken: any = jwtDecode(authToken) + console.log(decodedToken) if (decodedToken.exp * 1000 >= Date.now()) { axios.defaults.headers.common['Authorization'] = authToken await axios diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py index 9c9ed24a..62795e3a 100644 --- a/server/app/apis/auth.py +++ b/server/app/apis/auth.py @@ -1,19 +1,15 @@ +from datetime import timedelta + import app.core.http_codes as codes import app.database.controller as dbc from app.apis import item_response, protect_route, text_response +from app.core import sockets from app.core.codes import verify_code from app.core.dto import AuthDTO, CodeDTO -from flask_jwt_extended import ( - create_access_token, - create_refresh_token, - get_jwt_identity, - get_raw_jwt, - jwt_refresh_token_required, -) -from flask_restx import Resource -from flask_restx import inputs, reqparse -from datetime import timedelta -from app.core import sockets +from flask_jwt_extended import (create_access_token, create_refresh_token, + get_jwt_identity, get_raw_jwt, + jwt_refresh_token_required) +from flask_restx import Resource, inputs, reqparse api = AuthDTO.api schema = AuthDTO.schema @@ -95,9 +91,10 @@ class AuthLoginCode(Resource): api.abort(codes.UNAUTHORIZED, "Invalid code") item_code = dbc.get.code_by_code(code) - - if item_code.competition_id not in sockets.presentations: - api.abort(codes.UNAUTHORIZED, "Competition not active") + + if item_code.view_type_id != 4: + if item_code.competition_id not in sockets.presentations: + api.abort(codes.UNAUTHORIZED, "Competition not active") access_token = create_access_token( item_code.id, user_claims=get_code_claims(item_code), expires_delta=timedelta(hours=8) -- GitLab