From 005a3c5a0d091cb93524418fefc9edafabdfd88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?gr=C3=A9goire=20parant?= Date: Sun, 15 Aug 2021 23:13:48 +0200 Subject: [PATCH 1/3] Release 1.4.14 (#1370) * New version of cache management (#1365) Signed-off-by: Gregoire Parant * Exit scene acess denied detected (#1369) * Add auth token user to get right in admin and check if user have right Signed-off-by: Gregoire Parant * Update error show Signed-off-by: Gregoire Parant * Update token generation (#1372) - Permit only decode token to get map details, - If user have token expired, set the token to null and reload the page. This feature will be updated when authentication stategy will be finished. Signed-off-by: Gregoire Parant --- front/dist/service-worker-prod.js | 2 +- .../src/Components/Video/VideoMediaBox.svelte | 1 - front/src/Connexion/ConnectionManager.ts | 19 +++++++++++-- front/src/Connexion/Room.ts | 2 ++ front/src/Phaser/Game/GameScene.ts | 28 +++++++++++++++++-- front/src/Phaser/Login/EntryScene.ts | 13 ++++++++- front/src/Phaser/Reconnecting/ErrorScene.ts | 6 +++- pusher/src/Controller/BaseController.ts | 7 ++++- pusher/src/Controller/IoSocketController.ts | 2 +- pusher/src/Controller/MapController.ts | 18 +++++++++++- pusher/src/Services/AdminApi.ts | 10 +++++-- pusher/src/Services/JWTTokenManager.ts | 4 +-- 12 files changed, 96 insertions(+), 16 deletions(-) diff --git a/front/dist/service-worker-prod.js b/front/dist/service-worker-prod.js index 2f8d6a51..919b0b91 100644 --- a/front/dist/service-worker-prod.js +++ b/front/dist/service-worker-prod.js @@ -1,4 +1,4 @@ -let CACHE_NAME = 'workavdenture-cache-v1.2'; +let CACHE_NAME = 'workavdenture-cache-v1.4.14'; let urlsToCache = [ '/' ]; diff --git a/front/src/Components/Video/VideoMediaBox.svelte b/front/src/Components/Video/VideoMediaBox.svelte index 6b981dc8..924bb13a 100644 --- a/front/src/Components/Video/VideoMediaBox.svelte +++ b/front/src/Components/Video/VideoMediaBox.svelte @@ -22,7 +22,6 @@ let isMobile : boolean|null; const unsubscribe = obtainedMediaConstraintIsMobileStore.subscribe(value => { - console.log('unsubscribe => obtainedMediaConstraintIsMobileStore', value); isMobile = value; }); onDestroy(unsubscribe); diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 38272737..9e245d3a 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -29,11 +29,24 @@ class ConnectionManager { }); } - public loadOpenIDScreen() { - localUserStore.setAuthToken(null); + /** + * @return Promise + */ + public loadOpenIDScreen(): Promise { const state = localUserStore.generateState(); const nonce = localUserStore.generateNonce(); - window.location.assign(`http://${PUSHER_URL}/login-screen?state=${state}&nonce=${nonce}`); + localUserStore.setAuthToken(null); + + //TODO refactor this and don't realise previous call + return Axios.get(`http://${PUSHER_URL}/login-screen?state=${state}&nonce=${nonce}`) + .then(() => { + window.location.assign(`http://${PUSHER_URL}/login-screen?state=${state}&nonce=${nonce}`); + }) + .catch((err) => { + console.error(err, "We don't have URL to regenerate authentication user"); + //TODO show modal login + window.location.reload(); + }); } public logout() { diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index 2053911d..be9b5294 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -1,6 +1,7 @@ import Axios from "axios"; import { PUSHER_URL } from "../Enum/EnvironmentVariable"; import type { CharacterTexture } from "./LocalUser"; +import { localUserStore } from "./LocalUserStore"; export class MapDetail { constructor(public readonly mapUrl: string, public readonly textures: CharacterTexture[] | undefined) {} @@ -87,6 +88,7 @@ export class Room { const result = await Axios.get(`${PUSHER_URL}/map`, { params: { playUri: this.roomUrl.toString(), + authToken: localUserStore.getAuthToken(), }, }); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index ce947224..16fa2c83 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -94,6 +94,7 @@ import { userIsAdminStore } from "../../Stores/GameStore"; import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore"; import { get } from "svelte/store"; import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager"; +import { helpCameraSettingsVisibleStore } from "../../Stores/HelpCameraSettingsStore"; export interface GameSceneInitInterface { initPosition: PointInterface | null; @@ -814,13 +815,24 @@ export class GameScene extends DirtyScene { private triggerOnMapLayerPropertyChange() { this.gameMap.onPropertyChange("exitSceneUrl", (newValue, oldValue) => { - if (newValue) + if (newValue) { this.onMapExit( Room.getRoomPathFromExitSceneUrl(newValue as string, window.location.toString(), this.MapUrlFile) ); + } else { + setTimeout(() => { + layoutManagerActionStore.removeAction("roomAccessDenied"); + }, 2000); + } }); this.gameMap.onPropertyChange("exitUrl", (newValue, oldValue) => { - if (newValue) this.onMapExit(Room.getRoomPathFromExitUrl(newValue as string, window.location.toString())); + if (newValue) { + this.onMapExit(Room.getRoomPathFromExitUrl(newValue as string, window.location.toString())); + } else { + setTimeout(() => { + layoutManagerActionStore.removeAction("roomAccessDenied"); + }, 2000); + } }); this.gameMap.onPropertyChange("openWebsite", (newValue, oldValue, allProps) => { if (newValue === undefined) { @@ -1290,6 +1302,18 @@ ${escapedMessage} targetRoom = await Room.createRoom(roomUrl); } catch (e /*: unknown*/) { console.error('Error while fetching new room "' + roomUrl.toString() + '"', e); + + //show information room access denied + layoutManagerActionStore.addAction({ + uuid: "roomAccessDenied", + type: "warning", + message: "Room access denied. You don't have right to access on this room.", + callback: () => { + layoutManagerActionStore.removeAction("roomAccessDenied"); + }, + userInputManager: this.userInputManager, + }); + this.mapTransitioning = false; return; } diff --git a/front/src/Phaser/Login/EntryScene.ts b/front/src/Phaser/Login/EntryScene.ts index 3180d0f6..63181ae9 100644 --- a/front/src/Phaser/Login/EntryScene.ts +++ b/front/src/Phaser/Login/EntryScene.ts @@ -1,6 +1,6 @@ import { gameManager } from "../Game/GameManager"; import { Scene } from "phaser"; -import { ErrorScene } from "../Reconnecting/ErrorScene"; +import { ErrorScene, ErrorSceneName } from "../Reconnecting/ErrorScene"; import { WAError } from "../Reconnecting/WAError"; import { waScaleManager } from "../Services/WaScaleManager"; @@ -36,6 +36,17 @@ export class EntryScene extends Scene { ), this.scene ); + } else if (err.response && err.response.status == 403) { + ErrorScene.showError( + new WAError( + "Connection rejected", + "You cannot join the World. Try again later" + + (err.response.data ? ". \n\r \n\r" + `${err.response.data}` : "") + + ".", + "If you want more information, you may contact administrator or contact us at: hello@workadventu.re" + ), + this.scene + ); } else { ErrorScene.showError(err, this.scene); } diff --git a/front/src/Phaser/Reconnecting/ErrorScene.ts b/front/src/Phaser/Reconnecting/ErrorScene.ts index fb3d333a..6477b918 100644 --- a/front/src/Phaser/Reconnecting/ErrorScene.ts +++ b/front/src/Phaser/Reconnecting/ErrorScene.ts @@ -90,7 +90,11 @@ export class ErrorScene extends Phaser.Scene { // Axios HTTP error // client received an error response (5xx, 4xx) scene.start(ErrorSceneName, { - title: "HTTP " + error.response.status + " - " + error.response.statusText, + title: + "HTTP " + + error.response.status + + " - " + + (error.response.data ? error.response.data : error.response.statusText), subTitle: "An error occurred while accessing URL:", message: error.response.config.url, }); diff --git a/pusher/src/Controller/BaseController.ts b/pusher/src/Controller/BaseController.ts index ce378a55..a983333a 100644 --- a/pusher/src/Controller/BaseController.ts +++ b/pusher/src/Controller/BaseController.ts @@ -29,7 +29,12 @@ export class BaseController { if (e.response) { res.writeStatus(e.response.status + " " + e.response.statusText); this.addCorsHeaders(res); - res.end("An error occurred: " + e.response.status + " " + e.response.statusText); + res.end( + "An error occurred: " + + e.response.status + + " " + + (e.response.data && e.response.data.message ? e.response.data.message : e.response.statusText) + ); } else { res.writeStatus("500 Internal Server Error"); this.addCorsHeaders(res); diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index daf45ce4..0466100c 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -174,7 +174,7 @@ export class IoSocketController { } const tokenData = - token && typeof token === "string" ? jwtTokenManager.decodeJWTToken(token) : null; + token && typeof token === "string" ? jwtTokenManager.verifyJWTToken(token) : null; const userIdentifier = tokenData ? tokenData.identifier : ""; let memberTags: string[] = []; diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index 6ea2f19d..ccaa231f 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -5,6 +5,9 @@ import { adminApi } from "../Services/AdminApi"; import { ADMIN_API_URL } from "../Enum/EnvironmentVariable"; import { GameRoomPolicyTypes } from "../Model/PusherRoom"; import { MapDetailsData } from "../Services/AdminApi/MapDetailsData"; +import { socketManager } from "../Services/SocketManager"; +import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; +import { v4 } from "uuid"; export class MapController extends BaseController { constructor(private App: TemplatedApp) { @@ -67,7 +70,20 @@ export class MapController extends BaseController { (async () => { try { - const mapDetails = await adminApi.fetchMapDetails(query.playUri as string); + let userId: string | undefined = undefined; + if (query.authToken != undefined) { + let authTokenData: AuthTokenData; + try { + authTokenData = jwtTokenManager.verifyJWTToken(query.authToken as string); + userId = authTokenData.identifier; + } catch (e) { + // Decode token, in this case we don't need to create new token. + authTokenData = jwtTokenManager.verifyJWTToken(query.authToken as string, true); + userId = authTokenData.identifier; + console.info("JWT expire, but decoded", userId); + } + } + const mapDetails = await adminApi.fetchMapDetails(query.playUri as string, userId); res.writeStatus("200 OK"); this.addCorsHeaders(res); diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 61ef8e20..f33480ca 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -31,13 +31,19 @@ export interface FetchMemberDataByUuidResponse { } class AdminApi { - async fetchMapDetails(playUri: string): Promise { + /** + * @var playUri: is url of the room + * @var userId: can to be undefined or email or uuid + * @return MapDetailsData|RoomRedirect + */ + async fetchMapDetails(playUri: string, userId?: string): Promise { if (!ADMIN_API_URL) { return Promise.reject(new Error("No admin backoffice set!")); } - const params: { playUri: string } = { + const params: { playUri: string; userId?: string } = { playUri, + userId, }; const res = await Axios.get(ADMIN_API_URL + "/api/map", { diff --git a/pusher/src/Services/JWTTokenManager.ts b/pusher/src/Services/JWTTokenManager.ts index bb21531c..4711ccfd 100644 --- a/pusher/src/Services/JWTTokenManager.ts +++ b/pusher/src/Services/JWTTokenManager.ts @@ -15,9 +15,9 @@ class JWTTokenManager { return Jwt.sign({ identifier }, SECRET_KEY, { expiresIn: "200d" }); } - public decodeJWTToken(token: string): AuthTokenData { + public verifyJWTToken(token: string, ignoreExpiration: boolean = false): AuthTokenData { try { - return Jwt.verify(token, SECRET_KEY, { ignoreExpiration: false }) as AuthTokenData; + return Jwt.verify(token, SECRET_KEY, { ignoreExpiration }) as AuthTokenData; } catch (e) { throw { reason: tokenInvalidException, message: e.message }; } From 0bfac1a164c776f3612f755a5d210dd1eb056234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?gr=C3=A9goire=20parant?= Date: Tue, 17 Aug 2021 13:26:20 +0200 Subject: [PATCH 2/3] Update cache API index (#1378) Signed-off-by: Gregoire Parant --- front/src/Connexion/LocalUserStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 25b673ac..03a64285 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -17,7 +17,7 @@ const authToken = "authToken"; const state = "state"; const nonce = "nonce"; -const cacheAPIIndex = "workavdenture-cache-v1"; +const cacheAPIIndex = "workavdenture-cache-v1.4.14"; class LocalUserStore { saveUser(localUser: LocalUser) { From 1ffd198b001df09f445fb1c05451723299791db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?gr=C3=A9goire=20parant?= Date: Thu, 19 Aug 2021 18:23:30 +0200 Subject: [PATCH 3/3] Update cache management (#1382) Change strategy of cache management. Today we don't have version of map building so we cannot use cache correctly. The idea is to have a less cache and keep HTPP cache management with GET method. Signed-off-by: Gregoire Parant --- front/dist/service-worker-prod.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/front/dist/service-worker-prod.js b/front/dist/service-worker-prod.js index 919b0b91..80486623 100644 --- a/front/dist/service-worker-prod.js +++ b/front/dist/service-worker-prod.js @@ -1,4 +1,4 @@ -let CACHE_NAME = 'workavdenture-cache-v1.4.14'; +let CACHE_NAME = 'workavdenture-cache'; let urlsToCache = [ '/' ]; @@ -14,7 +14,8 @@ self.addEventListener('install', function(event) { }); self.addEventListener('fetch', function(event) { - event.respondWith( + //TODO mamnage fetch data and cache management + /*event.respondWith( caches.match(event.request) .then(function(response) { // Cache hit - return response @@ -44,7 +45,7 @@ self.addEventListener('fetch', function(event) { } ); }) - ); + );*/ }); self.addEventListener('wait', function(event) {