diff --git a/back/src/Messages/JsonMessages/.gitignore b/back/src/Messages/JsonMessages/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/back/src/Messages/JsonMessages/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 7d7b24a5..0e8203cf 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -27,7 +27,7 @@ import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; import { RoomSocket, ZoneSocket } from "src/RoomManager"; import { Admin } from "../Model/Admin"; import { adminApi } from "../Services/AdminApi"; -import { isMapDetailsData, MapDetailsData } from "../Services/AdminApi/MapDetailsData"; +import { isMapDetailsData, MapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; import { ITiledMap } from "@workadventure/tiled-map-type-guard/dist"; import { mapFetcher } from "../Services/MapFetcher"; import { VariablesManager } from "../Services/VariablesManager"; @@ -35,7 +35,7 @@ import { ADMIN_API_URL } from "../Enum/EnvironmentVariable"; import { LocalUrlError } from "../Services/LocalUrlError"; import { emitErrorOnRoomSocket } from "../Services/MessageHelpers"; import { VariableError } from "../Services/VariableError"; -import { isRoomRedirect } from "../Services/AdminApi/RoomRedirect"; +import { isRoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; export type ConnectCallback = (user: User, group: Group) => void; export type DisconnectCallback = (user: User, group: Group) => void; @@ -571,8 +571,11 @@ export class GameRoom { return { mapUrl, policy_type: 1, - textures: [], tags: [], + authenticationMandatory: null, + roomSlug: null, + contactPage: null, + group: null, }; } diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts index f4fa40b6..148877af 100644 --- a/back/src/Services/AdminApi.ts +++ b/back/src/Services/AdminApi.ts @@ -1,7 +1,7 @@ import { ADMIN_API_TOKEN, ADMIN_API_URL } from "../Enum/EnvironmentVariable"; import Axios from "axios"; -import { isMapDetailsData, MapDetailsData } from "./AdminApi/MapDetailsData"; -import { isRoomRedirect, RoomRedirect } from "./AdminApi/RoomRedirect"; +import { isMapDetailsData, MapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; +import { isRoomRedirect, RoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; class AdminApi { async fetchMapDetails(playUri: string): Promise { diff --git a/back/src/Services/AdminApi/CharacterTexture.ts b/back/src/Services/AdminApi/CharacterTexture.ts deleted file mode 100644 index 055b3033..00000000 --- a/back/src/Services/AdminApi/CharacterTexture.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as tg from "generic-type-guard"; - -export const isCharacterTexture = new tg.IsInterface() - .withProperties({ - id: tg.isNumber, - level: tg.isNumber, - url: tg.isString, - rights: tg.isString, - }) - .get(); -export type CharacterTexture = tg.GuardedType; diff --git a/back/src/Services/AdminApi/MapDetailsData.ts b/back/src/Services/AdminApi/MapDetailsData.ts deleted file mode 100644 index d3402b92..00000000 --- a/back/src/Services/AdminApi/MapDetailsData.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as tg from "generic-type-guard"; -import { isCharacterTexture } from "./CharacterTexture"; -import { isAny, isNumber } from "generic-type-guard"; - -/*const isNumericEnum = - (vs: T) => - (v: any): v is T => - typeof v === "number" && v in vs;*/ - -export const isMapDetailsData = new tg.IsInterface() - .withProperties({ - mapUrl: tg.isString, - policy_type: isNumber, //isNumericEnum(GameRoomPolicyTypes), - tags: tg.isArray(tg.isString), - textures: tg.isArray(isCharacterTexture), - }) - .withOptionalProperties({ - roomSlug: tg.isUnion(tg.isString, tg.isNull), // deprecated - }) - .get(); -export type MapDetailsData = tg.GuardedType; diff --git a/back/src/Services/AdminApi/RoomRedirect.ts b/back/src/Services/AdminApi/RoomRedirect.ts deleted file mode 100644 index 7257ebd3..00000000 --- a/back/src/Services/AdminApi/RoomRedirect.ts +++ /dev/null @@ -1,8 +0,0 @@ -import * as tg from "generic-type-guard"; - -export const isRoomRedirect = new tg.IsInterface() - .withProperties({ - redirectUrl: tg.isString, - }) - .get(); -export type RoomRedirect = tg.GuardedType; diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 3ea8375b..f5896955 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -340,8 +340,21 @@ export class RoomConnection implements RoomConnection { this.userId = roomJoinedMessage.currentUserId; this.tags = roomJoinedMessage.tag; this._userRoomToken = roomJoinedMessage.userRoomToken; + + // If one of the URLs sent to us does not exist, let's go to the Woka selection screen. + for (const characterLayer of roomJoinedMessage.characterLayer) { + if (!characterLayer.url) { + this.goToSelectYourWokaScene(); + this.closed = true; + break; + } + } + if (this.closed) { + break; + } + const characterLayers = roomJoinedMessage.characterLayer.map( - this.mapCharactgerLayerToBodyResourceDescription.bind(this) + this.mapCharacterLayerToBodyResourceDescription.bind(this) ); this._roomJoinedMessageStream.next({ @@ -360,10 +373,7 @@ export class RoomConnection implements RoomConnection { break; } case "invalidTextureMessage": { - menuVisiblilityStore.set(false); - menuIconVisiblilityStore.set(false); - selectCharacterSceneVisibleStore.set(true); - gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene()); + this.goToSelectYourWokaScene(); this.closed = true; break; @@ -608,7 +618,7 @@ export class RoomConnection implements RoomConnection { }); }*/ - private mapCharactgerLayerToBodyResourceDescription( + private mapCharacterLayerToBodyResourceDescription( characterLayer: CharacterLayerMessage ): BodyResourceDescriptionInterface { return { @@ -624,9 +634,7 @@ export class RoomConnection implements RoomConnection { throw new Error("Invalid JOIN_ROOM message"); } - const characterLayers = message.characterLayers.map( - this.mapCharactgerLayerToBodyResourceDescription.bind(this) - ); + const characterLayers = message.characterLayers.map(this.mapCharacterLayerToBodyResourceDescription.bind(this)); const companion = message.companion; @@ -886,4 +894,11 @@ export class RoomConnection implements RoomConnection { public get userRoomToken(): string | undefined { return this._userRoomToken; } + + private goToSelectYourWokaScene(): void { + menuVisiblilityStore.set(false); + menuIconVisiblilityStore.set(false); + selectCharacterSceneVisibleStore.set(true); + gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene()); + } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 7248c2ed..b6d8e4fe 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1535,7 +1535,7 @@ ${escapedMessage} this.messageSubscription?.unsubscribe(); this.userInputManager.destroy(); this.pinchManager?.destroy(); - this.emoteManager.destroy(); + this.emoteManager?.destroy(); this.cameraManager.destroy(); this.peerStoreUnsubscribe(); this.emoteUnsubscribe(); diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 67cdc952..1708c634 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -43,9 +43,22 @@ export class SelectCharacterScene extends AbstractCharacterScene { } preload() { - const wokaMetadataKey = "woka-list"; + const wokaMetadataKey = "woka/list"; this.cache.json.remove(wokaMetadataKey); - this.load.json(wokaMetadataKey, `${PUSHER_URL}/${wokaMetadataKey}`); + + // FIXME: window.location.href is wrong. We need the URL of the main room (so we need to apply any redirect before!) + this.load.json( + wokaMetadataKey, + `${PUSHER_URL}/${wokaMetadataKey}/` + encodeURIComponent(window.location.href), + undefined, + { + responseType: "text", + headers: { + Authorization: localUserStore.getAuthToken() ?? "", + }, + withCredentials: true, + } + ); this.load.once(`filecomplete-json-${wokaMetadataKey}`, () => { this.playerTextures.loadPlayerTexturesMetadata(this.cache.json.get(wokaMetadataKey)); this.loadSelectSceneCharacters() diff --git a/messages/package.json b/messages/package.json index be11d915..524f9996 100644 --- a/messages/package.json +++ b/messages/package.json @@ -9,9 +9,10 @@ "copy-to-front-ts-proto": "sed 's/import { Observable } from \"rxjs\";/import type { Observable } from \"rxjs\";/g' ts-proto-generated/protos/messages.ts > ../front/src/Messages/ts-proto-generated/messages.ts", "copy-to-pusher": "rm -rf ../pusher/src/Messages/generated && cp -rf generated/ ../pusher/src/Messages/generated", "json-copy-to-pusher": "rm -rf ../pusher/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../pusher/src/Messages/JsonMessages/", + "json-copy-to-back": "rm -rf ../back/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../back/src/Messages/JsonMessages/", "json-copy-to-front": "rm -rf ../front/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../front/src/Messages/JsonMessages/", "precommit": "lint-staged", - "proto-all": "yarn run proto && yarn run ts-proto && yarn run copy-to-back && yarn run copy-to-front-ts-proto && yarn run copy-to-pusher && yarn run json-copy-to-pusher && yarn run json-copy-to-front", + "proto-all": "yarn run proto && yarn run ts-proto && yarn run copy-to-back && yarn run copy-to-front-ts-proto && yarn run copy-to-pusher && yarn run json-copy-to-pusher && yarn run json-copy-to-back && yarn run json-copy-to-front", "proto:watch": "yarn run proto-all; inotifywait -q -m -e close_write protos/messages.proto JsonMessages/ | while read -r filename event; do yarn run proto-all; done", "pretty": "yarn prettier --write 'JsonMessages/**/*.ts'", "pretty-check": "yarn prettier --check 'JsonMessages/**/*.ts'" diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 658367b4..110d2f6c 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -68,7 +68,7 @@ export class AuthenticateController extends BaseHttpController { ); res.status(302); res.setHeader("Location", loginUri); - return res; + return res.send(""); } catch (e) { console.error("openIDLogin => e", e); this.castErrorToResponse(e, res); diff --git a/pusher/src/Controller/WokaListController.ts b/pusher/src/Controller/WokaListController.ts index e4e563f5..0789fda6 100644 --- a/pusher/src/Controller/WokaListController.ts +++ b/pusher/src/Controller/WokaListController.ts @@ -1,11 +1,16 @@ import { BaseHttpController } from "./BaseHttpController"; import { wokaService } from "../Services/WokaService"; import * as tg from "generic-type-guard"; +import { jwtTokenManager } from "../Services/JWTTokenManager"; export class WokaListController extends BaseHttpController { routes() { + this.app.options("/woka/list/:roomUrl", {}, async (req, res) => { + res.status(200).send(""); + }); + // eslint-disable-next-line @typescript-eslint/no-misused-promises - this.app.get("/woka/list/:roomId", {}, async (req, res) => { + this.app.get("/woka/list/:roomUrl", {}, async (req, res) => { const token = req.header("Authorization"); if (!token) { @@ -13,12 +18,19 @@ export class WokaListController extends BaseHttpController { return; } + try { + const jwtData = jwtTokenManager.verifyJWTToken(token); + // Let's set the "uuid" param + req.params["uuid"] = jwtData.identifier; + } catch (e) { + console.error("Connection refused for token: " + token, e); + res.status(401).send("Invalid token sent"); + return; + } + const isParameters = new tg.IsInterface() .withProperties({ - roomId: tg.isString, - }) - .withOptionalProperties({ - messages: tg.isArray(tg.isUnknown), + roomUrl: tg.isString, }) .get(); @@ -26,8 +38,8 @@ export class WokaListController extends BaseHttpController { return res.status(400).send("Unknown parameters"); } - const roomId = req.path_parameters.roomId; - const wokaList = await wokaService.getWokaList(roomId, token); + const roomUrl = decodeURIComponent(req.path_parameters.roomUrl); + const wokaList = await wokaService.getWokaList(roomUrl, req.params["uuid"]); if (!wokaList) { return res.status(500).send("Error on getting woka list"); diff --git a/pusher/src/Middleware/Cors.ts b/pusher/src/Middleware/Cors.ts index 54fd32ad..9353d7a8 100644 --- a/pusher/src/Middleware/Cors.ts +++ b/pusher/src/Middleware/Cors.ts @@ -10,6 +10,7 @@ export function cors(req: Request, res: Response, next?: MiddlewareNext): Middle ); res.setHeader("access-control-allow-methods", "GET, POST, OPTIONS, PUT, PATCH, DELETE"); res.setHeader("access-control-allow-origin", FRONT_URL); + res.setHeader("access-control-allow-credentials", "true"); if (next) { next(); diff --git a/pusher/src/Services/AdminWokaService.ts b/pusher/src/Services/AdminWokaService.ts index acc10151..96f276d9 100644 --- a/pusher/src/Services/AdminWokaService.ts +++ b/pusher/src/Services/AdminWokaService.ts @@ -7,10 +7,14 @@ class AdminWokaService implements WokaServiceInterface { /** * Returns the list of all available Wokas for the current user. */ - getWokaList(roomId: string, token: string): Promise { + getWokaList(roomUrl: string, token: string): Promise { return axios - .get(`${ADMIN_API_URL}/api/woka/list/${roomId}/${token}`, { + .get(`${ADMIN_API_URL}/api/woka/list`, { headers: { Authorization: `${ADMIN_API_TOKEN}` }, + params: { + roomUrl, + uuid: token, + }, }) .then((res) => { if (isWokaList(res.data)) {