Completely getting rid of "userid"
authorDavid Négrier <d.negrier@thecodingmachine.com>
Thu, 14 May 2020 21:19:48 +0000 (23:19 +0200)
committerDavid Négrier <d.negrier@thecodingmachine.com>
Thu, 14 May 2020 21:20:43 +0000 (23:20 +0200)
Previously, userid was generated by the "/login" route and passed along.
This commit completely removes the uuid "userid" (and disables the LoginController too and any Jwt check).

"userid" is replaced by the "socket id" of the connection.
So a user is now identified using a socket id, which is unique for a given connection.

13 files changed:
back/src/Controller/AuthenticateController.ts
back/src/Controller/IoSocketController.ts
back/src/Model/Websocket/ExSocketInterface.ts
back/src/Model/Websocket/ExtRooms.ts
back/src/Model/Websocket/Identificable.ts [new file with mode: 0644]
back/src/Model/Websocket/Message.ts
back/src/Model/World.ts
back/tests/WorldTest.ts
front/src/Connexion.ts
front/src/Phaser/Game/GameManager.ts
front/src/Phaser/Game/GameScene.ts
front/src/Phaser/Login/LogincScene.ts
front/src/Phaser/Player/Player.ts

index 6eaa14ca3521d3449ddcda8f9372e80718aacf0e..db5fb1ce58226c66adcc60d362a63838d46fc537 100644 (file)
@@ -4,7 +4,7 @@ import {BAD_REQUEST, OK} from "http-status-codes";
 import {SECRET_KEY, URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..."
 import { uuid } from 'uuidv4';
 
-export class AuthenticateController{
+export class AuthenticateController {
     App : Application;
 
     constructor(App : Application) {
@@ -14,7 +14,8 @@ export class AuthenticateController{
 
     //permit to login on application. Return token to connect on Websocket IO.
     login(){
-        this.App.post("/login", (req: Request, res: Response) => {
+        // For now, let's completely forget the /login route.
+        /*this.App.post("/login", (req: Request, res: Response) => {
             let param = req.body;
             if(!param.email){
                 return res.status(BAD_REQUEST).send({
@@ -29,6 +30,6 @@ export class AuthenticateController{
                 mapUrlStart: URL_ROOM_STARTED,
                 userId: userId,
             });
-        });
+        });*/
     }
-}
\ No newline at end of file
+}
index d3410a13e8f675c5a5ff0afd562e47e917a45692..6349b352e35180473a8aca15d755b654830d4fe4 100644 (file)
@@ -14,6 +14,7 @@ import {UserInterface} from "_Model/UserInterface";
 enum SockerIoEvent {
     CONNECTION = "connection",
     DISCONNECT = "disconnect",
+    ATTRIBUTE_USER_ID = "attribute-user-id", // Sent from server to client just after the connexion is established to give the client its unique id.
     JOIN_ROOM = "join-room",
     USER_POSITION = "user-position",
     WEBRTC_SIGNAL = "webrtc-signal",
@@ -33,7 +34,8 @@ export class IoSocketController {
         this.Io = socketIO(server);
 
         // Authentication with token. it will be decoded and stored in the socket.
-        this.Io.use((socket: Socket, next) => {
+        // Completely commented for now, as we do not use the "/login" route at all.
+        /*this.Io.use((socket: Socket, next) => {
             if (!socket.handshake.query || !socket.handshake.query.token) {
                 return next(new Error('Authentication error'));
             }
@@ -47,7 +49,7 @@ export class IoSocketController {
                 (socket as ExSocketInterface).token = tokenDecoded;
                 next();
             });
-        });
+        });*/
 
         this.ioConnection();
         this.shareUsersPosition();
@@ -74,6 +76,7 @@ export class IoSocketController {
         let userId = lastUser.id;
         let client: ExSocketInterface|null = this.searchClientById(userId);
         if (client === null) {
+            console.warn('Could not find client ', userId, ' in group')
             return;
         }
         let roomId = client.roomId;
@@ -180,7 +183,6 @@ export class IoSocketController {
                     socket.leave(Client.webRtcRoomId);
 
                     //delete all socket information
-                    delete Client.userId;
                     delete Client.webRtcRoomId;
                     delete Client.roomId;
                     delete Client.token;
@@ -190,6 +192,9 @@ export class IoSocketController {
                     console.error(e);
                 }
             });
+
+            // Let's send the user id to the user
+            socket.emit(SockerIoEvent.ATTRIBUTE_USER_ID, socket.id);
         });
     }
 
@@ -201,11 +206,12 @@ export class IoSocketController {
         let clients: Array<any> = Object.values(this.Io.sockets.sockets);
         for (let i = 0; i < clients.length; i++) {
             let client: ExSocketInterface = clients[i];
-            if (client.userId !== userId) {
-                continue
+            if (client.id !== userId) {
+                continue;
             }
             return client;
         }
+        console.log("Could not find user with id ", userId);
         return null;
     }
 
@@ -216,7 +222,7 @@ export class IoSocketController {
         let clients: Array<any> = Object.values(this.Io.sockets.sockets);
         for (let i = 0; i < clients.length; i++) {
             let client: ExSocketInterface = clients[i];
-            if (client.userId !== userId) {
+            if (client.id !== userId) {
                 continue
             }
             return client;
@@ -230,7 +236,7 @@ export class IoSocketController {
      */
     sendDisconnectedEvent(Client: ExSocketInterface) {
         Client.broadcast.emit(SockerIoEvent.WEBRTC_DISCONNECT, JSON.stringify({
-            userId: Client.userId
+            userId: Client.id
         }));
 
         //disconnect webrtc room
@@ -248,14 +254,16 @@ export class IoSocketController {
     leaveRoom(Client : ExSocketInterface){
         //lease previous room and world
         if(Client.roomId){
-            //user leave previous room
-            Client.leave(Client.roomId);
             //user leave previous world
             let world : World|undefined = this.Worlds.get(Client.roomId);
             if(world){
+                console.log('Entering world.leave')
                 world.leave(Client);
-                this.Worlds.set(Client.roomId, world);
+                //this.Worlds.set(Client.roomId, world);
             }
+            //user leave previous room
+            Client.leave(Client.roomId);
+            delete Client.roomId;
         }
     }
     /**
@@ -293,7 +301,7 @@ export class IoSocketController {
                 });
             });
             //join world
-            world.join(messageUserPosition);
+            world.join(Client, messageUserPosition);
             this.Worlds.set(messageUserPosition.roomId, world);
         }
 
@@ -322,11 +330,11 @@ export class IoSocketController {
         clients.forEach((client: ExSocketInterface, index: number) => {
 
             let clientsId = clients.reduce((tabs: Array<any>, clientId: ExSocketInterface, indexClientId: number) => {
-                if (!clientId.userId || clientId.userId === client.userId) {
+                if (!clientId.id || clientId.id === client.id) {
                     return tabs;
                 }
                 tabs.push({
-                    userId: clientId.userId,
+                    userId: clientId.id,
                     name: clientId.name,
                     initiator: index <= indexClientId
                 });
@@ -341,7 +349,7 @@ export class IoSocketController {
     saveUserInformation(socket: ExSocketInterface, message: MessageUserPosition) {
         socket.position = message.position;
         socket.roomId = message.roomId;
-        socket.userId = message.userId;
+        //socket.userId = message.userId;
         socket.name = message.name;
         socket.character = message.character;
     }
@@ -354,9 +362,9 @@ export class IoSocketController {
         }
         rooms.refreshUserPosition(rooms, this.Io);
 
-        // update position in the worl
+        // update position in the world
         let data = {
-            userId: Client.userId,
+            userId: Client.id,
             roomId: Client.roomId,
             position: Client.position,
             name: Client.name,
@@ -367,7 +375,7 @@ export class IoSocketController {
         if (!world) {
             return;
         }
-        world.updatePosition(messageUserPosition);
+        world.updatePosition(Client, messageUserPosition);
         this.Worlds.set(messageUserPosition.roomId, world);
     }
 
index 22cd29c2dfdaa65534c43843ad4e96396acab85d..c55a97595adc062a774673ef6cb317737fa6f4ec 100644 (file)
@@ -1,11 +1,12 @@
 import {Socket} from "socket.io";
 import {PointInterface} from "./PointInterface";
+import {Identificable} from "./Identificable";
 
-export interface ExSocketInterface extends Socket {
+export interface ExSocketInterface extends Socket, Identificable {
     token: any;
     roomId: string;
     webRtcRoomId: string;
-    userId: string;
+    //userId: string;
     name: string;
     character: string;
     position: PointInterface;
index 7fb6216d2421afd320fa6e626865dfc79c761c8e..43395a28990ed90e50f0ccc29d9b477fb1f5a6ff 100644 (file)
@@ -22,7 +22,7 @@ let RefreshUserPositionFunction = function(rooms : ExtRooms, Io: socketIO.Server
             continue;
         }
         let data = {
-            userId: socket.userId,
+            userId: socket.id,
             roomId: socket.roomId,
             position: socket.position,
             name: socket.name,
diff --git a/back/src/Model/Websocket/Identificable.ts b/back/src/Model/Websocket/Identificable.ts
new file mode 100644 (file)
index 0000000..8c34425
--- /dev/null
@@ -0,0 +1,3 @@
+export interface Identificable {
+    id: string;
+}
index ccf5d28722ed60634546e21de44abf7011660128..f4bdead88d3dba98bb6a87beda35b28e4c5ed35b 100644 (file)
@@ -16,6 +16,7 @@ export class Message {
     }
 
     toJson() {
+
         return {
             userId: this.userId,
             roomId: this.roomId,
index 72bf029cf1919f943bd21a27809613d64bc1fcc6..19e8c1940496f613175c6178a6e034f8fa78506b 100644 (file)
@@ -5,6 +5,7 @@ import {Distance} from "./Distance";
 import {UserInterface} from "./UserInterface";
 import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface";
 import {PositionInterface} from "_Model/PositionInterface";
+import {Identificable} from "_Model/Websocket/Identificable";
 
 export type ConnectCallback = (user: string, group: Group) => void;
 export type DisconnectCallback = (user: string, group: Group) => void;
@@ -14,17 +15,17 @@ export type GroupUpdatedCallback = (group: Group) => void;
 export type GroupDeletedCallback = (uuid: string, lastUser: UserInterface) => void;
 
 export class World {
-    private minDistance: number;
-    private groupRadius: number;
+    private readonly minDistance: number;
+    private readonly groupRadius: number;
 
     // Users, sorted by ID
-    private users: Map<string, UserInterface>;
-    private groups: Group[];
+    private readonly users: Map<string, UserInterface>;
+    private readonly groups: Group[];
 
-    private connectCallback: ConnectCallback;
-    private disconnectCallback: DisconnectCallback;
-    private groupUpdatedCallback: GroupUpdatedCallback;
-    private groupDeletedCallback: GroupDeletedCallback;
+    private readonly connectCallback: ConnectCallback;
+    private readonly disconnectCallback: DisconnectCallback;
+    private readonly groupUpdatedCallback: GroupUpdatedCallback;
+    private readonly groupDeletedCallback: GroupDeletedCallback;
 
     constructor(connectCallback: ConnectCallback,
                 disconnectCallback: DisconnectCallback,
@@ -47,25 +48,29 @@ export class World {
         return this.groups;
     }
 
-    public join(userPosition: MessageUserPosition): void {
-        this.users.set(userPosition.userId, {
-            id: userPosition.userId,
+    public join(socket : Identificable, userPosition: MessageUserPosition): void {
+        this.users.set(socket.id, {
+            id: socket.id,
             position: userPosition.position
         });
         // Let's call update position to trigger the join / leave room
-        this.updatePosition(userPosition);
+        this.updatePosition(socket, userPosition);
     }
 
-    public leave(user : ExSocketInterface){
+    public leave(user : Identificable){
         let userObj = this.users.get(user.id);
+        if (userObj === undefined) {
+            // FIXME: this seems always wrong. I guess user.id is different from userPosition.userId
+            console.warn('User ', user.id, 'does not belong to world! It should!');
+        }
         if (userObj !== undefined && typeof userObj.group !== 'undefined') {
-            this.leaveGroup(user);
+            this.leaveGroup(userObj);
         }
-        this.users.delete(user.userId);
+        this.users.delete(user.id);
     }
 
-    public updatePosition(userPosition: MessageUserPosition): void {
-        let user = this.users.get(userPosition.userId);
+    public updatePosition(socket : Identificable, userPosition: MessageUserPosition): void {
+        let user = this.users.get(socket.id);
         if(typeof user === 'undefined') {
             return;
         }
@@ -118,7 +123,6 @@ export class World {
             throw new Error("The user is part of no group");
         }
         group.leave(user);
-
         if (group.isEmpty()) {
             this.groupDeletedCallback(group.getId(), user);
             group.destroy();
index 57f0f3f4dd1cb5c2a54f070b19673185042f27e7..148e738387b5c3597e1158ab8170eefa345fc2cc 100644 (file)
@@ -17,36 +17,36 @@ describe("World", () => {
 
         let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
 
-        world.join(new MessageUserPosition({
-            userId: "foo",
+        world.join({ id: "foo" }, new MessageUserPosition({
+            userId: "foofoo",
             roomId: 1,
             position: new Point(100, 100)
         }));
 
-        world.join(new MessageUserPosition({
-            userId: "bar",
+        world.join({ id: "bar" }, new MessageUserPosition({
+            userId: "barbar",
             roomId: 1,
             position: new Point(500, 100)
         }));
 
-        world.updatePosition(new MessageUserPosition({
-            userId: "bar",
+        world.updatePosition({ id: "bar" }, new MessageUserPosition({
+            userId: "barbar",
             roomId: 1,
             position: new Point(261, 100)
         }));
 
         expect(connectCalledNumber).toBe(0);
 
-        world.updatePosition(new MessageUserPosition({
-            userId: "bar",
+        world.updatePosition({ id: "bar" }, new MessageUserPosition({
+            userId: "barbar",
             roomId: 1,
             position: new Point(101, 100)
         }));
 
         expect(connectCalledNumber).toBe(2);
 
-        world.updatePosition(new MessageUserPosition({
-            userId: "bar",
+        world.updatePosition({ id: "bar" }, new MessageUserPosition({
+            userId: "barbar",
             roomId: 1,
             position: new Point(102, 100)
         }));
@@ -64,14 +64,14 @@ describe("World", () => {
 
         let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
 
-        world.join(new MessageUserPosition({
-            userId: "foo",
+        world.join({ id: "foo" }, new MessageUserPosition({
+            userId: "foofoo",
             roomId: 1,
             position: new Point(100, 100)
         }));
 
-        world.join(new MessageUserPosition({
-            userId: "bar",
+        world.join({ id: "bar" }, new MessageUserPosition({
+            userId: "barbar",
             roomId: 1,
             position: new Point(200, 100)
         }));
@@ -80,16 +80,16 @@ describe("World", () => {
         connectCalled = false;
 
         // baz joins at the outer limit of the group
-        world.join(new MessageUserPosition({
-            userId: "baz",
+        world.join({ id: "baz" }, new MessageUserPosition({
+            userId: "bazbaz",
             roomId: 1,
             position: new Point(311, 100)
         }));
 
         expect(connectCalled).toBe(false);
 
-        world.updatePosition(new MessageUserPosition({
-            userId: "baz",
+        world.updatePosition({ id: "baz" }, new MessageUserPosition({
+            userId: "bazbaz",
             roomId: 1,
             position: new Point(309, 100)
         }));
@@ -109,14 +109,14 @@ describe("World", () => {
 
         let world = new World(connect, disconnect, 160, 160, () => {}, () => {});
 
-        world.join(new MessageUserPosition({
-            userId: "foo",
+        world.join({ id: "foo" }, new MessageUserPosition({
+            userId: "foofoo",
             roomId: 1,
             position: new Point(100, 100)
         }));
 
-        world.join(new MessageUserPosition({
-            userId: "bar",
+        world.join({ id: "bar" }, new MessageUserPosition({
+            userId: "barbar",
             roomId: 1,
             position: new Point(259, 100)
         }));
@@ -124,16 +124,16 @@ describe("World", () => {
         expect(connectCalled).toBe(true);
         expect(disconnectCallNumber).toBe(0);
 
-        world.updatePosition(new MessageUserPosition({
-            userId: "bar",
+        world.updatePosition({ id: "bar" }, new MessageUserPosition({
+            userId: "barbar",
             roomId: 1,
             position: new Point(100+160+160+1, 100)
         }));
 
         expect(disconnectCallNumber).toBe(2);
 
-        world.updatePosition(new MessageUserPosition({
-            userId: "bar",
+        world.updatePosition({ id: "bar" }, new MessageUserPosition({
+            userId: "barbar",
             roomId: 1,
             position: new Point(262, 100)
         }));
index d6d37025ff54af8b1ad32b123cb22f275470bdb2..f29bef2ef1a388e260aa09359b14fb64f4e99a62 100644 (file)
@@ -18,7 +18,8 @@ enum EventMessage{
     GROUP_DELETE = "group-delete",
 
     CONNECT_ERROR = "connect_error",
-    RECONNECT = "reconnect"
+    RECONNECT = "reconnect",
+    ATTRIBUTE_USER_ID = "attribute-user-id" // Sent from server to client just after the connexion is established to give the client its unique id.
 }
 
 class Message {
@@ -184,25 +185,37 @@ export class Connexion implements ConnexionInterface {
      * @param characterSelected
      */
     createConnexion(characterSelected: string): Promise<ConnexionInterface> {
-        return Axios.post(`${API_URL}/login`, {email: this.email})
+        /*return Axios.post(`${API_URL}/login`, {email: this.email})
             .then((res) => {
                 this.token = res.data.token;
-                this.userId = res.data.userId;
+                this.userId = res.data.userId;*/
 
                 this.socket = SocketIo(`${API_URL}`, {
-                    query: {
+                    /*query: {
                         token: this.token
-                    }
+                    }*/
                 });
 
                 this.connectSocketServer();
 
-                return res.data;
+                // TODO: maybe trigger promise only when connexion is established?
+                let promise = new Promise<ConnexionInterface>((resolve, reject) => {
+                    /*console.log('PROMISE CREATED')
+                    this.socket.on('connection', () => {
+                        console.log('CONNECTED');
+                        resolve(this);
+                    });*/
+                    resolve(this);
+                });
+
+                return promise;
+
+         /*       return res.data;
             })
             .catch((err) => {
                 console.error(err);
                 throw err;
-            });
+            });*/
     }
 
     /**
@@ -229,6 +242,7 @@ export class Connexion implements ConnexionInterface {
         }
 
         //listen event
+        this.attributeUserId();
         this.positionOfAllUser();
         this.disconnectServer();
         this.errorMessage();
@@ -286,6 +300,15 @@ export class Connexion implements ConnexionInterface {
         this.socket.emit(EventMessage.USER_POSITION, messageUserPosition.toString());
     }
 
+    attributeUserId(): void {
+        // This event is received as soon as the connexion is established.
+        // It allows informing the browser of its own user id.
+        this.socket.on(EventMessage.ATTRIBUTE_USER_ID, (userId: string) => {
+            console.log('Received my user id: ', userId);
+            this.userId = userId;
+        });
+    }
+
     /**
      * The data sent is an array with information for each user :
      * [
index 6a4381ddf9d9c7894887caa767f72027e1dbdf70..b886de9ebebb3be314711073383e97c01c7aeff0 100644 (file)
@@ -67,7 +67,7 @@ export class GameManager {
      */
     createCurrentPlayer(): void {
         //Get started room send by the backend
-        this.currentGameScene.createCurrentPlayer(this.ConnexionInstance.userId);
+        this.currentGameScene.createCurrentPlayer();
         this.status = StatusGameManagerEnum.CURRENT_USER_CREATED;
     }
 
@@ -119,6 +119,10 @@ export class GameManager {
         return this.playerName;
     }
 
+    getPlayerId(): string {
+        return this.ConnexionInstance.userId;
+    }
+
     getCharacterSelected(): string {
         return this.characterUserSelected;
     }
index 7b32e3efa82dc5b01db7a4d9bc0eba9ac0f5fa8a..1e4d1e6e7e869a1b9a6f1b9119cf2f3b6a64c475 100644 (file)
@@ -15,7 +15,7 @@ export enum Textures {
 
 export interface GameSceneInterface extends Phaser.Scene {
     Map: Phaser.Tilemaps.Tilemap;
-    createCurrentPlayer(UserId : string) : void;
+    createCurrentPlayer() : void;
     shareUserPosition(UsersPosition : Array<MessageUserPositionInterface>): void;
     shareGroupPosition(groupPositionMessage: GroupCreatedUpdatedMessageInterface): void;
     updateOrCreateMapPlayer(UsersPosition : Array<MessageUserPositionInterface>): void;
@@ -266,11 +266,11 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface, Creat
         })
     }
 
-    createCurrentPlayer(UserId : string){
+    createCurrentPlayer(){
         //initialise player
         //TODO create animation moving between exit and strat
         this.CurrentPlayer = new Player(
-            UserId,
+            null, // The current player is not has no id (because the id can change if connexion is lost and we should check that id using the GameManager.
             this,
             this.startX,
             this.startY,
@@ -347,9 +347,11 @@ export class GameScene extends Phaser.Scene implements GameSceneInterface, Creat
             return;
         }
 
+        let currentPlayerId = this.GameManager.getPlayerId();
+
         //add or create new user
         UsersPosition.forEach((userPosition : MessageUserPositionInterface) => {
-            if(userPosition.userId === this.CurrentPlayer.userId){
+            if(userPosition.userId === currentPlayerId){
                 return;
             }
             let player = this.findPlayerInMap(userPosition.userId);
index ea21e9f5aee53fd0e94e7c07dc00b026d2f7889b..f7dd0029accb7fa2f681d68cd8f71d3159c6ccd7 100644 (file)
@@ -90,7 +90,7 @@ export class LogincScene extends Phaser.Scene implements GameSceneInterface {
         });
 
         /*create user*/
-        this.createCurrentPlayer("test");
+        this.createCurrentPlayer();
         cypressAsserter.initFinished();
     }
 
@@ -144,7 +144,7 @@ export class LogincScene extends Phaser.Scene implements GameSceneInterface {
         throw new Error("Method not implemented.");
     }
 
-    createCurrentPlayer(UserId: string): void {
+    createCurrentPlayer(): void {
         for (let i = 0; i <PLAYER_RESOURCES.length; i++) {
             let playerResource = PLAYER_RESOURCES[i];
             let player = this.physics.add.sprite(playerResource.x, playerResource.y, playerResource.name, playerResource.name);
index 932f889d8f7a24e4cbd13527840b26a1e43b2487..dd33bd3cb5f3450ba2962874fb3ad051a2536b56 100644 (file)
@@ -7,7 +7,6 @@ import {PlayableCaracter} from "../Entity/PlayableCaracter";
 
 export const hasMovedEventName = "hasMoved";
 export interface CurrentGamerInterface extends PlayableCaracter{
-    userId : string;
     initAnimation() : void;
     moveUser(delta: number) : void;
     say(text : string) : void;