From f87422187f1ca06011373027a461dfeba3ec2477 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Thu, 14 Oct 2021 17:22:43 +0200 Subject: [PATCH 01/71] HotFix user data connection Create local store for user connected in SSO Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 19 +++++--- .../src/Controller/AuthenticateController.ts | 44 +++++++++++++++++-- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index b346f450..b85e045e 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -98,7 +98,7 @@ class ConnectionManager { localUserStore.setCode(code); this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl())); try { - await this.checkAuthUserConnexion(); + await this.checkAuthUserConnexion(this._currentRoom.key); analyticsClient.loggedWithSso(); } catch (err) { console.error(err); @@ -169,7 +169,7 @@ class ConnectionManager { await this.anonymousLogin(); } else { try { - await this.checkAuthUserConnexion(); + await this.checkAuthUserConnexion(this._currentRoom.key); } catch (err) { console.error(err); } @@ -275,7 +275,7 @@ class ConnectionManager { return this.connexionType; } - async checkAuthUserConnexion() { + async checkAuthUserConnexion(playUri: string) { //set connected store for menu at false userIsConnected.set(false); @@ -289,10 +289,17 @@ class ConnectionManager { } const nonce = localUserStore.getNonce(); const token = localUserStore.getAuthToken(); - const { authToken } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce, token } }).then( - (res) => res.data - ); + const { authToken, userUuid, tags, textures, emails } = await Axios.get(`${PUSHER_URL}/login-callback`, { + params: { code, nonce, token, playUri }, + }).then((res) => res.data); localUserStore.setAuthToken(authToken); + const localUser: LocalUser = { + uuid: userUuid, + textures: textures, + email: emails, + }; + this.localUser = new LocalUser(userUuid, textures, emails); + localUserStore.saveUser(this.localUser); this.authToken = authToken; //user connected, set connected store for menu at true diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 972cc102..3d01d30c 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -1,7 +1,7 @@ import { v4 } from "uuid"; import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js"; import { BaseController } from "./BaseController"; -import { adminApi } from "../Services/AdminApi"; +import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi"; import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; import { parse } from "query-string"; import { openIDClient } from "../Services/OpenIDClient"; @@ -55,7 +55,8 @@ export class AuthenticateController extends BaseController { res.onAborted(() => { console.warn("/message request was aborted"); }); - const { code, nonce, token } = parse(req.getQuery()); + const IPAddress = req.getHeader("x-forwarded-for"); + const { code, nonce, token, playUri } = parse(req.getQuery()); try { //verify connected by token if (token != undefined) { @@ -65,9 +66,17 @@ export class AuthenticateController extends BaseController { throw Error("Token cannot to be check on Hydra"); } await openIDClient.checkTokenAuth(authTokenData.hydraAccessToken); + + //Get user data from Admin Back Office + //This is very important to create User Local in LocalStorage in WorkAdventure + const data = await this.getUserByUserIdentifier( + authTokenData.identifier, + playUri as string, + IPAddress + ); res.writeStatus("200"); this.addCorsHeaders(res); - return res.end(JSON.stringify({ authToken: token })); + return res.end(JSON.stringify({ ...data, authToken: token })); } catch (err) { console.info("User was not connected", err); } @@ -80,9 +89,14 @@ export class AuthenticateController extends BaseController { throw new Error("No email in the response"); } const authToken = jwtTokenManager.createAuthToken(email, userInfo.access_token); + + //Get user data from Admin Back Office + //This is very important to create User Local in LocalStorage in WorkAdventure + const data = await this.getUserByUserIdentifier(email, playUri as string, IPAddress); + res.writeStatus("200"); this.addCorsHeaders(res); - return res.end(JSON.stringify({ authToken })); + return res.end(JSON.stringify({ ...data, authToken })); } catch (e) { console.error("openIDCallback => ERROR", e); return this.errorToResponse(e, res); @@ -223,4 +237,26 @@ export class AuthenticateController extends BaseController { } }); } + + /** + * + * @param email + * @param playUri + * @param IPAddress + * @return FetchMemberDataByUuidResponse|object + * @private + */ + private async getUserByUserIdentifier( + email: string, + playUri: string, + IPAddress: string + ): Promise { + let data: FetchMemberDataByUuidResponse | object = {}; + try { + data = await adminApi.fetchMemberDataByUuid(email, playUri, IPAddress); + } catch (err) { + console.error("openIDCallback => fetchMemberDataByUuid", err); + } + return data; + } } From b7692dd3550ad1ec1b5f3726795cadbd2ba5ca4e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Thu, 14 Oct 2021 17:25:36 +0200 Subject: [PATCH 02/71] Fix local user useless Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index b85e045e..06c20a5c 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -293,11 +293,6 @@ class ConnectionManager { params: { code, nonce, token, playUri }, }).then((res) => res.data); localUserStore.setAuthToken(authToken); - const localUser: LocalUser = { - uuid: userUuid, - textures: textures, - email: emails, - }; this.localUser = new LocalUser(userUuid, textures, emails); localUserStore.saveUser(this.localUser); this.authToken = authToken; From 497a7c3467aed14577ce1762f9197614095b410e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Thu, 14 Oct 2021 17:33:53 +0200 Subject: [PATCH 03/71] Fix typo emails => email Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 06c20a5c..1995759e 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -289,11 +289,11 @@ class ConnectionManager { } const nonce = localUserStore.getNonce(); const token = localUserStore.getAuthToken(); - const { authToken, userUuid, tags, textures, emails } = await Axios.get(`${PUSHER_URL}/login-callback`, { + const { authToken, userUuid, textures, email } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce, token, playUri }, }).then((res) => res.data); localUserStore.setAuthToken(authToken); - this.localUser = new LocalUser(userUuid, textures, emails); + this.localUser = new LocalUser(userUuid, textures, email); localUserStore.saveUser(this.localUser); this.authToken = authToken; From 6832fe49901ce28b3413000337a142cc9dae445c Mon Sep 17 00:00:00 2001 From: Lurkars Date: Thu, 21 Oct 2021 16:28:57 +0200 Subject: [PATCH 04/71] use OIDC without admin api, option to disable anonymous login --- .env.template | 1 + docker-compose.single-domain.yaml | 1 + docker-compose.yaml | 1 + front/src/Connexion/ConnectionManager.ts | 34 ++++++++++++++----- .../src/Controller/AuthenticateController.ts | 26 ++++++++------ pusher/src/Controller/IoSocketController.ts | 7 +++- pusher/src/Controller/MapController.ts | 9 +++-- pusher/src/Enum/EnvironmentVariable.ts | 1 + .../src/Services/AdminApi/MapDetailsData.ts | 1 + 9 files changed, 59 insertions(+), 22 deletions(-) diff --git a/.env.template b/.env.template index 715ebeec..f511b398 100644 --- a/.env.template +++ b/.env.template @@ -22,6 +22,7 @@ MAX_USERNAME_LENGTH=8 OPID_CLIENT_ID= OPID_CLIENT_SECRET= OPID_CLIENT_ISSUER= +DISABLE_ANONYMOUS= # If you want to have a contact page in your menu, you MUST set CONTACT_URL to the URL of the page that you want CONTACT_URL= \ No newline at end of file diff --git a/docker-compose.single-domain.yaml b/docker-compose.single-domain.yaml index 4e85d702..2e2cab7d 100644 --- a/docker-compose.single-domain.yaml +++ b/docker-compose.single-domain.yaml @@ -70,6 +70,7 @@ services: OPID_CLIENT_ID: $OPID_CLIENT_ID OPID_CLIENT_SECRET: $OPID_CLIENT_SECRET OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER + DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS volumes: - ./pusher:/usr/src/app labels: diff --git a/docker-compose.yaml b/docker-compose.yaml index 4b3904dd..65dcc61f 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -70,6 +70,7 @@ services: OPID_CLIENT_ID: $OPID_CLIENT_ID OPID_CLIENT_SECRET: $OPID_CLIENT_SECRET OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER + DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS volumes: - ./pusher:/usr/src/app labels: diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index b346f450..c91e4590 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -42,11 +42,22 @@ class ConnectionManager { localUserStore.setAuthToken(null); //TODO fix me to redirect this URL by pusher - if (!this._currentRoom || !this._currentRoom.iframeAuthentication) { + if (!this._currentRoom) { loginSceneVisibleIframeStore.set(false); return null; } - const redirectUrl = new URL(`${this._currentRoom.iframeAuthentication}`); + + // also allow OIDC login without admin API by using pusher + let redirectUrl: URL; + if (this._currentRoom.iframeAuthentication) { + redirectUrl = new URL(`${this._currentRoom.iframeAuthentication}`); + } else { + // need origin if PUSHER_URL is relative (in Single-Domain-Deployment) + redirectUrl = new URL( + `${PUSHER_URL}/login-screen`, + !PUSHER_URL.startsWith("http:") || !PUSHER_URL.startsWith("https:") ? window.location.origin : undefined + ); + } redirectUrl.searchParams.append("state", state); redirectUrl.searchParams.append("nonce", nonce); redirectUrl.searchParams.append("playUri", this._currentRoom.key); @@ -204,13 +215,18 @@ class ConnectionManager { } public async anonymousLogin(isBenchmark: boolean = false): Promise { - const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); - this.localUser = new LocalUser(data.userUuid, []); - this.authToken = data.authToken; - if (!isBenchmark) { - // In benchmark, we don't have a local storage. - localUserStore.saveUser(this.localUser); - localUserStore.setAuthToken(this.authToken); + try { + const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); + this.localUser = new LocalUser(data.userUuid, []); + this.authToken = data.authToken; + if (!isBenchmark) { + // In benchmark, we don't have a local storage. + localUserStore.saveUser(this.localUser); + localUserStore.setAuthToken(this.authToken); + } + } catch (error) { + // anonymous login failed (through 403 DISABLE_ANONYMOUS) + this.loadOpenIDScreen(); } } diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 972cc102..2dafe065 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -5,6 +5,7 @@ import { adminApi } from "../Services/AdminApi"; import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; import { parse } from "query-string"; import { openIDClient } from "../Services/OpenIDClient"; +import { DISABLE_ANONYMOUS } from "../Enum/EnvironmentVariable"; export interface TokenInterface { userUuid: string; @@ -175,16 +176,21 @@ export class AuthenticateController extends BaseController { console.warn("Login request was aborted"); }); - const userUuid = v4(); - const authToken = jwtTokenManager.createAuthToken(userUuid); - res.writeStatus("200 OK"); - this.addCorsHeaders(res); - res.end( - JSON.stringify({ - authToken, - userUuid, - }) - ); + if (DISABLE_ANONYMOUS) { + res.writeStatus("403 FORBIDDEN"); + res.end(); + } else { + const userUuid = v4(); + const authToken = jwtTokenManager.createAuthToken(userUuid); + res.writeStatus("200 OK"); + this.addCorsHeaders(res); + res.end( + JSON.stringify({ + authToken, + userUuid, + }) + ); + } }); } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 0466100c..500f4142 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -26,7 +26,7 @@ import { jwtTokenManager, tokenInvalidException } from "../Services/JWTTokenMana import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi"; import { SocketManager, socketManager } from "../Services/SocketManager"; import { emitInBatch } from "../Services/IoSocketHelpers"; -import { ADMIN_API_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable"; +import { ADMIN_API_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER, DISABLE_ANONYMOUS } from "../Enum/EnvironmentVariable"; import { Zone } from "_Model/Zone"; import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; import { v4 } from "uuid"; @@ -175,6 +175,11 @@ export class IoSocketController { const tokenData = token && typeof token === "string" ? jwtTokenManager.verifyJWTToken(token) : null; + + if (DISABLE_ANONYMOUS && !tokenData) { + throw new Error("Expecting token"); + } + const userIdentifier = tokenData ? tokenData.identifier : ""; let memberTags: string[] = []; diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index f775b50c..437bac6a 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -2,9 +2,9 @@ import { HttpRequest, HttpResponse, TemplatedApp } from "uWebSockets.js"; import { BaseController } from "./BaseController"; import { parse } from "query-string"; import { adminApi } from "../Services/AdminApi"; -import { ADMIN_API_URL } from "../Enum/EnvironmentVariable"; +import { ADMIN_API_URL, DISABLE_ANONYMOUS } from "../Enum/EnvironmentVariable"; import { GameRoomPolicyTypes } from "../Model/PusherRoom"; -import { MapDetailsData } from "../Services/AdminApi/MapDetailsData"; +import { isMapDetailsData, MapDetailsData } from "../Services/AdminApi/MapDetailsData"; import { socketManager } from "../Services/SocketManager"; import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; import { v4 } from "uuid"; @@ -64,6 +64,7 @@ export class MapController extends BaseController { tags: [], textures: [], contactPage: undefined, + authenticationMandatory: DISABLE_ANONYMOUS, } as MapDetailsData) ); @@ -87,6 +88,10 @@ export class MapController extends BaseController { } const mapDetails = await adminApi.fetchMapDetails(query.playUri as string, userId); + if (isMapDetailsData(mapDetails) && DISABLE_ANONYMOUS) { + (mapDetails as MapDetailsData).authenticationMandatory = true; + } + res.writeStatus("200 OK"); this.addCorsHeaders(res); res.end(JSON.stringify(mapDetails)); diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index ab1ce110..be81871d 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -15,6 +15,7 @@ export const FRONT_URL = process.env.FRONT_URL || "http://localhost"; export const OPID_CLIENT_ID = process.env.OPID_CLIENT_ID || ""; export const OPID_CLIENT_SECRET = process.env.OPID_CLIENT_SECRET || ""; export const OPID_CLIENT_ISSUER = process.env.OPID_CLIENT_ISSUER || ""; +export const DISABLE_ANONYMOUS = process.env.DISABLE_ANONYMOUS ? process.env.DISABLE_ANONYMOUS == "true" : false; export { SECRET_KEY, diff --git a/pusher/src/Services/AdminApi/MapDetailsData.ts b/pusher/src/Services/AdminApi/MapDetailsData.ts index 278b81bb..7a1f57ff 100644 --- a/pusher/src/Services/AdminApi/MapDetailsData.ts +++ b/pusher/src/Services/AdminApi/MapDetailsData.ts @@ -16,6 +16,7 @@ export const isMapDetailsData = new tg.IsInterface() tags: tg.isArray(tg.isString), textures: tg.isArray(isCharacterTexture), contactPage: tg.isUnion(tg.isString, tg.isUndefined), + authenticationMandatory: tg.isUnion(tg.isBoolean, tg.isUndefined), }) .get(); From 78e0d1bead402348f36a3341ceea68b3f8e462e3 Mon Sep 17 00:00:00 2001 From: Lurkars Date: Thu, 21 Oct 2021 16:53:24 +0200 Subject: [PATCH 05/71] fix linter, cleanup --- front/src/Connexion/ConnectionManager.ts | 19 +++++++------------ pusher/src/Controller/MapController.ts | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index c91e4590..aae8440a 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -215,18 +215,13 @@ class ConnectionManager { } public async anonymousLogin(isBenchmark: boolean = false): Promise { - try { - const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); - this.localUser = new LocalUser(data.userUuid, []); - this.authToken = data.authToken; - if (!isBenchmark) { - // In benchmark, we don't have a local storage. - localUserStore.saveUser(this.localUser); - localUserStore.setAuthToken(this.authToken); - } - } catch (error) { - // anonymous login failed (through 403 DISABLE_ANONYMOUS) - this.loadOpenIDScreen(); + const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); + this.localUser = new LocalUser(data.userUuid, []); + this.authToken = data.authToken; + if (!isBenchmark) { + // In benchmark, we don't have a local storage. + localUserStore.saveUser(this.localUser); + localUserStore.setAuthToken(this.authToken); } } diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index 437bac6a..18748d9e 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -89,7 +89,7 @@ export class MapController extends BaseController { const mapDetails = await adminApi.fetchMapDetails(query.playUri as string, userId); if (isMapDetailsData(mapDetails) && DISABLE_ANONYMOUS) { - (mapDetails as MapDetailsData).authenticationMandatory = true; + mapDetails.authenticationMandatory = true; } res.writeStatus("200 OK"); From d809682c08c71f9860b883c70de66275fe8ec6f3 Mon Sep 17 00:00:00 2001 From: Kharhamel Date: Tue, 26 Oct 2021 14:58:34 +0200 Subject: [PATCH 06/71] HOTFIX: now uses a specific secret token for the admin sockets --- deeployer.libsonnet | 1 + pusher/src/Controller/IoSocketController.ts | 5 ++--- pusher/src/Enum/EnvironmentVariable.ts | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/deeployer.libsonnet b/deeployer.libsonnet index d3320bc0..c4b34e38 100644 --- a/deeployer.libsonnet +++ b/deeployer.libsonnet @@ -62,6 +62,7 @@ } + (if adminUrl != null then { "ADMIN_API_URL": adminUrl, "ADMIN_API_TOKEN": env.ADMIN_API_TOKEN, + "ADMIN_SOCKETS_TOKEN": env.ADMIN_SOCKETS_TOKEN, } else {}) }, "front": { diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 0466100c..c2aded67 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -26,10 +26,9 @@ import { jwtTokenManager, tokenInvalidException } from "../Services/JWTTokenMana import { adminApi, FetchMemberDataByUuidResponse } from "../Services/AdminApi"; import { SocketManager, socketManager } from "../Services/SocketManager"; import { emitInBatch } from "../Services/IoSocketHelpers"; -import { ADMIN_API_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable"; +import { ADMIN_SOCKETS_TOKEN, ADMIN_API_URL, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable"; import { Zone } from "_Model/Zone"; import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; -import { v4 } from "uuid"; import { CharacterTexture } from "../Services/AdminApi/CharacterTexture"; export class IoSocketController { @@ -48,7 +47,7 @@ export class IoSocketController { const websocketProtocol = req.getHeader("sec-websocket-protocol"); const websocketExtensions = req.getHeader("sec-websocket-extensions"); const token = query.token; - if (token !== ADMIN_API_TOKEN) { + if (token !== ADMIN_SOCKETS_TOKEN) { console.log("Admin access refused for token: " + token); res.writeStatus("401 Unauthorized").end("Incorrect token"); return; diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index ab1ce110..ad369a17 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -4,6 +4,7 @@ const API_URL = process.env.API_URL || ""; const ADMIN_API_URL = process.env.ADMIN_API_URL || ""; const ADMIN_URL = process.env.ADMIN_URL || ""; const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN || "myapitoken"; +export const ADMIN_SOCKETS_TOKEN = process.env.ADMIN_SOCKETS_TOKEN || "myapitoken"; const CPU_OVERHEAT_THRESHOLD = Number(process.env.CPU_OVERHEAT_THRESHOLD) || 80; const JITSI_URL: string | undefined = process.env.JITSI_URL === "" ? undefined : process.env.JITSI_URL; const JITSI_ISS = process.env.JITSI_ISS || ""; From f8ae189cee2e0f1d8133fcde39b220da5d1e045e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 27 Oct 2021 14:29:39 +0200 Subject: [PATCH 07/71] HotFix handler message in chat store Signed-off-by: Gregoire Parant --- front/src/Api/IframeListener.ts | 2 +- front/src/Stores/ChatStore.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 5a9aca85..668b2e94 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -284,7 +284,7 @@ class IframeListener { registerScript(scriptUrl: string): Promise { return new Promise((resolve, reject) => { - console.log("Loading map related script at ", scriptUrl); + console.info("Loading map related script at ", scriptUrl); if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") { // Using external iframe mode ( diff --git a/front/src/Stores/ChatStore.ts b/front/src/Stores/ChatStore.ts index feb1f3ec..0cda645d 100644 --- a/front/src/Stores/ChatStore.ts +++ b/front/src/Stores/ChatStore.ts @@ -1,6 +1,7 @@ import { writable } from "svelte/store"; import { playersStore } from "./PlayersStore"; import type { PlayerInterface } from "../Phaser/Game/PlayerInterface"; +import { iframeListener } from "../Api/IframeListener"; export const chatVisibilityStore = writable(false); export const chatInputFocusStore = writable(false); @@ -66,6 +67,9 @@ function createChatMessagesStore() { }); }, addPersonnalMessage(text: string) { + //post message iframe listener + iframeListener.sendUserInputChat(text); + newChatMessageStore.set(text); update((list) => { const lastMessage = list[list.length - 1]; From 6c0d8942e54081adf5e25dcfa2858a1611b12da3 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 27 Oct 2021 16:48:33 +0200 Subject: [PATCH 08/71] HotFix set last room for first connexion Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 1995759e..70d29f08 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -164,6 +164,9 @@ class ConnectionManager { //before set token of user we must load room and all information. For example the mandatory authentication could be require on current room this._currentRoom = await Room.createRoom(new URL(roomPath)); + //defined last room url this room path + localUserStore.setLastRoomUrl(this._currentRoom.key); + //todo: add here some kind of warning if authToken has expired. if (!this.authToken && !this._currentRoom.authenticationMandatory) { await this.anonymousLogin(); From 6c78717d9790775fb05575ee3dfc02f56b4c484b Mon Sep 17 00:00:00 2001 From: Kharhamel Date: Thu, 28 Oct 2021 14:53:07 +0200 Subject: [PATCH 09/71] FIX: the admin sockets now uses a short live to check room authorization --- pusher/src/Controller/IoSocketController.ts | 16 +++++++++------- pusher/src/Services/JWTTokenManager.ts | 9 ++++++++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index c2aded67..7cfd9bd7 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -47,15 +47,19 @@ export class IoSocketController { const websocketProtocol = req.getHeader("sec-websocket-protocol"); const websocketExtensions = req.getHeader("sec-websocket-extensions"); const token = query.token; - if (token !== ADMIN_SOCKETS_TOKEN) { - console.log("Admin access refused for token: " + token); + let authorizedRoomIds: string[]; + try { + const data = jwtTokenManager.verifyAdminSocketToken(token as string); + authorizedRoomIds = data.authorizedRoomIds; + } catch (e) { + console.error("Admin access refused for token: " + token); res.writeStatus("401 Unauthorized").end("Incorrect token"); return; } const roomId = query.roomId; - if (typeof roomId !== "string") { - console.error("Received"); - res.writeStatus("400 Bad Request").end("Missing room id"); + if (typeof roomId !== "string" || !authorizedRoomIds.includes(roomId)) { + console.error("Invalid room id"); + res.writeStatus("403 Bad Request").end("Invalid room id"); return; } @@ -69,8 +73,6 @@ export class IoSocketController { }, message: (ws, arrayBuffer, isBinary): void => { try { - const roomId = ws.roomId as string; - //TODO refactor message type and data const message: { event: string; message: { type: string; message: unknown; userUuid: string } } = JSON.parse(new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer))); diff --git a/pusher/src/Services/JWTTokenManager.ts b/pusher/src/Services/JWTTokenManager.ts index 24393084..fe418475 100644 --- a/pusher/src/Services/JWTTokenManager.ts +++ b/pusher/src/Services/JWTTokenManager.ts @@ -1,4 +1,4 @@ -import { ADMIN_API_URL, ALLOW_ARTILLERY, SECRET_KEY } from "../Enum/EnvironmentVariable"; +import { ADMIN_API_URL, ADMIN_SOCKETS_TOKEN, ALLOW_ARTILLERY, SECRET_KEY } from "../Enum/EnvironmentVariable"; import { uuid } from "uuidv4"; import Jwt, { verify } from "jsonwebtoken"; import { TokenInterface } from "../Controller/AuthenticateController"; @@ -8,9 +8,16 @@ export interface AuthTokenData { identifier: string; //will be a email if logged in or an uuid if anonymous hydraAccessToken?: string; } +export interface AdminSocketTokenData { + authorizedRoomIds: string[]; //the list of rooms the client is authorized to read from. +} export const tokenInvalidException = "tokenInvalid"; class JWTTokenManager { + public verifyAdminSocketToken(token: string): AdminSocketTokenData { + return Jwt.verify(token, ADMIN_SOCKETS_TOKEN) as AdminSocketTokenData; + } + public createAuthToken(identifier: string, hydraAccessToken?: string) { return Jwt.sign({ identifier, hydraAccessToken }, SECRET_KEY, { expiresIn: "30d" }); } From f20c39f6a4605130eca4a49750845d09e770a22e Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Wed, 3 Nov 2021 16:51:33 +0100 Subject: [PATCH 10/71] wip --- maps/tests/TriggerMessageApi/script.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/maps/tests/TriggerMessageApi/script.js b/maps/tests/TriggerMessageApi/script.js index 9ab02ec2..a09b7653 100644 --- a/maps/tests/TriggerMessageApi/script.js +++ b/maps/tests/TriggerMessageApi/script.js @@ -1,16 +1,19 @@ WA.onInit().then(() => { - let message; + let message; + console.log('the right place!') - WA.room.onEnterZone("carpet", () => { - message = WA.ui.displayActionMessage({ - message: "This is a test message. Press space to display a chat message. Walk out to hide the message.", - callback: () => { - WA.chat.sendChatMessage("Hello world!", "The bot"); - } - }); + WA.room.onEnterLayer("carpet").subscribe(() => { + message = WA.ui.displayActionMessage({ + message: + "This is a bis test message. Press space to display a chat message. Walk out to hide the message.", + callback: () => { + WA.chat.sendChatMessage("Hello world!", "The bot"); + }, }); + }); - WA.room.onLeaveZone("carpet", () => { - message && message.remove(); - }); + + WA.room.onLeaveLayer("carpet").subscribe(() => { + message && message.remove(); + }); }); From 854d8775d5ab12ff839e3ba3a6bc72ab63aa82b3 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Wed, 3 Nov 2021 19:24:24 +0100 Subject: [PATCH 11/71] Edit documentation exemples --- docs/maps/api-controls.md | 4 ++-- docs/maps/api-ui.md | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/maps/api-controls.md b/docs/maps/api-controls.md index dcb0f17b..c2b47262 100644 --- a/docs/maps/api-controls.md +++ b/docs/maps/api-controls.md @@ -15,7 +15,7 @@ When controls are disabled, the user cannot move anymore using keyboard input. T Example: ```javascript -WA.room.onEnterZone('myZone', () => { +WA.room.onEnterLayer('myZone').subscribe(() => { WA.controls.disablePlayerControls(); WA.ui.openPopup("popupRectangle", 'This is an imporant message!', [{ label: "Got it!", @@ -25,5 +25,5 @@ WA.room.onEnterZone('myZone', () => { popup.close(); } }]); -}); +}) ``` diff --git a/docs/maps/api-ui.md b/docs/maps/api-ui.md index dc701500..8583c061 100644 --- a/docs/maps/api-ui.md +++ b/docs/maps/api-ui.md @@ -49,7 +49,7 @@ Example: let helloWorldPopup; // Open the popup when we enter a given zone -helloWorldPopup = WA.room.onEnterZone('myZone', () => { +helloWorldPopup = WA.room.onEnterLayer("myZone").subscribe(() => { WA.ui.openPopup("popupRectangle", 'Hello world!', [{ label: "Close", className: "primary", @@ -57,13 +57,13 @@ helloWorldPopup = WA.room.onEnterZone('myZone', () => { // Close the popup when the "Close" button is pressed. popup.close(); } - }); -}]); + }]); +}); // Close the popup when we leave the zone. -WA.room.onLeaveZone('myZone', () => { +WA.room.onLeaveLayer("myZone").subscribe(() => { helloWorldPopup.close(); -}); +}) ``` ### Add custom menu From 7da975749c025807bfde287c364d34ee73efc428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dicte=20Q?= <37311765+HimeShaman@users.noreply.github.com> Date: Mon, 8 Nov 2021 10:51:08 +0100 Subject: [PATCH 12/71] Update maps/tests/TriggerMessageApi/script.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Négrier --- maps/tests/TriggerMessageApi/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maps/tests/TriggerMessageApi/script.js b/maps/tests/TriggerMessageApi/script.js index a09b7653..143f418b 100644 --- a/maps/tests/TriggerMessageApi/script.js +++ b/maps/tests/TriggerMessageApi/script.js @@ -5,7 +5,7 @@ WA.onInit().then(() => { WA.room.onEnterLayer("carpet").subscribe(() => { message = WA.ui.displayActionMessage({ message: - "This is a bis test message. Press space to display a chat message. Walk out to hide the message.", + "This is a test message. Press space to display a chat message. Walk out to hide the message.", callback: () => { WA.chat.sendChatMessage("Hello world!", "The bot"); }, From b9bf27983f839ccdb49753451d05776e8e6c3bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dicte=20Q?= <37311765+HimeShaman@users.noreply.github.com> Date: Mon, 8 Nov 2021 10:51:18 +0100 Subject: [PATCH 13/71] Update maps/tests/TriggerMessageApi/script.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Négrier --- maps/tests/TriggerMessageApi/script.js | 1 - 1 file changed, 1 deletion(-) diff --git a/maps/tests/TriggerMessageApi/script.js b/maps/tests/TriggerMessageApi/script.js index 143f418b..4041d98b 100644 --- a/maps/tests/TriggerMessageApi/script.js +++ b/maps/tests/TriggerMessageApi/script.js @@ -1,6 +1,5 @@ WA.onInit().then(() => { let message; - console.log('the right place!') WA.room.onEnterLayer("carpet").subscribe(() => { message = WA.ui.displayActionMessage({ From 4c028bfcb3f64748b310645eadadb6163b7b00e8 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 8 Nov 2021 19:27:01 +0100 Subject: [PATCH 14/71] OpenId from Admin connect - Create admin environment for redirect uri of openID - Add log out redirect when user click on log out button Signed-off-by: Gregoire Parant --- pusher/src/Enum/EnvironmentVariable.ts | 1 + pusher/src/Services/AdminApi.ts | 4 ++++ pusher/src/Services/OpenIDClient.ts | 33 +++++++++++++------------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index ab1ce110..22c4db4f 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -15,6 +15,7 @@ export const FRONT_URL = process.env.FRONT_URL || "http://localhost"; export const OPID_CLIENT_ID = process.env.OPID_CLIENT_ID || ""; export const OPID_CLIENT_SECRET = process.env.OPID_CLIENT_SECRET || ""; export const OPID_CLIENT_ISSUER = process.env.OPID_CLIENT_ISSUER || ""; +export const OPID_CLIENT_REDIREC_URL = process.env.OPID_CLIENT_REDIREC_URL || FRONT_URL + "/jwt"; export { SECRET_KEY, diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index e53d00ae..d002ff8b 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -150,6 +150,10 @@ class AdminApi { return ADMIN_URL + `/profile?token=${accessToken}`; } + + async logoutOauth(token: string) { + await Axios.get(ADMIN_API_URL + `/oauth/logout?token=${token}`); + } } export const adminApi = new AdminApi(); diff --git a/pusher/src/Services/OpenIDClient.ts b/pusher/src/Services/OpenIDClient.ts index c9137ad5..1a475224 100644 --- a/pusher/src/Services/OpenIDClient.ts +++ b/pusher/src/Services/OpenIDClient.ts @@ -1,7 +1,10 @@ import { Issuer, Client, IntrospectionResponse } from "openid-client"; -import { OPID_CLIENT_ID, OPID_CLIENT_SECRET, OPID_CLIENT_ISSUER, FRONT_URL } from "../Enum/EnvironmentVariable"; - -const opidRedirectUri = FRONT_URL + "/jwt"; +import { + OPID_CLIENT_ID, + OPID_CLIENT_SECRET, + OPID_CLIENT_ISSUER, + OPID_CLIENT_REDIREC_URL, +} from "../Enum/EnvironmentVariable"; class OpenIDClient { private issuerPromise: Promise | null = null; @@ -12,7 +15,7 @@ class OpenIDClient { return new issuer.Client({ client_id: OPID_CLIENT_ID, client_secret: OPID_CLIENT_SECRET, - redirect_uris: [opidRedirectUri], + redirect_uris: [OPID_CLIENT_REDIREC_URL], response_types: ["code"], }); }); @@ -20,30 +23,26 @@ class OpenIDClient { return this.issuerPromise; } - public authorizationUrl(state: string, nonce: string, playUri?: string, redirect?: string) { + public authorizationUrl(playUri?: string, redirect?: string) { return this.initClient().then((client) => { return client.authorizationUrl({ scope: "openid email", prompt: "login", - state: state, - nonce: nonce, playUri: playUri, redirect: redirect, }); }); } - public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string }> { + public getUserInfo(accessToken: string): Promise<{ email: string; sub: string; access_token: string }> { return this.initClient().then((client) => { - return client.callback(opidRedirectUri, { code }, { nonce }).then((tokenSet) => { - return client.userinfo(tokenSet).then((res) => { - return { - ...res, - email: res.email as string, - sub: res.sub, - access_token: tokenSet.access_token as string, - }; - }); + return client.userinfo(accessToken).then((res) => { + return { + ...res, + email: res.email as string, + sub: res.sub, + access_token: accessToken as string, + }; }); }); } From 89baafba2fb69f8252d49958f1a9c455a5251047 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 9 Nov 2021 00:08:01 +0100 Subject: [PATCH 15/71] Rollback openid connect to use code and nonce Signed-off-by: Gregoire Parant --- front/src/Connexion/LocalUserStore.ts | 4 ++++ pusher/src/Services/OpenIDClient.ts | 22 +++++++++++++--------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 6c20aadb..a113291d 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -165,6 +165,10 @@ class LocalUserStore { verifyState(value: string): boolean { const oldValue = localStorage.getItem(state); + if (!oldValue) { + localStorage.setItem(state, value); + return true; + } return oldValue === value; } getState(): string | null { diff --git a/pusher/src/Services/OpenIDClient.ts b/pusher/src/Services/OpenIDClient.ts index 1a475224..bc0dd6c9 100644 --- a/pusher/src/Services/OpenIDClient.ts +++ b/pusher/src/Services/OpenIDClient.ts @@ -23,26 +23,30 @@ class OpenIDClient { return this.issuerPromise; } - public authorizationUrl(playUri?: string, redirect?: string) { + public authorizationUrl(state: string, nonce: string, playUri?: string, redirect?: string) { return this.initClient().then((client) => { return client.authorizationUrl({ scope: "openid email", prompt: "login", + state: state, + nonce: nonce, playUri: playUri, redirect: redirect, }); }); } - public getUserInfo(accessToken: string): Promise<{ email: string; sub: string; access_token: string }> { + public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string }> { return this.initClient().then((client) => { - return client.userinfo(accessToken).then((res) => { - return { - ...res, - email: res.email as string, - sub: res.sub, - access_token: accessToken as string, - }; + return client.callback(OPID_CLIENT_REDIREC_URL, { code }, { nonce }).then((tokenSet) => { + return client.userinfo(tokenSet).then((res) => { + return { + ...res, + email: res.email as string, + sub: res.sub, + access_token: tokenSet.access_token as string, + }; + }); }); }); } From 7406b620939af5d508144722437ef1965cc26bd4 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 9 Nov 2021 00:38:32 +0100 Subject: [PATCH 16/71] Add jwt token if is defined un URL Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index b346f450..032a60f1 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -87,6 +87,8 @@ class ConnectionManager { urlManager.pushRoomIdToUrl(this._currentRoom); } else if (connexionType === GameConnexionTypes.jwt) { const urlParams = new URLSearchParams(window.location.search); + + //TODO if jwt is defined, state and nonce can to be deleted const code = urlParams.get("code"); const state = urlParams.get("state"); if (!state || !localUserStore.verifyState(state)) { @@ -96,6 +98,12 @@ class ConnectionManager { throw "No Auth code provided"; } localUserStore.setCode(code); + + const jwt = urlParams.get("jwt"); + if (jwt) { + this.authToken = jwt; + localUserStore.setAuthToken(jwt); + } this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl())); try { await this.checkAuthUserConnexion(); From 1c48feeb61c9550b2f51edecfbb64b97f791daba Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Wed, 10 Nov 2021 15:20:04 +0100 Subject: [PATCH 17/71] Add iconserver in services --- docker-compose.yaml | 12 ++ front/src/Enum/EnvironmentVariable.ts | 2 + front/src/WebRtc/CoWebsiteManager.ts | 169 +++++++++++++------------- front/webpack.config.ts | 1 + 4 files changed, 99 insertions(+), 85 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 4b3904dd..f68ed6a0 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -29,6 +29,7 @@ services: PUSHER_URL: //pusher.workadventure.localhost UPLOADER_URL: //uploader.workadventure.localhost ADMIN_URL: //workadventure.localhost + ICON_URL: //icon.workadventure.localhost STARTUP_COMMAND_1: ./templater.sh STARTUP_COMMAND_2: yarn install STUN_SERVER: "stun:stun.l.google.com:19302" @@ -177,6 +178,17 @@ services: - "traefik.http.routers.redisinsight-ssl.tls=true" - "traefik.http.routers.redisinsight-ssl.service=redisinsight" + icon: + image: matthiasluedtke/iconserver:v3.13.0 + labels: + - "traefik.http.routers.icon.rule=Host(`icon.workadventure.localhost`)" + - "traefik.http.routers.icon.entryPoints=web" + - "traefik.http.services.icon.loadbalancer.server.port=8080" + - "traefik.http.routers.icon-ssl.rule=Host(`icon.workadventure.localhost`)" + - "traefik.http.routers.icon-ssl.entryPoints=websecure" + - "traefik.http.routers.icon-ssl.tls=true" + - "traefik.http.routers.icon-ssl.service=icon" + # coturn: # image: coturn/coturn:4.5.2 # command: diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts index c0c19f1c..cf76a87d 100644 --- a/front/src/Enum/EnvironmentVariable.ts +++ b/front/src/Enum/EnvironmentVariable.ts @@ -4,6 +4,7 @@ const START_ROOM_URL: string = const PUSHER_URL = process.env.PUSHER_URL || "//pusher.workadventure.localhost"; export const ADMIN_URL = process.env.ADMIN_URL || "//workadventu.re"; const UPLOADER_URL = process.env.UPLOADER_URL || "//uploader.workadventure.localhost"; +const ICON_URL = process.env.ICON_URL || "//icon.workadventure.localhost"; const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302"; const TURN_SERVER: string = process.env.TURN_SERVER || ""; const SKIP_RENDER_OPTIMIZATIONS: boolean = process.env.SKIP_RENDER_OPTIMIZATIONS == "true"; @@ -32,6 +33,7 @@ export { DISABLE_NOTIFICATIONS, PUSHER_URL, UPLOADER_URL, + ICON_URL, POSITION_DELAY, MAX_EXTRAPOLATION_TIME, STUN_SERVER, diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 823e138b..23dd7e2a 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -3,6 +3,7 @@ import { Subject } from "rxjs"; import { iframeListener } from "../Api/IframeListener"; import { touchScreenManager } from "../Touch/TouchScreenManager"; import { waScaleManager } from "../Phaser/Services/WaScaleManager"; +import { ICON_URL } from "../Enum/EnvironmentVariable"; enum iframeStates { closed = 1, @@ -27,16 +28,16 @@ interface TouchMoveCoordinates { y: number; } -export type CoWebsite = { - iframe: HTMLIFrameElement, - icon: HTMLDivElement, - position: number -} +export type CoWebsite = { + iframe: HTMLIFrameElement; + icon: HTMLDivElement; + position: number; +}; type CoWebsiteSlot = { - container: HTMLElement, - position: number -} + container: HTMLElement; + position: number; +}; class CoWebsiteManager { private openedMain: iframeStates = iframeStates.closed; @@ -61,7 +62,7 @@ class CoWebsiteManager { private slots: CoWebsiteSlot[]; - private resizeObserver = new ResizeObserver(entries => { + private resizeObserver = new ResizeObserver((entries) => { this.resizeAllIframes(); }); @@ -108,23 +109,23 @@ class CoWebsiteManager { this.slots = [ { container: this.cowebsiteMainDom, - position: 0 + position: 0, }, { - container: HtmlUtils.getElementByIdOrFail('cowebsite-slot-1'), - position: 1 + container: HtmlUtils.getElementByIdOrFail("cowebsite-slot-1"), + position: 1, }, { - container: HtmlUtils.getElementByIdOrFail('cowebsite-slot-2'), - position: 2 + container: HtmlUtils.getElementByIdOrFail("cowebsite-slot-2"), + position: 2, }, { - container: HtmlUtils.getElementByIdOrFail('cowebsite-slot-3'), - position: 3 + container: HtmlUtils.getElementByIdOrFail("cowebsite-slot-3"), + position: 3, }, { - container: HtmlUtils.getElementByIdOrFail('cowebsite-slot-4'), - position: 4 + container: HtmlUtils.getElementByIdOrFail("cowebsite-slot-4"), + position: 4, }, ]; @@ -163,8 +164,10 @@ class CoWebsiteManager { } private isSmallScreen(): boolean { - return window.matchMedia("(max-aspect-ratio: 1/1)").matches || - window.matchMedia("(max-width:960px) and (max-height:768px)").matches; + return ( + window.matchMedia("(max-aspect-ratio: 1/1)").matches || + window.matchMedia("(max-width:960px) and (max-height:768px)").matches + ); } private initResizeListeners(touchMode: boolean) { @@ -235,12 +238,12 @@ class CoWebsiteManager { private initActionsListeners() { this.slots.forEach((slot: CoWebsiteSlot) => { - const expandButton = slot.container.querySelector('.expand'); - const highlightButton = slot.container.querySelector('.hightlight'); - const closeButton = slot.container.querySelector('.close'); + const expandButton = slot.container.querySelector(".expand"); + const highlightButton = slot.container.querySelector(".hightlight"); + const closeButton = slot.container.querySelector(".close"); if (expandButton) { - expandButton.addEventListener('click', (event) => { + expandButton.addEventListener("click", (event) => { event.preventDefault(); const coWebsite = this.getCoWebsiteByPosition(slot.position); @@ -253,7 +256,7 @@ class CoWebsiteManager { } if (highlightButton) { - highlightButton.addEventListener('click', (event) => { + highlightButton.addEventListener("click", (event) => { event.preventDefault(); const coWebsite = this.getCoWebsiteByPosition(slot.position); @@ -266,7 +269,7 @@ class CoWebsiteManager { } if (closeButton) { - closeButton.addEventListener('click', (event) => { + closeButton.addEventListener("click", (event) => { event.preventDefault(); const coWebsite = this.getCoWebsiteByPosition(slot.position); @@ -284,37 +287,37 @@ class CoWebsiteManager { return this.coWebsites; } - public getCoWebsiteById(coWebsiteId: string): CoWebsite|undefined { + public getCoWebsiteById(coWebsiteId: string): CoWebsite | undefined { return this.coWebsites.find((coWebsite: CoWebsite) => coWebsite.iframe.id === coWebsiteId); } - private getSlotByPosition(position: number): CoWebsiteSlot|undefined { + private getSlotByPosition(position: number): CoWebsiteSlot | undefined { return this.slots.find((slot: CoWebsiteSlot) => slot.position === position); } - private getCoWebsiteByPosition(position: number): CoWebsite|undefined { + private getCoWebsiteByPosition(position: number): CoWebsite | undefined { return this.coWebsites.find((coWebsite: CoWebsite) => coWebsite.position === position); } private setIframeOffset(coWebsite: CoWebsite, slot: CoWebsiteSlot) { const bounding = slot.container.getBoundingClientRect(); - if (coWebsite.iframe.classList.contains('thumbnail')) { - coWebsite.iframe.style.width = ((bounding.right - bounding.left) * 2) + 'px'; - coWebsite.iframe.style.height = ((bounding.bottom - bounding.top) * 2) + 'px'; - coWebsite.iframe.style.top = (bounding.top - (Math.floor(bounding.height * 0.5))) + 'px'; - coWebsite.iframe.style.left = (bounding.left - (Math.floor(bounding.width * 0.5))) + 'px'; + if (coWebsite.iframe.classList.contains("thumbnail")) { + coWebsite.iframe.style.width = (bounding.right - bounding.left) * 2 + "px"; + coWebsite.iframe.style.height = (bounding.bottom - bounding.top) * 2 + "px"; + coWebsite.iframe.style.top = bounding.top - Math.floor(bounding.height * 0.5) + "px"; + coWebsite.iframe.style.left = bounding.left - Math.floor(bounding.width * 0.5) + "px"; } else { - coWebsite.iframe.style.top = bounding.top + 'px'; - coWebsite.iframe.style.left = bounding.left + 'px'; - coWebsite.iframe.style.width = (bounding.right - bounding.left) + 'px'; - coWebsite.iframe.style.height = (bounding.bottom - bounding.top) + 'px'; + coWebsite.iframe.style.top = bounding.top + "px"; + coWebsite.iframe.style.left = bounding.left + "px"; + coWebsite.iframe.style.width = bounding.right - bounding.left + "px"; + coWebsite.iframe.style.height = bounding.bottom - bounding.top + "px"; } } private resizeAllIframes() { this.coWebsites.forEach((coWebsite: CoWebsite) => { - const slot = this.getSlotByPosition(coWebsite.position); + const slot = this.getSlotByPosition(coWebsite.position); if (slot) { this.setIframeOffset(coWebsite, slot); @@ -333,34 +336,34 @@ class CoWebsiteManager { coWebsite.iframe.scrolling = newPosition === 0 || newPosition === 1 ? "yes" : "no"; if (newPosition === 0) { - coWebsite.iframe.classList.add('main'); + coWebsite.iframe.classList.add("main"); coWebsite.icon.style.display = "none"; } else { - coWebsite.iframe.classList.remove('main'); + coWebsite.iframe.classList.remove("main"); coWebsite.icon.style.display = "flex"; } if (newPosition === 1) { - coWebsite.iframe.classList.add('sub-main'); + coWebsite.iframe.classList.add("sub-main"); } else { - coWebsite.iframe.classList.remove('sub-main'); + coWebsite.iframe.classList.remove("sub-main"); } if (newPosition >= 2) { - coWebsite.iframe.classList.add('thumbnail'); + coWebsite.iframe.classList.add("thumbnail"); } else { - coWebsite.iframe.classList.remove('thumbnail'); + coWebsite.iframe.classList.remove("thumbnail"); } coWebsite.position = newPosition; if (oldSlot && !this.getCoWebsiteByPosition(oldSlot.position)) { - oldSlot.container.style.display = 'none'; + oldSlot.container.style.display = "none"; } - newSlot.container.style.display = 'block'; + newSlot.container.style.display = "block"; - coWebsite.iframe.classList.remove('pixel'); + coWebsite.iframe.classList.remove("pixel"); this.resizeAllIframes(); } @@ -384,11 +387,7 @@ class CoWebsiteManager { this.moveCoWebsite(coWebsite, newPosition); - if (newPosition === 4 || - !currentCoWebsite || - currentCoWebsite.iframe.id === coWebsite.iframe.id - ) - { + if (newPosition === 4 || !currentCoWebsite || currentCoWebsite.iframe.id === coWebsite.iframe.id) { return; } @@ -411,12 +410,12 @@ class CoWebsiteManager { if (coWebsite.position > 0) { const slot = this.getSlotByPosition(coWebsite.position); if (slot) { - slot.container.style.display = 'none'; + slot.container.style.display = "none"; } } - const previousCoWebsite = this.coWebsites.find((coWebsiteToCheck: CoWebsite) => - coWebsite.position + 1 === coWebsiteToCheck.position + const previousCoWebsite = this.coWebsites.find( + (coWebsiteToCheck: CoWebsite) => coWebsite.position + 1 === coWebsiteToCheck.position ); if (previousCoWebsite) { @@ -427,10 +426,8 @@ class CoWebsiteManager { coWebsite.iframe.remove(); } - public searchJitsi(): CoWebsite|undefined { - return this.coWebsites.find((coWebsite : CoWebsite) => - coWebsite.iframe.id.toLowerCase().includes('jitsi') - ); + public searchJitsi(): CoWebsite | undefined { + return this.coWebsites.find((coWebsite: CoWebsite) => coWebsite.iframe.id.toLowerCase().includes("jitsi")); } private generateCoWebsiteIcon(iframe: HTMLIFrameElement): HTMLDivElement { @@ -439,7 +436,7 @@ class CoWebsiteManager { icon.style.display = "none"; const iconImage = document.createElement("img"); - iconImage.src = `https://www.google.com/s2/favicons?sz=64&domain_url=${iframe.src}`; + iconImage.src = `${ICON_URL}/icon?url=${iframe.src}&size=16..30..256`; const url = new URL(iframe.src); iconImage.alt = url.hostname; @@ -456,27 +453,30 @@ class CoWebsiteManager { widthPercent?: number, position?: number ): Promise { + return this.addCoWebsite( + (iframeBuffer) => { + const iframe = document.createElement("iframe"); + iframe.src = new URL(url, base).toString(); - return this.addCoWebsite((iframeBuffer) => { - const iframe = document.createElement("iframe"); - iframe.src = new URL(url, base).toString() + if (allowPolicy) { + iframe.allow = allowPolicy; + } - if (allowPolicy) { - iframe.allow = allowPolicy; - } + if (allowApi) { + iframeListener.registerIframe(iframe); + } - if (allowApi) { - iframeListener.registerIframe(iframe); - } + iframeBuffer.appendChild(iframe); - iframeBuffer.appendChild(iframe); - - return iframe; - }, widthPercent, position); + return iframe; + }, + widthPercent, + position + ); } public async addCoWebsite( - callback: (iframeBuffer: HTMLDivElement) => PromiseLike|HTMLIFrameElement, + callback: (iframeBuffer: HTMLDivElement) => PromiseLike | HTMLIFrameElement, widthPercent?: number, position?: number ): Promise { @@ -484,10 +484,10 @@ class CoWebsiteManager { if (this.coWebsites.length < 1) { this.loadMain(); } else if (this.coWebsites.length === 5) { - throw new Error('Too many we') + throw new Error("Too many we"); } - Promise.resolve(callback(this.cowebsiteBufferDom)).then(iframe =>{ + Promise.resolve(callback(this.cowebsiteBufferDom)).then((iframe) => { iframe?.classList.add("pixel"); if (!iframe.id) { @@ -533,14 +533,14 @@ class CoWebsiteManager { setTimeout(() => { this.fire(); - position !== undefined ? - this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) : - this.moveCoWebsite(coWebsite, coWebsite.position); + position !== undefined + ? this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) + : this.moveCoWebsite(coWebsite, coWebsite.position); }, animationTime); } else { - position !== undefined ? - this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) : - this.moveCoWebsite(coWebsite, coWebsite.position); + position !== undefined + ? this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) + : this.moveCoWebsite(coWebsite, coWebsite.position); } return resolve(coWebsite); @@ -583,8 +583,7 @@ class CoWebsiteManager { } public closeCoWebsites(): Promise { - this.currentOperationPromise = this.currentOperationPromise - .then(() => { + this.currentOperationPromise = this.currentOperationPromise.then(() => { this.coWebsites.forEach((coWebsite: CoWebsite) => { this.closeCoWebsite(coWebsite); }); diff --git a/front/webpack.config.ts b/front/webpack.config.ts index cf414bd2..003db1fc 100644 --- a/front/webpack.config.ts +++ b/front/webpack.config.ts @@ -193,6 +193,7 @@ module.exports = { ADMIN_URL: undefined, CONTACT_URL: null, PROFILE_URL: null, + ICON_URL: null, DEBUG_MODE: null, STUN_SERVER: null, TURN_SERVER: null, From 624593e425cf9ac7ad3cd86bf8041e10569ccd93 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Tue, 9 Nov 2021 18:10:55 +0100 Subject: [PATCH 18/71] Implement twemoji on emote --- .../src/Components/EmoteMenu/EmoteMenu.svelte | 9 ++++-- front/src/Components/Menu/MenuIcon.svelte | 8 +---- front/src/Phaser/Entity/Character.ts | 8 +++-- front/src/Phaser/Game/GameScene.ts | 29 ++++++++++--------- front/src/Phaser/Player/Player.ts | 20 ++++++++++--- front/src/Stores/EmoteStore.ts | 8 ++++- 6 files changed, 51 insertions(+), 31 deletions(-) diff --git a/front/src/Components/EmoteMenu/EmoteMenu.svelte b/front/src/Components/EmoteMenu/EmoteMenu.svelte index 70aeb4cc..b4c24f39 100644 --- a/front/src/Components/EmoteMenu/EmoteMenu.svelte +++ b/front/src/Components/EmoteMenu/EmoteMenu.svelte @@ -18,13 +18,18 @@ '--font': 'Press Start 2P' }, emojisPerRow: isMobile() ? 6 : 8, - autoFocusSearch: false + autoFocusSearch: false, + style: 'twemoji', }); //the timeout is here to prevent the menu from flashing setTimeout(() => picker.showPicker(emojiContainer), 100); picker.on("emoji", (selection) => { - emoteStore.set(selection.emoji); + emoteStore.set({ + unicode: selection.emoji, + url: selection.url, + name: selection.name + }); }); picker.on("hidden", () => { diff --git a/front/src/Components/Menu/MenuIcon.svelte b/front/src/Components/Menu/MenuIcon.svelte index fa177c88..4b37238f 100644 --- a/front/src/Components/Menu/MenuIcon.svelte +++ b/front/src/Components/Menu/MenuIcon.svelte @@ -11,15 +11,9 @@ function showChat(){ chatVisibilityStore.set(true); } - - function onKeyDown(e: KeyboardEvent) { - if (e.key === "Tab") { - showMenu(); - } - } - +
open menu diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 3606159f..7df18fcd 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -3,6 +3,7 @@ import { SpeechBubble } from "./SpeechBubble"; import Text = Phaser.GameObjects.Text; import Container = Phaser.GameObjects.Container; import Sprite = Phaser.GameObjects.Sprite; +import DOMElement = Phaser.GameObjects.DOMElement; import { TextureError } from "../../Exception/TextureError"; import { Companion } from "../Companion/Companion"; import type { GameScene } from "../Game/GameScene"; @@ -33,7 +34,7 @@ export abstract class Character extends Container { //private teleportation: Sprite; private invisible: boolean; public companion?: Companion; - private emote: Phaser.GameObjects.Text | null = null; + private emote: Phaser.GameObjects.DOMElement | null = null; private emoteTween: Phaser.Tweens.Tween | null = null; scene: GameScene; @@ -300,8 +301,9 @@ export abstract class Character extends Container { playEmote(emote: string) { this.cancelPreviousEmote(); const emoteY = -45; - this.playerName.setVisible(false); - this.emote = new Text(this.scene, -10, 0, emote, { fontSize: "20px" }); + const image = new Image(16, 16); + image.src = emote; + this.emote = new DOMElement(this.scene, -1, 0, image, "z-index:10;"); this.emote.setAlpha(0); this.add(this.emote); this.createStartTransition(emoteY); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 6091f4cc..4812c142 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -15,10 +15,7 @@ import type { import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY } from "../../Enum/EnvironmentVariable"; import { Queue } from "queue-typescript"; -import { - Box, - ON_ACTION_TRIGGER_BUTTON, -} from "../../WebRtc/LayoutManager"; +import { Box, ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager"; import { CoWebsite, coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; import type { UserMovedMessage } from "../../Messages/generated/messages_pb"; import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils"; @@ -497,7 +494,10 @@ export class GameScene extends DirtyScene { object.properties, 'in the "' + object.name + '" object of type "website"' ); - const allowApi = PropertyUtils.findBooleanProperty(GameMapProperties.ALLOW_API, object.properties); + const allowApi = PropertyUtils.findBooleanProperty( + GameMapProperties.ALLOW_API, + object.properties + ); // TODO: add a "allow" property to iframe this.embeddedWebsiteManager.createEmbeddedWebsite( @@ -614,10 +614,10 @@ export class GameScene extends DirtyScene { oldPeerNumber = newPeerNumber; }); - this.emoteUnsubscribe = emoteStore.subscribe((emoteKey) => { - if (emoteKey) { - this.CurrentPlayer?.playEmote(emoteKey); - this.connection?.emitEmoteEvent(emoteKey); + this.emoteUnsubscribe = emoteStore.subscribe((emote) => { + if (emote) { + this.CurrentPlayer?.playEmote(emote.url); + this.connection?.emitEmoteEvent(emote.url); emoteStore.set(null); } }); @@ -763,14 +763,14 @@ export class GameScene extends DirtyScene { this.gameMap.setPosition(this.CurrentPlayer.x, this.CurrentPlayer.y); // Init layer change listener - this.gameMap.onEnterLayer(layers => { - layers.forEach(layer => { + this.gameMap.onEnterLayer((layers) => { + layers.forEach((layer) => { iframeListener.sendEnterLayerEvent(layer.name); }); }); - this.gameMap.onLeaveLayer(layers => { - layers.forEach(layer => { + this.gameMap.onLeaveLayer((layers) => { + layers.forEach((layer) => { iframeListener.sendLeaveLayerEvent(layer.name); }); }); @@ -1868,7 +1868,8 @@ ${escapedMessage} public startJitsi(roomName: string, jwt?: string): void { const allProps = this.gameMap.getCurrentProperties(); const jitsiConfig = this.safeParseJSONstring( - allProps.get(GameMapProperties.JITSI_CONFIG) as string | undefined, GameMapProperties.JITSI_CONFIG + allProps.get(GameMapProperties.JITSI_CONFIG) as string | undefined, + GameMapProperties.JITSI_CONFIG ); const jitsiInterfaceConfig = this.safeParseJSONstring( allProps.get(GameMapProperties.JITSI_INTERFACE_CONFIG) as string | undefined, diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 28a1d3bd..a1924457 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -3,8 +3,6 @@ import type { GameScene } from "../Game/GameScene"; import { UserInputEvent, UserInputManager } from "../UserInput/UserInputManager"; import { Character } from "../Entity/Character"; import { userMovingStore } from "../../Stores/GameStore"; -import { get } from "svelte/store"; -import { emoteMenuStore } from "../../Stores/EmoteStore"; export const hasMovedEventName = "hasMoved"; export const requestEmoteEventName = "requestEmote"; @@ -68,10 +66,24 @@ export class Player extends Character { } else if (this.wasMoving && moving) { // slow joystick movement this.move(0, 0); - this.emit(hasMovedEventName, { moving, direction: this.previousDirection, x: this.x, y: this.y, oldX: x, oldY: y }); + this.emit(hasMovedEventName, { + moving, + direction: this.previousDirection, + x: this.x, + y: this.y, + oldX: x, + oldY: y, + }); } else if (this.wasMoving && !moving) { this.stop(); - this.emit(hasMovedEventName, { moving, direction: this.previousDirection, x: this.x, y: this.y, oldX: x, oldY: y }); + this.emit(hasMovedEventName, { + moving, + direction: this.previousDirection, + x: this.x, + y: this.y, + oldX: x, + oldY: y, + }); } if (direction !== null) { diff --git a/front/src/Stores/EmoteStore.ts b/front/src/Stores/EmoteStore.ts index 56b06659..3f6c3cf5 100644 --- a/front/src/Stores/EmoteStore.ts +++ b/front/src/Stores/EmoteStore.ts @@ -1,5 +1,11 @@ import { writable } from "svelte/store"; +export interface Emoji { + unicode: string; + url: string; + name: string; +} + function createEmoteMenuStore() { const { subscribe, set } = writable(false); @@ -14,5 +20,5 @@ function createEmoteMenuStore() { }; } -export const emoteStore = writable(null); +export const emoteStore = writable(null); export const emoteMenuStore = createEmoteMenuStore(); From eac5e8adc8da2b3f184cad54b9226e305cb65dfe Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Wed, 10 Nov 2021 18:17:44 +0100 Subject: [PATCH 19/71] Add icon server on single domain services --- docker-compose.single-domain.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docker-compose.single-domain.yaml b/docker-compose.single-domain.yaml index 4e85d702..f7c9e429 100644 --- a/docker-compose.single-domain.yaml +++ b/docker-compose.single-domain.yaml @@ -30,6 +30,7 @@ services: UPLOADER_URL: /uploader ADMIN_URL: /admin MAPS_URL: /maps + ICON_URL: /icon STARTUP_COMMAND_1: ./templater.sh STARTUP_COMMAND_2: yarn install TURN_SERVER: "turn:localhost:3478,turns:localhost:5349" @@ -177,6 +178,20 @@ services: redis: image: redis:6 + icon: + image: matthiasluedtke/iconserver:v3.13.0 + labels: + - "traefik.http.middlewares.strip-icon-prefix.stripprefix.prefixes=/icon" + - "traefik.http.routers.icon.rule=PathPrefix(`/icon`)" + - "traefik.http.routers.icon.middlewares=strip-icon-prefix@docker" + - "traefik.http.routers.icon.entryPoints=web" + - "traefik.http.services.icon.loadbalancer.server.port=8080" + - "traefik.http.routers.icon-ssl.rule=PathPrefix(`/icon`)" + - "traefik.http.routers.icon-ssl.middlewares=strip-icon-prefix@docker" + - "traefik.http.routers.icon-ssl.entryPoints=websecure" + - "traefik.http.routers.icon-ssl.tls=true" + - "traefik.http.routers.icon-ssl.service=icon" + # coturn: # image: coturn/coturn:4.5.2 # command: From 2513f8b3d633d5f2a0d383409e573e93b65c2a23 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Wed, 10 Nov 2021 18:22:03 +0100 Subject: [PATCH 20/71] Add icon server on production docker services --- contrib/docker/docker-compose.prod.yaml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/contrib/docker/docker-compose.prod.yaml b/contrib/docker/docker-compose.prod.yaml index 6b3b8520..0e307dd9 100644 --- a/contrib/docker/docker-compose.prod.yaml +++ b/contrib/docker/docker-compose.prod.yaml @@ -38,6 +38,7 @@ services: JITSI_URL: $JITSI_URL JITSI_PRIVATE_MODE: "$JITSI_PRIVATE_MODE" PUSHER_URL: //pusher.${DOMAIN} + PUSHER_URL: //icon.${DOMAIN} TURN_SERVER: "${TURN_SERVER}" TURN_USER: "${TURN_USER}" TURN_PASSWORD: "${TURN_PASSWORD}" @@ -98,3 +99,15 @@ services: - "traefik.http.routers.back-ssl.service=back" - "traefik.http.routers.back-ssl.tls.certresolver=myresolver" restart: unless-stopped + + icon: + image: matthiasluedtke/iconserver:v3.13.0 + labels: + - "traefik.http.routers.icon.rule=Host(`icon.${DOMAIN}`)" + - "traefik.http.routers.icon.entryPoints=web,traefik" + - "traefik.http.services.icon.loadbalancer.server.port=8080" + - "traefik.http.routers.icon-ssl.rule=Host(`icon.${DOMAIN}`)" + - "traefik.http.routers.icon-ssl.entryPoints=websecure" + - "traefik.http.routers.icon-ssl.tls=true" + - "traefik.http.routers.icon-ssl.service=icon" + - "traefik.http.routers.icon-ssl.tls.certresolver=myresolver" From 1db22d82af53cbfe241319a956e64435443cef54 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 10 Nov 2021 18:26:50 +0100 Subject: [PATCH 21/71] Set state in local storage when openid provider redirect on jwt with token value Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 16 ++++++++++------ front/src/Connexion/LocalUserStore.ts | 3 +++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 032a60f1..cb600d6d 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -91,6 +91,16 @@ class ConnectionManager { //TODO if jwt is defined, state and nonce can to be deleted const code = urlParams.get("code"); const state = urlParams.get("state"); + const jwt = urlParams.get("jwt"); + + if (jwt) { + this.authToken = jwt; + localUserStore.setAuthToken(jwt); + //if we use jwt we can update new state from openid provider + if (state) { + localUserStore.setState(state); + } + } if (!state || !localUserStore.verifyState(state)) { throw "Could not validate state!"; } @@ -98,12 +108,6 @@ class ConnectionManager { throw "No Auth code provided"; } localUserStore.setCode(code); - - const jwt = urlParams.get("jwt"); - if (jwt) { - this.authToken = jwt; - localUserStore.setAuthToken(jwt); - } this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl())); try { await this.checkAuthUserConnexion(); diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index a113291d..7607fb2d 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -171,6 +171,9 @@ class LocalUserStore { } return oldValue === value; } + setState(value: string) { + localStorage.setItem(state, value); + } getState(): string | null { return localStorage.getItem(state); } From d3f120f2bb0b38db31e05e5050b6ade628e1d091 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Fri, 12 Nov 2021 12:13:44 +0100 Subject: [PATCH 22/71] Update to have token when user is connected - Update menu design Signed-off-by: Gregoire Parant --- .../src/Components/Menu/ContactSubMenu.svelte | 52 +++++++++++- .../Components/Menu/CreateMapSubMenu.svelte | 51 ----------- front/src/Components/Menu/GuestSubMenu.svelte | 75 ++++++++++++++++ front/src/Components/Menu/Menu.svelte | 14 +-- .../src/Components/Menu/ProfileSubMenu.svelte | 85 ++++++++++++------- front/src/Connexion/ConnectionManager.ts | 56 ++++++------ front/src/Stores/MenuStore.ts | 10 +-- 7 files changed, 220 insertions(+), 123 deletions(-) delete mode 100644 front/src/Components/Menu/CreateMapSubMenu.svelte create mode 100644 front/src/Components/Menu/GuestSubMenu.svelte diff --git a/front/src/Components/Menu/ContactSubMenu.svelte b/front/src/Components/Menu/ContactSubMenu.svelte index 61ecc56e..3cf1f5fb 100644 --- a/front/src/Components/Menu/ContactSubMenu.svelte +++ b/front/src/Components/Menu/ContactSubMenu.svelte @@ -1,10 +1,60 @@ - +
+
+
+

Getting started

+

+ WorkAdventure allows you to create an online space to communicate spontaneously with others. + And it all starts with creating your own space. Choose from a large selection of prefabricated maps by our team. +

+ +
+ +
+

Create your map

+

You can also create your own custom map by following the step of the documentation.

+ +
+ + +
+
diff --git a/front/src/Components/Menu/GuestSubMenu.svelte b/front/src/Components/Menu/GuestSubMenu.svelte new file mode 100644 index 00000000..13a7981a --- /dev/null +++ b/front/src/Components/Menu/GuestSubMenu.svelte @@ -0,0 +1,75 @@ + + +
+
+ +
+

Share the link of the room !

+ + +
+
+
+ + \ No newline at end of file diff --git a/front/src/Components/Menu/Menu.svelte b/front/src/Components/Menu/Menu.svelte index 4eecb370..83715304 100644 --- a/front/src/Components/Menu/Menu.svelte +++ b/front/src/Components/Menu/Menu.svelte @@ -2,11 +2,11 @@ import {fly} from "svelte/transition"; import SettingsSubMenu from "./SettingsSubMenu.svelte"; import ProfileSubMenu from "./ProfileSubMenu.svelte"; - import CreateMapSubMenu from "./CreateMapSubMenu.svelte"; import AboutRoomSubMenu from "./AboutRoomSubMenu.svelte"; import GlobalMessageSubMenu from "./GlobalMessagesSubMenu.svelte"; import ContactSubMenu from "./ContactSubMenu.svelte"; import CustomSubMenu from "./CustomSubMenu.svelte" + import GuestSubMenu from "./GuestSubMenu.svelte"; import { checkSubMenuToShow, customMenuIframe, @@ -19,21 +19,21 @@ import type {Unsubscriber} from "svelte/store"; import {sendMenuClickedEvent} from "../../Api/iframe/Ui/MenuItem"; - let activeSubMenu: string = SubMenusInterface.settings; - let activeComponent: typeof SettingsSubMenu | typeof CustomSubMenu = SettingsSubMenu; + let activeSubMenu: string = SubMenusInterface.profile; + let activeComponent: typeof ProfileSubMenu | typeof CustomSubMenu = ProfileSubMenu; let props: { url: string, allowApi: boolean }; let unsubscriberSubMenuStore: Unsubscriber; onMount(() => { unsubscriberSubMenuStore = subMenusStore.subscribe(() => { if(!get(subMenusStore).includes(activeSubMenu)) { - switchMenu(SubMenusInterface.settings); + switchMenu(SubMenusInterface.profile); } }) checkSubMenuToShow(); - switchMenu(SubMenusInterface.settings); + switchMenu(SubMenusInterface.profile); }) onDestroy(() => { @@ -52,8 +52,8 @@ case SubMenusInterface.profile: activeComponent = ProfileSubMenu; break; - case SubMenusInterface.createMap: - activeComponent = CreateMapSubMenu; + case SubMenusInterface.invite: + activeComponent = GuestSubMenu; break; case SubMenusInterface.aboutRoom: activeComponent = AboutRoomSubMenu; diff --git a/front/src/Components/Menu/ProfileSubMenu.svelte b/front/src/Components/Menu/ProfileSubMenu.svelte index 83ec329c..e543096e 100644 --- a/front/src/Components/Menu/ProfileSubMenu.svelte +++ b/front/src/Components/Menu/ProfileSubMenu.svelte @@ -55,54 +55,73 @@
- {#if $userIsConnected} + + +
+ {#if $userIsConnected} +
+ {#if PROFILE_URL != undefined} + + {/if} +
+
+ +
+ {:else} +
+ Sign in +
+ {/if} +
+ + +
+
+ +
+
+ Profile validated by domain: ${domain} +
+
+ Your email: ${email} +
+
+ + ` + ); + } +} diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index 43bfc7bf..52382266 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -16,6 +16,7 @@ export const OPID_CLIENT_ID = process.env.OPID_CLIENT_ID || ""; export const OPID_CLIENT_SECRET = process.env.OPID_CLIENT_SECRET || ""; export const OPID_CLIENT_ISSUER = process.env.OPID_CLIENT_ISSUER || ""; export const OPID_CLIENT_REDIREC_URL = process.env.OPID_CLIENT_REDIREC_URL || FRONT_URL + "/jwt"; +export const OPID_PROFILE_SCREEN_PROVIDER = process.env.OPID_PROFILE_SCREEN_PROVIDER || ADMIN_URL + "/profile"; export const DISABLE_ANONYMOUS = process.env.DISABLE_ANONYMOUS || false; export { diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index d002ff8b..6e1848eb 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -1,4 +1,4 @@ -import { ADMIN_API_TOKEN, ADMIN_API_URL, ADMIN_URL } from "../Enum/EnvironmentVariable"; +import { ADMIN_API_TOKEN, ADMIN_API_URL, ADMIN_URL, OPID_PROFILE_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable"; import Axios from "axios"; import { GameRoomPolicyTypes } from "_Model/PusherRoom"; import { CharacterTexture } from "./AdminApi/CharacterTexture"; @@ -142,13 +142,15 @@ class AdminApi { }); } - /*TODO add constant to use profile companny*/ + /** + * + * @param accessToken + */ getProfileUrl(accessToken: string): string { - if (!ADMIN_URL) { + if (!OPID_PROFILE_SCREEN_PROVIDER) { throw new Error("No admin backoffice set!"); } - - return ADMIN_URL + `/profile?token=${accessToken}`; + return `${OPID_PROFILE_SCREEN_PROVIDER}?accessToken=${accessToken}`; } async logoutOauth(token: string) { From 16c08d86f29e144bf995b72b136d48fc5f41c5ba Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 15 Nov 2021 12:30:25 +0100 Subject: [PATCH 31/71] Update hydraAccessToken to accessToken --- pusher/src/Controller/AuthenticateController.ts | 15 ++++++++------- pusher/src/Services/JWTTokenManager.ts | 6 +++--- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 2dafe065..7b1f50bd 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -62,10 +62,11 @@ export class AuthenticateController extends BaseController { if (token != undefined) { try { const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false); - if (authTokenData.hydraAccessToken == undefined) { + if (authTokenData.accessToken == undefined) { throw Error("Token cannot to be check on Hydra"); } - await openIDClient.checkTokenAuth(authTokenData.hydraAccessToken); + const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken); + console.log("resCheckTokenAuth", resCheckTokenAuth); res.writeStatus("200"); this.addCorsHeaders(res); return res.end(JSON.stringify({ authToken: token })); @@ -100,10 +101,10 @@ export class AuthenticateController extends BaseController { try { const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false); - if (authTokenData.hydraAccessToken == undefined) { + if (authTokenData.accessToken == undefined) { throw Error("Token cannot to be logout on Hydra"); } - await openIDClient.logoutUser(authTokenData.hydraAccessToken); + await openIDClient.logoutUser(authTokenData.accessToken); } catch (error) { console.error("openIDCallback => logout-callback", error); } finally { @@ -208,14 +209,14 @@ export class AuthenticateController extends BaseController { if (token != undefined) { try { const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false); - if (authTokenData.hydraAccessToken == undefined) { + if (authTokenData.accessToken == undefined) { throw Error("Token cannot to be check on Hydra"); } - await openIDClient.checkTokenAuth(authTokenData.hydraAccessToken); + await openIDClient.checkTokenAuth(authTokenData.accessToken); //get login profile res.writeStatus("302"); - res.writeHeader("Location", adminApi.getProfileUrl(authTokenData.hydraAccessToken)); + res.writeHeader("Location", adminApi.getProfileUrl(authTokenData.accessToken)); this.addCorsHeaders(res); // eslint-disable-next-line no-unsafe-finally return res.end(); diff --git a/pusher/src/Services/JWTTokenManager.ts b/pusher/src/Services/JWTTokenManager.ts index 24393084..2f482dbf 100644 --- a/pusher/src/Services/JWTTokenManager.ts +++ b/pusher/src/Services/JWTTokenManager.ts @@ -6,13 +6,13 @@ import { adminApi, AdminBannedData } from "../Services/AdminApi"; export interface AuthTokenData { identifier: string; //will be a email if logged in or an uuid if anonymous - hydraAccessToken?: string; + accessToken?: string; } export const tokenInvalidException = "tokenInvalid"; class JWTTokenManager { - public createAuthToken(identifier: string, hydraAccessToken?: string) { - return Jwt.sign({ identifier, hydraAccessToken }, SECRET_KEY, { expiresIn: "30d" }); + public createAuthToken(identifier: string, accessToken?: string) { + return Jwt.sign({ identifier, accessToken }, SECRET_KEY, { expiresIn: "30d" }); } public verifyJWTToken(token: string, ignoreExpiration: boolean = false): AuthTokenData { From 61b8d584af01432deca9b4d014c5b97f3e086e2e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 15 Nov 2021 14:34:23 +0100 Subject: [PATCH 32/71] delete useless userIdentify --- pusher/src/Controller/AuthenticateController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 7b1f50bd..0cef24bb 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -203,7 +203,7 @@ export class AuthenticateController extends BaseController { res.onAborted(() => { console.warn("/message request was aborted"); }); - const { userIdentify, token } = parse(req.getQuery()); + const { token } = parse(req.getQuery()); try { //verify connected by token if (token != undefined) { From bbc2ac255002133b00607b0cb8d6e73bc0c6dfc9 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 15 Nov 2021 15:25:49 +0100 Subject: [PATCH 33/71] Update Connection manager to clean url history of navigator --- front/src/Connexion/ConnectionManager.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 1f844bf2..793831bf 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -212,6 +212,8 @@ class ConnectionManager { analyticsClient.identifyUser(this.localUser.uuid, this.localUser.email); } + //clean history with new URL + window.history.pushState({}, document.title, window.location.pathname); this.serviceWorker = new _ServiceWorker(); return Promise.resolve(this._currentRoom); } From 210a789aa460c63d759dc3d85e3eb5a49def0346 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 15 Nov 2021 15:58:08 +0100 Subject: [PATCH 34/71] Fix feedback @moufmouf --- .env.template | 2 +- docker-compose.single-domain.yaml | 2 +- docker-compose.yaml | 2 +- front/src/Connexion/ConnectionManager.ts | 3 + .../src/Controller/AuthenticateController.ts | 1 - .../src/Controller/OpenIdProfileController.ts | 64 +++++++++---------- pusher/src/Enum/EnvironmentVariable.ts | 2 +- pusher/src/Services/OpenIDClient.ts | 6 +- 8 files changed, 42 insertions(+), 40 deletions(-) diff --git a/.env.template b/.env.template index 5328fe08..0bd7bf6d 100644 --- a/.env.template +++ b/.env.template @@ -22,7 +22,7 @@ MAX_USERNAME_LENGTH=8 OPID_CLIENT_ID= OPID_CLIENT_SECRET= OPID_CLIENT_ISSUER= -OPID_CLIENT_REDIREC_URL= +OPID_CLIENT_REDIRECT_URL= OPID_LOGIN_SCREEN_PROVIDER=http://pusher.workadventure.localhost/login-screen OPID_PROFILE_SCREEN_PROVIDER= DISABLE_ANONYMOUS= diff --git a/docker-compose.single-domain.yaml b/docker-compose.single-domain.yaml index e241c108..cd38a0f9 100644 --- a/docker-compose.single-domain.yaml +++ b/docker-compose.single-domain.yaml @@ -71,7 +71,7 @@ services: OPID_CLIENT_ID: $OPID_CLIENT_ID OPID_CLIENT_SECRET: $OPID_CLIENT_SECRET OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER - OPID_CLIENT_REDIREC_URL: $OPID_CLIENT_REDIREC_URL + OPID_CLIENT_REDIRECT_URL: $OPID_CLIENT_REDIRECT_URL OPID_PROFILE_SCREEN_PROVIDER: $OPID_PROFILE_SCREEN_PROVIDER DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS volumes: diff --git a/docker-compose.yaml b/docker-compose.yaml index 03395f22..0e22fa91 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -73,7 +73,7 @@ services: OPID_CLIENT_ID: $OPID_CLIENT_ID OPID_CLIENT_SECRET: $OPID_CLIENT_SECRET OPID_CLIENT_ISSUER: $OPID_CLIENT_ISSUER - OPID_CLIENT_REDIREC_URL: $OPID_CLIENT_REDIREC_URL + OPID_CLIENT_REDIRECT_URL: $OPID_CLIENT_REDIRECT_URL OPID_PROFILE_SCREEN_PROVIDER: $OPID_PROFILE_SCREEN_PROVIDER DISABLE_ANONYMOUS: $DISABLE_ANONYMOUS volumes: diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 793831bf..9316e37f 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -183,8 +183,11 @@ class ConnectionManager { } else { try { await this.checkAuthUserConnexion(); + analyticsClient.loggedWithSso(); } catch (err) { console.error(err); + this.loadOpenIDScreen(); + return Promise.reject(new Error("You will be redirect on login page")); } } this.localUser = localUserStore.getLocalUser() as LocalUser; //if authToken exist in localStorage then localUser cannot be null diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 0cef24bb..70e333a8 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -66,7 +66,6 @@ export class AuthenticateController extends BaseController { throw Error("Token cannot to be check on Hydra"); } const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken); - console.log("resCheckTokenAuth", resCheckTokenAuth); res.writeStatus("200"); this.addCorsHeaders(res); return res.end(JSON.stringify({ authToken: token })); diff --git a/pusher/src/Controller/OpenIdProfileController.ts b/pusher/src/Controller/OpenIdProfileController.ts index 372b603b..f33e7a22 100644 --- a/pusher/src/Controller/OpenIdProfileController.ts +++ b/pusher/src/Controller/OpenIdProfileController.ts @@ -44,37 +44,37 @@ export class OpenIdProfileController extends BaseController { } buildHtml(domain: string, email: string, pictureUrl?: string) { - return ( - "" + - ` -
- -
- -
-
- -
-
- Profile validated by domain: ${domain} -
-
- Your email: ${email} -
-
- - ` - ); + return ` + + + + + + +
+
+ +
+
+ Profile validated by domain: ${domain} +
+
+ Your email: ${email} +
+
+ + + `; } } diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index 52382266..23d2c23f 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -15,7 +15,7 @@ export const FRONT_URL = process.env.FRONT_URL || "http://localhost"; export const OPID_CLIENT_ID = process.env.OPID_CLIENT_ID || ""; export const OPID_CLIENT_SECRET = process.env.OPID_CLIENT_SECRET || ""; export const OPID_CLIENT_ISSUER = process.env.OPID_CLIENT_ISSUER || ""; -export const OPID_CLIENT_REDIREC_URL = process.env.OPID_CLIENT_REDIREC_URL || FRONT_URL + "/jwt"; +export const OPID_CLIENT_REDIRECT_URL = process.env.OPID_CLIENT_REDIRECT_URL || FRONT_URL + "/jwt"; export const OPID_PROFILE_SCREEN_PROVIDER = process.env.OPID_PROFILE_SCREEN_PROVIDER || ADMIN_URL + "/profile"; export const DISABLE_ANONYMOUS = process.env.DISABLE_ANONYMOUS || false; diff --git a/pusher/src/Services/OpenIDClient.ts b/pusher/src/Services/OpenIDClient.ts index bc0dd6c9..13bf6f76 100644 --- a/pusher/src/Services/OpenIDClient.ts +++ b/pusher/src/Services/OpenIDClient.ts @@ -3,7 +3,7 @@ import { OPID_CLIENT_ID, OPID_CLIENT_SECRET, OPID_CLIENT_ISSUER, - OPID_CLIENT_REDIREC_URL, + OPID_CLIENT_REDIRECT_URL, } from "../Enum/EnvironmentVariable"; class OpenIDClient { @@ -15,7 +15,7 @@ class OpenIDClient { return new issuer.Client({ client_id: OPID_CLIENT_ID, client_secret: OPID_CLIENT_SECRET, - redirect_uris: [OPID_CLIENT_REDIREC_URL], + redirect_uris: [OPID_CLIENT_REDIRECT_URL], response_types: ["code"], }); }); @@ -38,7 +38,7 @@ class OpenIDClient { public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string }> { return this.initClient().then((client) => { - return client.callback(OPID_CLIENT_REDIREC_URL, { code }, { nonce }).then((tokenSet) => { + return client.callback(OPID_CLIENT_REDIRECT_URL, { code }, { nonce }).then((tokenSet) => { return client.userinfo(tokenSet).then((res) => { return { ...res, From 20164417fb2be7149bd0885138828ceb1267d606 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 15 Nov 2021 16:34:23 +0100 Subject: [PATCH 35/71] Change data by resCheckTokenAuth --- pusher/src/Controller/AuthenticateController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 21b636f9..f505923c 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -69,7 +69,7 @@ export class AuthenticateController extends BaseController { const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken); res.writeStatus("200"); this.addCorsHeaders(res); - return res.end(JSON.stringify({ ...data, authToken: token })); + return res.end(JSON.stringify({ ...resCheckTokenAuth, authToken: token })); } catch (err) { console.info("User was not connected", err); } From d3964ae25b86ee03af1a212a8412eda89365b86a Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Mon, 15 Nov 2021 16:36:10 +0100 Subject: [PATCH 36/71] HotFix conflict merging --- front/src/Connexion/ConnectionManager.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 87f23843..00e721ae 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -111,7 +111,7 @@ class ConnectionManager { this._currentRoom = await Room.createRoom(new URL(localUserStore.getLastRoomUrl())); try { - await this.checkAuthUserConnexion(this._currentRoom.key); + await this.checkAuthUserConnexion(); analyticsClient.loggedWithSso(); } catch (err) { console.error(err); @@ -296,7 +296,7 @@ class ConnectionManager { return this.connexionType; } - async checkAuthUserConnexion(playUri: string) { + async checkAuthUserConnexion() { //set connected store for menu at false userIsConnected.set(false); @@ -313,9 +313,9 @@ class ConnectionManager { throw "No Auth code provided"; } } - const { authToken, userUuid, textures, email } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce, token } }).then( - (res) => res.data - ); + const { authToken, userUuid, textures, email } = await Axios.get(`${PUSHER_URL}/login-callback`, { + params: { code, nonce, token }, + }).then((res) => res.data); localUserStore.setAuthToken(authToken); this.localUser = new LocalUser(userUuid, textures, email); localUserStore.saveUser(this.localUser); From c1dc438138f5ec474f9407944c00428d45955178 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 16 Nov 2021 11:14:27 +0100 Subject: [PATCH 37/71] HotFix encrypted and decrypted error Signed-off-by: Gregoire Parant --- front/src/Connexion/Room.ts | 48 ++++++++++++++++---------- pusher/src/Controller/MapController.ts | 15 +++++--- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index 535d2f8d..0f4bb20c 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -89,27 +89,37 @@ export class Room { } private async getMapDetail(): Promise { - const result = await Axios.get(`${PUSHER_URL}/map`, { - params: { - playUri: this.roomUrl.toString(), - authToken: localUserStore.getAuthToken(), - }, - }); + try { + const result = await Axios.get(`${PUSHER_URL}/map`, { + params: { + playUri: this.roomUrl.toString(), + authToken: localUserStore.getAuthToken(), + }, + }); - const data = result.data; - if (data.redirectUrl) { - return { - redirectUrl: data.redirectUrl as string, - }; + const data = result.data; + if (data.redirectUrl) { + return { + redirectUrl: data.redirectUrl as string, + }; + } + console.log("Map ", this.id, " resolves to URL ", data.mapUrl); + this._mapUrl = data.mapUrl; + this._textures = data.textures; + this._group = data.group; + this._authenticationMandatory = data.authenticationMandatory || (DISABLE_ANONYMOUS as boolean); + this._iframeAuthentication = data.iframeAuthentication || OPID_LOGIN_SCREEN_PROVIDER; + this._contactPage = data.contactPage || CONTACT_URL; + return new MapDetail(data.mapUrl, data.textures); + } catch (e) { + console.log("Error => getMapDetail", e, e.response); + //TODO fix me and manage Error class + if (e.response?.data === "Token decrypted error") { + localUserStore.setAuthToken(null); + window.location.assign("/login"); + } + throw e; } - console.log("Map ", this.id, " resolves to URL ", data.mapUrl); - this._mapUrl = data.mapUrl; - this._textures = data.textures; - this._group = data.group; - this._authenticationMandatory = data.authenticationMandatory || (DISABLE_ANONYMOUS as boolean); - this._iframeAuthentication = data.iframeAuthentication || OPID_LOGIN_SCREEN_PROVIDER; - this._contactPage = data.contactPage || CONTACT_URL; - return new MapDetail(data.mapUrl, data.textures); } /** diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index 18748d9e..d7d506e2 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -80,10 +80,17 @@ export class MapController extends BaseController { 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); + try { + // 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); + } catch (e) { + // The token was not good, redirect user on login page + res.writeStatus("500"); + res.end("Token decrypted error"); + return; + } } } const mapDetails = await adminApi.fetchMapDetails(query.playUri as string, userId); From ec1cc92c8b12b36d8d08483e58b758593ba8c015 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 16 Nov 2021 11:23:18 +0100 Subject: [PATCH 38/71] replace console log by error Signed-off-by: Gregoire Parant --- front/src/Connexion/Room.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index 0f4bb20c..dd80b704 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -112,7 +112,7 @@ export class Room { this._contactPage = data.contactPage || CONTACT_URL; return new MapDetail(data.mapUrl, data.textures); } catch (e) { - console.log("Error => getMapDetail", e, e.response); + console.error("Error => getMapDetail", e, e.response); //TODO fix me and manage Error class if (e.response?.data === "Token decrypted error") { localUserStore.setAuthToken(null); From 87683744602f770b36756e4c297123514b1c65e6 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 16 Nov 2021 13:14:40 +0100 Subject: [PATCH 39/71] HotFix connexion manager Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 2 +- front/src/Enum/EnvironmentVariable.ts | 2 +- front/src/Phaser/Game/GameScene.ts | 4 +++- pusher/src/Controller/AuthenticateController.ts | 14 +++++++++++++- pusher/src/Enum/EnvironmentVariable.ts | 2 +- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 00e721ae..fbff365c 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -314,7 +314,7 @@ class ConnectionManager { } } const { authToken, userUuid, textures, email } = await Axios.get(`${PUSHER_URL}/login-callback`, { - params: { code, nonce, token }, + params: { code, nonce, token, playUri: this.currentRoom?.key }, }).then((res) => res.data); localUserStore.setAuthToken(authToken); this.localUser = new LocalUser(userUuid, textures, email); diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts index 644b7a77..76b4c8af 100644 --- a/front/src/Enum/EnvironmentVariable.ts +++ b/front/src/Enum/EnvironmentVariable.ts @@ -23,7 +23,7 @@ export const CONTACT_URL = process.env.CONTACT_URL || undefined; export const PROFILE_URL = process.env.PROFILE_URL || undefined; export const POSTHOG_API_KEY: string = (process.env.POSTHOG_API_KEY as string) || ""; export const POSTHOG_URL = process.env.POSTHOG_URL || undefined; -export const DISABLE_ANONYMOUS = process.env.DISABLE_ANONYMOUS || false; +export const DISABLE_ANONYMOUS: boolean = process.env.DISABLE_ANONYMOUS === "true"; export const OPID_LOGIN_SCREEN_PROVIDER = process.env.OPID_LOGIN_SCREEN_PROVIDER; export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600; diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 4812c142..81cf3676 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -88,6 +88,7 @@ import { analyticsClient } from "../../Administration/AnalyticsClient"; import { get } from "svelte/store"; import { contactPageStore } from "../../Stores/MenuStore"; import { GameMapProperties } from "./GameMapProperties"; +import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile; export interface GameSceneInitInterface { initPosition: PointInterface | null; @@ -293,7 +294,8 @@ export class GameScene extends DirtyScene { } //once preloading is over, we don't want loading errors to crash the game, so we need to disable this behavior after preloading. - if (this.preloading) { + //if SpriteSheetFile (WOKA file) don't display error and give an access for user + if (this.preloading && !(file instanceof SpriteSheetFile)) { //remove loader in progress removeLoader(this); diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index f505923c..5e4eb19f 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -64,6 +64,18 @@ export class AuthenticateController extends BaseController { try { const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false); if (authTokenData.accessToken == undefined) { + //if not nonce and code, user connected in anonymous + //get data with identifier and return token + if (!code && !nonce) { + const data = await this.getUserByUserIdentifier( + authTokenData.identifier, + playUri as string, + IPAddress + ); + res.writeStatus("200"); + this.addCorsHeaders(res); + return res.end(JSON.stringify({ ...data, authToken: token })); + } throw Error("Token cannot to be check on Hydra"); } const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken); @@ -81,7 +93,7 @@ export class AuthenticateController extends BaseController { if (!email) { throw new Error("No email in the response"); } - const authToken = jwtTokenManager.createAuthToken(email, userInfo.access_token); + const authToken = jwtTokenManager.createAuthToken(email, userInfo?.access_token); //Get user data from Admin Back Office //This is very important to create User Local in LocalStorage in WorkAdventure diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index 3b55579f..127af38f 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -18,7 +18,7 @@ export const OPID_CLIENT_SECRET = process.env.OPID_CLIENT_SECRET || ""; export const OPID_CLIENT_ISSUER = process.env.OPID_CLIENT_ISSUER || ""; export const OPID_CLIENT_REDIRECT_URL = process.env.OPID_CLIENT_REDIRECT_URL || FRONT_URL + "/jwt"; export const OPID_PROFILE_SCREEN_PROVIDER = process.env.OPID_PROFILE_SCREEN_PROVIDER || ADMIN_URL + "/profile"; -export const DISABLE_ANONYMOUS = process.env.DISABLE_ANONYMOUS || false; +export const DISABLE_ANONYMOUS: boolean = process.env.DISABLE_ANONYMOUS === "true"; export { SECRET_KEY, From ba89d9b1221796025a85468eafaab1b478a9e322 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 16 Nov 2021 13:18:40 +0100 Subject: [PATCH 40/71] HotFix DISABLED_ROOM for env variable Signed-off-by: Gregoire Parant --- front/src/Connexion/Room.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index dd80b704..2f408126 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -14,7 +14,7 @@ export interface RoomRedirect { export class Room { public readonly id: string; public readonly isPublic: boolean; - private _authenticationMandatory: boolean = DISABLE_ANONYMOUS as boolean; + private _authenticationMandatory: boolean = DISABLE_ANONYMOUS; private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER; private _mapUrl: string | undefined; private _textures: CharacterTexture[] | undefined; @@ -107,7 +107,7 @@ export class Room { this._mapUrl = data.mapUrl; this._textures = data.textures; this._group = data.group; - this._authenticationMandatory = data.authenticationMandatory || (DISABLE_ANONYMOUS as boolean); + this._authenticationMandatory = data.authenticationMandatory || DISABLE_ANONYMOUS; this._iframeAuthentication = data.iframeAuthentication || OPID_LOGIN_SCREEN_PROVIDER; this._contactPage = data.contactPage || CONTACT_URL; return new MapDetail(data.mapUrl, data.textures); From e54732be1bf7560693915c4e9006a03f84dc072f Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 16 Nov 2021 14:46:32 +0100 Subject: [PATCH 41/71] HotFix authentication manager to get data from back Signed-off-by: Gregoire Parant --- front/src/Connexion/ConnectionManager.ts | 2 +- .../src/Controller/AuthenticateController.ts | 28 +++++++++++++------ pusher/src/Controller/IoSocketController.ts | 1 + pusher/src/Services/AdminApi.ts | 1 + 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index fbff365c..ffd91d39 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -226,7 +226,7 @@ class ConnectionManager { public async anonymousLogin(isBenchmark: boolean = false): Promise { const data = await Axios.post(`${PUSHER_URL}/anonymLogin`).then((res) => res.data); - this.localUser = new LocalUser(data.userUuid, []); + this.localUser = new LocalUser(data.userUuid, [], data.email); this.authToken = data.authToken; if (!isBenchmark) { // In benchmark, we don't have a local storage. diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 5e4eb19f..fb428141 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -63,25 +63,30 @@ export class AuthenticateController extends BaseController { if (token != undefined) { try { const authTokenData: AuthTokenData = jwtTokenManager.verifyJWTToken(token as string, false); + + //Get user data from Admin Back Office + //This is very important to create User Local in LocalStorage in WorkAdventure + const resUserData = await this.getUserByUserIdentifier( + authTokenData.identifier, + playUri as string, + IPAddress + ); + if (authTokenData.accessToken == undefined) { //if not nonce and code, user connected in anonymous //get data with identifier and return token if (!code && !nonce) { - const data = await this.getUserByUserIdentifier( - authTokenData.identifier, - playUri as string, - IPAddress - ); res.writeStatus("200"); this.addCorsHeaders(res); - return res.end(JSON.stringify({ ...data, authToken: token })); + return res.end(JSON.stringify({ ...resUserData, authToken: token })); } throw Error("Token cannot to be check on Hydra"); } + const resCheckTokenAuth = await openIDClient.checkTokenAuth(authTokenData.accessToken); res.writeStatus("200"); this.addCorsHeaders(res); - return res.end(JSON.stringify({ ...resCheckTokenAuth, authToken: token })); + return res.end(JSON.stringify({ ...resCheckTokenAuth, ...resUserData, authToken: token })); } catch (err) { console.info("User was not connected", err); } @@ -261,7 +266,14 @@ export class AuthenticateController extends BaseController { playUri: string, IPAddress: string ): Promise { - let data: FetchMemberDataByUuidResponse | object = {}; + let data: FetchMemberDataByUuidResponse = { + email: email, + userUuid: email, + tags: [], + messages: [], + visitCardUrl: null, + textures: [], + }; try { data = await adminApi.fetchMemberDataByUuid(email, playUri, IPAddress); } catch (err) { diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 9b6c1510..35fd08d5 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -189,6 +189,7 @@ export class IoSocketController { let memberTextures: CharacterTexture[] = []; const room = await socketManager.getOrCreateRoom(roomId); let userData: FetchMemberDataByUuidResponse = { + email: userIdentifier, userUuid: userIdentifier, tags: [], visitCardUrl: null, diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 6e1848eb..416b9cb6 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -22,6 +22,7 @@ export interface AdminBannedData { } export interface FetchMemberDataByUuidResponse { + email: string; userUuid: string; tags: string[]; visitCardUrl: string | null; From 0614fa7b47251d3a0bd9347a0f8f0974efc1b32c Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 16 Nov 2021 15:03:54 +0100 Subject: [PATCH 42/71] HotFix copy link to share it Signed-off-by: Gregoire Parant --- front/src/Components/Menu/AboutRoomSubMenu.svelte | 8 +++++--- front/src/Components/Menu/GuestSubMenu.svelte | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/front/src/Components/Menu/AboutRoomSubMenu.svelte b/front/src/Components/Menu/AboutRoomSubMenu.svelte index 16b5c057..2a546a14 100644 --- a/front/src/Components/Menu/AboutRoomSubMenu.svelte +++ b/front/src/Components/Menu/AboutRoomSubMenu.svelte @@ -40,7 +40,9 @@ }) function copyLink() { - HTMLShareLink.select(); + const input: HTMLInputElement = document.getElementById('input-share-link') as HTMLInputElement; + input.focus(); + input.select(); document.execCommand('copy'); } @@ -59,12 +61,12 @@

Share the link of the room !

- +

Information on the map

diff --git a/front/src/Components/Menu/GuestSubMenu.svelte b/front/src/Components/Menu/GuestSubMenu.svelte index 13a7981a..d054ff4d 100644 --- a/front/src/Components/Menu/GuestSubMenu.svelte +++ b/front/src/Components/Menu/GuestSubMenu.svelte @@ -2,7 +2,9 @@ let HTMLShareLink: HTMLInputElement; function copyLink() { - HTMLShareLink.select(); + const input: HTMLInputElement = document.getElementById('input-share-link') as HTMLInputElement; + input.focus(); + input.select(); document.execCommand('copy'); } @@ -22,12 +24,12 @@

Share the link of the room !

- +
From e5979998c5348cc7e124607eb11690e2b2bec635 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 16 Nov 2021 15:28:54 +0100 Subject: [PATCH 43/71] Update design of credit page Signed-off-by: Gregoire Parant --- .../Components/Menu/AboutRoomSubMenu.svelte | 59 +------------------ front/src/Components/Menu/GuestSubMenu.svelte | 6 +- 2 files changed, 3 insertions(+), 62 deletions(-) diff --git a/front/src/Components/Menu/AboutRoomSubMenu.svelte b/front/src/Components/Menu/AboutRoomSubMenu.svelte index 2a546a14..6c10cc76 100644 --- a/front/src/Components/Menu/AboutRoomSubMenu.svelte +++ b/front/src/Components/Menu/AboutRoomSubMenu.svelte @@ -4,7 +4,6 @@ let gameScene = gameManager.getCurrentGameScene(); - let HTMLShareLink: HTMLInputElement; let expandedMapCopyright = false; let expandedTilesetCopyright = false; @@ -38,37 +37,9 @@ } } }) - - function copyLink() { - const input: HTMLInputElement = document.getElementById('input-share-link') as HTMLInputElement; - input.focus(); - input.select(); - document.execCommand('copy'); - } - - async function shareLink() { - const shareData = {url: location.toString()}; - - try { - await navigator.share(shareData); - } catch (err) { - console.error('Error: ' + err); - copyLink(); - } - }
- -
-

Share the link of the room !

- - -

Information on the map

{mapName}

@@ -95,24 +66,6 @@ div.about-room-main { height: calc(100% - 56px); - section.share-url { - text-align: center; - margin-bottom: 20px; - - input { - width: 85%; - border-radius: 32px; - padding: 3px; - } - input::selection { - background-color: #209cee; - } - } - - section.is-mobile { - display: none; - } - h2, h3 { width: 100%; text-align: center; @@ -128,21 +81,11 @@ margin: 0; padding: 0; overflow-y: auto; - } + } } @media only screen and (max-width: 800px), only screen and (max-height: 800px) { div.about-room-main { - section.share-url.not-mobile { - display: none; - } - - section.is-mobile { - display: block; - text-align: center; - margin-bottom: 20px; - } - section.container-overflow { height: calc(100% - 120px); } diff --git a/front/src/Components/Menu/GuestSubMenu.svelte b/front/src/Components/Menu/GuestSubMenu.svelte index d054ff4d..bda16ca5 100644 --- a/front/src/Components/Menu/GuestSubMenu.svelte +++ b/front/src/Components/Menu/GuestSubMenu.svelte @@ -1,6 +1,4 @@