rewrote the authorisation flow: give more responsability to gameManager and less to gameScene

This commit is contained in:
arp 2020-10-12 16:23:07 +02:00
parent 032facb75f
commit 02c193a262
14 changed files with 244 additions and 170 deletions

View file

@ -1,8 +1,7 @@
import {URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..."
import { v4 } from 'uuid';
import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js";
import {BaseController} from "./BaseController";
import {adminApi, AdminApiData} from "../Services/AdminApi";
import {adminApi} from "../Services/AdminApi";
import {jwtTokenManager} from "../Services/JWTTokenManager";
export interface TokenInterface {
@ -13,18 +12,19 @@ export class AuthenticateController extends BaseController {
constructor(private App : TemplatedApp) {
super();
this.login();
this.register();
this.anonymLogin();
}
//permit to login on application. Return token to connect on Websocket IO.
login(){
this.App.options("/login", (res: HttpResponse, req: HttpRequest) => {
//Try to login with an admin token
register(){
this.App.options("/register", (res: HttpResponse, req: HttpRequest) => {
this.addCorsHeaders(res);
res.end();
});
this.App.post("/login", (res: HttpResponse, req: HttpRequest) => {
this.App.post("/register", (res: HttpResponse, req: HttpRequest) => {
(async () => {
this.addCorsHeaders(res);
@ -36,35 +36,25 @@ export class AuthenticateController extends BaseController {
//todo: what to do if the organizationMemberToken is already used?
const organizationMemberToken:string|null = param.organizationMemberToken;
const mapSlug:string|null = param.mapSlug;
try {
let userUuid;
let mapUrlStart;
let newUrl: string|null = null;
if (typeof organizationMemberToken != 'string') throw new Error('No organization token');
const data = await adminApi.fetchMemberDataByToken(organizationMemberToken);
if (organizationMemberToken) {
const data = await adminApi.fetchMemberDataByToken(organizationMemberToken);
userUuid = data.userUuid;
mapUrlStart = data.mapUrlStart;
newUrl = this.getNewUrlOnAdminAuth(data)
} else if (mapSlug !== null) {
userUuid = v4();
mapUrlStart = mapSlug;
newUrl = null;
} else {
userUuid = v4();
mapUrlStart = host.replace('api.', 'maps.') + URL_ROOM_STARTED;
newUrl = '_/global/'+mapUrlStart;
}
const userUuid = data.userUuid;
const organizationSlug = data.organizationSlug;
const worldSlug = data.worldSlug;
const roomSlug = data.roomSlug;
const mapUrlStart = data.mapUrlStart;
const authToken = jwtTokenManager.createJWTToken(userUuid);
res.writeStatus("200 OK").end(JSON.stringify({
authToken,
userUuid,
organizationSlug,
worldSlug,
roomSlug,
mapUrlStart,
newUrl,
}));
} catch (e) {
@ -75,12 +65,32 @@ export class AuthenticateController extends BaseController {
})();
});
}
private getNewUrlOnAdminAuth(data:AdminApiData): string {
const organizationSlug = data.organizationSlug;
const worldSlug = data.worldSlug;
const roomSlug = data.roomSlug;
return '/@/'+organizationSlug+'/'+worldSlug+'/'+roomSlug;
//permit to login on application. Return token to connect on Websocket IO.
anonymLogin(){
this.App.options("/anonymLogin", (res: HttpResponse, req: HttpRequest) => {
this.addCorsHeaders(res);
res.end();
});
this.App.post("/anonymLogin", (res: HttpResponse, req: HttpRequest) => {
(async () => {
this.addCorsHeaders(res);
res.onAborted(() => {
console.warn('Login request was aborted');
})
const userUuid = v4();
const authToken = jwtTokenManager.createJWTToken(userUuid);
res.writeStatus("200 OK").end(JSON.stringify({
authToken,
userUuid,
}));
})();
});
}
}

View file

@ -42,6 +42,7 @@ import {cpuTracker} from "../Services/CpuTracker";
import {ViewportInterface} from "../Model/Websocket/ViewportMessage";
import {jwtTokenManager} from "../Services/JWTTokenManager";
import {adminApi} from "../Services/AdminApi";
import {RoomIdentifier} from "../Model/RoomIdentifier";
function emitInBatch(socket: ExSocketInterface, payload: SubMessage): void {
socket.batchedMessages.addPayload(payload);
@ -88,7 +89,7 @@ export class IoSocketController {
ioConnection() {
this.app.ws('/room/*', {
this.app.ws('/room', {
/* Options */
//compression: uWS.SHARED_COMPRESSOR,
maxPayloadLength: 16 * 1024 * 1024,
@ -112,7 +113,12 @@ export class IoSocketController {
const websocketProtocol = req.getHeader('sec-websocket-protocol');
const websocketExtensions = req.getHeader('sec-websocket-extensions');
const roomId = req.getUrl().substr(6);
const roomId = query.roomId;
//todo: better validation: /\/_\/.*\/.*/ or /\/@\/.*\/.*\/.*/
if (typeof roomId !== 'string') {
throw new Error('Undefined room ID: ');
}
const roomIdentifier = new RoomIdentifier(roomId);
const token = query.token;
const x = Number(query.x);
@ -140,12 +146,14 @@ export class IoSocketController {
const userUuid = await jwtTokenManager.getUserUuidFromToken(token);
console.log('uuid', userUuid);
const isGranted = await adminApi.memberIsGrantedAccessToRoom(userUuid, roomId);
if (!isGranted) {
console.log('access not granted for user '+userUuid+' and room '+roomId);
throw new Error('Client cannot acces this ressource.')
} else {
console.log('access granted for user '+userUuid+' and room '+roomId);
if (roomIdentifier.anonymous === false) {
const isGranted = await adminApi.memberIsGrantedAccessToRoom(userUuid, roomIdentifier);
if (!isGranted) {
console.log('access not granted for user '+userUuid+' and room '+roomId);
throw new Error('Client cannot acces this ressource.')
} else {
console.log('access granted for user '+userUuid+' and room '+roomId);
}
}
if (upgradeAborted.aborted) {

View file

@ -0,0 +1,14 @@
export class RoomIdentifier {
public anonymous: boolean;
public id:string
constructor(roomID: string) {
if (roomID.indexOf('_/') === 0) {
this.anonymous = true;
} else if(roomID.indexOf('@/') === 0) {
this.anonymous = false;
} else {
throw new Error('Incorrect room ID: '+roomID);
}
this.id = roomID; //todo: extract more data from the id (like room slug, organization name, etc);
}
}

View file

@ -1,5 +1,6 @@
import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable";
import Axios, {AxiosError} from "axios";
import Axios from "axios";
import {RoomIdentifier} from "../Model/RoomIdentifier";
export interface AdminApiData {
organizationSlug: string
@ -22,13 +23,14 @@ class AdminApi {
return res.data;
}
async memberIsGrantedAccessToRoom(memberId: string, roomId: string): Promise<boolean> {
async memberIsGrantedAccessToRoom(memberId: string, roomIdentifier: RoomIdentifier): Promise<boolean> {
if (!ADMIN_API_URL) {
return Promise.reject('No admin backoffice set!');
}
try {
//todo: send more specialized data instead of the whole id
const res = await Axios.get(ADMIN_API_URL+'/api/member/is-granted-access',
{ headers: {"Authorization" : `${ADMIN_API_TOKEN}`}, params: {memberId, roomIdentifier: roomId} }
{ headers: {"Authorization" : `${ADMIN_API_TOKEN}`}, params: {memberId, roomIdentifier: roomIdentifier.id} }
)
return !!res.data;
} catch (e) {