From 5e54fc2c26901810572842fbeea66bb10e983e18 Mon Sep 17 00:00:00 2001 From: arp Date: Fri, 9 Oct 2020 14:53:18 +0200 Subject: [PATCH] some fixes --- back/src/Controller/AuthenticateController.ts | 6 +- back/src/Controller/IoSocketController.ts | 68 +++---------------- back/src/Services/JWTTokenManager.ts | 60 ++++++++++++++++ front/src/Connexion/ConnectionManager.ts | 2 +- front/src/Connexion/RoomConnection.ts | 4 +- .../src/Phaser/Entity/GameSceneDescriptor.ts | 6 -- 6 files changed, 76 insertions(+), 70 deletions(-) create mode 100644 back/src/Services/JWTTokenManager.ts delete mode 100644 front/src/Phaser/Entity/GameSceneDescriptor.ts diff --git a/back/src/Controller/AuthenticateController.ts b/back/src/Controller/AuthenticateController.ts index a178b530..d84ccb56 100644 --- a/back/src/Controller/AuthenticateController.ts +++ b/back/src/Controller/AuthenticateController.ts @@ -1,9 +1,9 @@ -import Jwt from "jsonwebtoken"; -import {ADMIN_API_TOKEN, ADMIN_API_URL, SECRET_KEY, URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." +import {URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." import { uuid } from 'uuidv4'; import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js"; import {BaseController} from "./BaseController"; import {adminApi, AdminApiData} from "../Services/AdminApi"; +import {jwtTokenManager} from "../Services/JWTTokenManager"; export interface TokenInterface { userUuid: string @@ -59,7 +59,7 @@ export class AuthenticateController extends BaseController { newUrl = '_/global/'+mapUrlStart; } - const authToken = Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '24h'}); + const authToken = jwtTokenManager.createJWTToken(userUuid); res.writeStatus("200 OK").end(JSON.stringify({ authToken, userUuid, diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 943f949c..30f14134 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -1,14 +1,11 @@ import {ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.." -import Jwt from "jsonwebtoken"; -import {SECRET_KEY, MINIMUM_DISTANCE, GROUP_RADIUS, ALLOW_ARTILLERY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." +import {MINIMUM_DISTANCE, GROUP_RADIUS} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." import {GameRoom} from "../Model/GameRoom"; import {Group} from "../Model/Group"; import {User} from "../Model/User"; import {isSetPlayerDetailsMessage,} from "../Model/Websocket/SetPlayerDetailsMessage"; import {Gauge} from "prom-client"; -import {TokenInterface} from "../Controller/AuthenticateController"; import {PointInterface} from "../Model/Websocket/PointInterface"; -import {uuid} from 'uuidv4'; import {Movable} from "../Model/Movable"; import { PositionMessage, @@ -43,6 +40,8 @@ import {TemplatedApp} from "uWebSockets.js" import {parse} from "query-string"; import {cpuTracker} from "../Services/CpuTracker"; import {ViewportInterface} from "../Model/Websocket/ViewportMessage"; +import {jwtTokenManager} from "../Services/JWTTokenManager"; +import {adminApi} from "../Services/AdminApi"; function emitInBatch(socket: ExSocketInterface, payload: SubMessage): void { socket.batchedMessages.addPayload(payload); @@ -86,54 +85,7 @@ export class IoSocketController { this.ioConnection(); } - private isValidToken(token: object): token is TokenInterface { - if (typeof((token as TokenInterface).userUuid) !== 'string') { - return false; - } - return true; - } - - private async getUserUuidFromToken(token: unknown): Promise { - - if (!token) { - throw new Error('An authentication error happened, a user tried to connect without a token.'); - } - if (typeof(token) !== "string") { - throw new Error('Token is expected to be a string'); - } - - - if(token === 'test') { - if (ALLOW_ARTILLERY) { - return uuid(); - } else { - throw new Error("In order to perform a load-testing test on this environment, you must set the ALLOW_ARTILLERY environment variable to 'true'"); - } - } - - return new Promise((resolve, reject) => { - Jwt.verify(token, SECRET_KEY, {},(err, tokenDecoded) => { - const tokenInterface = tokenDecoded as TokenInterface; - if (err) { - console.error('An authentication error happened, invalid JsonWebToken.', err); - reject(new Error('An authentication error happened, invalid JsonWebToken. '+err.message)); - return; - } - if (tokenDecoded === undefined) { - console.error('Empty token found.'); - reject(new Error('Empty token found.')); - return; - } - - if (!this.isValidToken(tokenInterface)) { - reject(new Error('Authentication error, invalid token structure.')); - return; - } - - resolve(tokenInterface.userUuid); - }); - }); - } + ioConnection() { this.app.ws('/room/*', { @@ -181,7 +133,12 @@ export class IoSocketController { } - const userUuid = await this.getUserUuidFromToken(token); + const userUuid = await jwtTokenManager.getUserUuidFromToken(token); + + const isGranted = await adminApi.memberIsGrantedAccessToRoom(userUuid, roomId); + if (!isGranted) { + throw Error('Client cannot acces this ressource.'); + } if (upgradeAborted.aborted) { console.log("Ouch! Client disconnected before we could upgrade it!"); @@ -256,11 +213,6 @@ export class IoSocketController { // Let's join the room this.handleJoinRoom(client, client.roomId, client.position, client.viewport, client.name, client.characterLayers); - /*const isGranted = await adminApi.memberIsGrantedAccessToRoom(client.userUuid, roomId); - if (!isGranted) { - throw Error('Client cannot acces this ressource.'); - }*/ - const setUserIdMessage = new SetUserIdMessage(); setUserIdMessage.setUserid(client.userId); diff --git a/back/src/Services/JWTTokenManager.ts b/back/src/Services/JWTTokenManager.ts new file mode 100644 index 00000000..905e0ac6 --- /dev/null +++ b/back/src/Services/JWTTokenManager.ts @@ -0,0 +1,60 @@ +import {ALLOW_ARTILLERY, SECRET_KEY} from "../Enum/EnvironmentVariable"; +import {uuid} from "uuidv4"; +import Jwt from "jsonwebtoken"; +import {TokenInterface} from "../Controller/AuthenticateController"; + +class JWTTokenManager { + + public createJWTToken(userUuid: string) { + return Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '24h'}); + } + + public async getUserUuidFromToken(token: unknown): Promise { + + if (!token) { + throw new Error('An authentication error happened, a user tried to connect without a token.'); + } + if (typeof(token) !== "string") { + throw new Error('Token is expected to be a string'); + } + + + if(token === 'test') { + if (ALLOW_ARTILLERY) { + return uuid(); + } else { + throw new Error("In order to perform a load-testing test on this environment, you must set the ALLOW_ARTILLERY environment variable to 'true'"); + } + } + + return new Promise((resolve, reject) => { + Jwt.verify(token, SECRET_KEY, {},(err, tokenDecoded) => { + const tokenInterface = tokenDecoded as TokenInterface; + if (err) { + console.error('An authentication error happened, invalid JsonWebToken.', err); + reject(new Error('An authentication error happened, invalid JsonWebToken. '+err.message)); + return; + } + if (tokenDecoded === undefined) { + console.error('Empty token found.'); + reject(new Error('Empty token found.')); + return; + } + + if (!this.isValidToken(tokenInterface)) { + reject(new Error('Authentication error, invalid token structure.')); + return; + } + + resolve(tokenInterface.userUuid); + }); + }); + } + + private isValidToken(token: object): token is TokenInterface { + return !(typeof((token as TokenInterface).userUuid) !== 'string'); + } + +} + +export const jwtTokenManager = new JWTTokenManager(); \ No newline at end of file diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index a9d15dd9..06f8fe03 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -48,7 +48,7 @@ class ConnectionManager { public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface): Promise { return new Promise((resolve, reject) => { - const connection = new RoomConnection(this.authToken as string, roomId, name, characterLayers, position, viewport); + const connection = new RoomConnection(this.authToken, roomId, name, characterLayers, position, viewport); connection.onConnectError((error: object) => { console.log('An error occurred while connecting to socket server. Retrying'); reject(error); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 8799f977..f34197d3 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -53,10 +53,10 @@ export class RoomConnection implements RoomConnection { * @param token A JWT token containing the UUID of the user * @param roomId The ID of the room in the form "_/[instance]/[map_url]" or "@/[org]/[event]/[map]" */ - public constructor(token: string, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface) { + public constructor(token: string|null, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface) { let url = API_URL.replace('http://', 'ws://').replace('https://', 'wss://'); url += '/room/'+roomId - url += '?token='+encodeURIComponent(token); + url += '?token='+(token ?encodeURIComponent(token):''); url += '&name='+encodeURIComponent(name); for (let layer of characterLayers) { url += '&characterLayers='+encodeURIComponent(layer); diff --git a/front/src/Phaser/Entity/GameSceneDescriptor.ts b/front/src/Phaser/Entity/GameSceneDescriptor.ts deleted file mode 100644 index df114c28..00000000 --- a/front/src/Phaser/Entity/GameSceneDescriptor.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class GameSceneDescriptor { - - constructor(MapKey : string, MapUrlFile: string, instance: string, key: string) { - this.roomId = '';// - } -} \ No newline at end of file