Merge branch 'develop' into changeRegisterAccess
Signed-off-by: Gregoire Parant <g.parant@thecodingmachine.com> # Conflicts: # pusher/src/Services/AdminApi.ts
This commit is contained in:
@@ -1,26 +1,34 @@
|
||||
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 "../Messages/JsonMessages/CharacterTexture";
|
||||
import Axios, { AxiosResponse } from "axios";
|
||||
import { MapDetailsData } from "../Messages/JsonMessages/MapDetailsData";
|
||||
import { RoomRedirect } from "../Messages/JsonMessages/RoomRedirect";
|
||||
import { AdminApiData, isAdminApiData } from "../Messages/JsonMessages/AdminApiData";
|
||||
import * as tg from "generic-type-guard";
|
||||
import { isNumber } from "generic-type-guard";
|
||||
import { isWokaDetail } from "../Enum/PlayerTextures";
|
||||
import qs from "qs";
|
||||
|
||||
export interface AdminBannedData {
|
||||
is_banned: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface FetchMemberDataByUuidResponse {
|
||||
email: string;
|
||||
userUuid: string;
|
||||
tags: string[];
|
||||
visitCardUrl: string | null;
|
||||
textures: CharacterTexture[];
|
||||
messages: unknown[];
|
||||
anonymous?: boolean;
|
||||
userRoomToken: string | undefined;
|
||||
}
|
||||
const isFetchMemberDataByUuidResponse = new tg.IsInterface()
|
||||
.withProperties({
|
||||
email: tg.isString,
|
||||
userUuid: tg.isString,
|
||||
tags: tg.isArray(tg.isString),
|
||||
visitCardUrl: tg.isNullable(tg.isString),
|
||||
textures: tg.isArray(isWokaDetail),
|
||||
messages: tg.isArray(tg.isUnknown),
|
||||
})
|
||||
.withOptionalProperties({
|
||||
anonymous: tg.isBoolean,
|
||||
userRoomToken: tg.isString,
|
||||
})
|
||||
.get();
|
||||
|
||||
export type FetchMemberDataByUuidResponse = tg.GuardedType<typeof isFetchMemberDataByUuidResponse>;
|
||||
|
||||
class AdminApi {
|
||||
/**
|
||||
@@ -48,20 +56,30 @@ class AdminApi {
|
||||
async fetchMemberDataByUuid(
|
||||
userIdentifier: string | null,
|
||||
playUri: string,
|
||||
ipAddress: string
|
||||
ipAddress: string,
|
||||
characterLayers: string[]
|
||||
): Promise<FetchMemberDataByUuidResponse> {
|
||||
if (!ADMIN_API_URL) {
|
||||
return Promise.reject(new Error("No admin backoffice set!"));
|
||||
}
|
||||
const res = await Axios.get(ADMIN_API_URL + "/api/room/access", {
|
||||
const res = await Axios.get<unknown, AxiosResponse<unknown>>(ADMIN_API_URL + "/api/room/access", {
|
||||
params: {
|
||||
userIdentifier,
|
||||
roomId: playUri /* @deprecated */,
|
||||
playUri,
|
||||
roomId: playUri,
|
||||
ipAddress,
|
||||
characterLayers,
|
||||
},
|
||||
headers: { Authorization: `${ADMIN_API_TOKEN}` },
|
||||
paramsSerializer: (p) => {
|
||||
return qs.stringify(p, { arrayFormat: "brackets" });
|
||||
},
|
||||
});
|
||||
if (!isFetchMemberDataByUuidResponse(res.data)) {
|
||||
throw new Error(
|
||||
"Invalid answer received from the admin for the /api/room/access endpoint. Received: " +
|
||||
JSON.stringify(res.data)
|
||||
);
|
||||
}
|
||||
return res.data;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
import { ADMIN_API_TOKEN, ADMIN_API_URL } from "../Enum/EnvironmentVariable";
|
||||
import { wokaList, WokaList } from "../Enum/PlayerTextures";
|
||||
import { WokaServiceInterface } from "./WokaServiceInterface";
|
||||
|
||||
class AdminWokaService implements WokaServiceInterface {
|
||||
/**
|
||||
* Returns the list of all available Wokas for the current user.
|
||||
*/
|
||||
getWokaList(roomUrl: string, token: string): Promise<WokaList | undefined> {
|
||||
return axios
|
||||
.get<unknown, AxiosResponse<unknown>>(`${ADMIN_API_URL}/api/woka/list`, {
|
||||
headers: { Authorization: `${ADMIN_API_TOKEN}` },
|
||||
params: {
|
||||
roomUrl,
|
||||
uuid: token,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
return wokaList.parse(res.data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(`Cannot get woka list from admin API with token: ${token}`, err);
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const adminWokaService = new AdminWokaService();
|
||||
@@ -5,6 +5,8 @@ import { InvalidTokenError } from "../Controller/InvalidTokenError";
|
||||
export interface AuthTokenData {
|
||||
identifier: string; //will be a email if logged in or an uuid if anonymous
|
||||
accessToken?: string;
|
||||
username?: string;
|
||||
locale?: string;
|
||||
}
|
||||
export interface AdminSocketTokenData {
|
||||
authorizedRoomIds: string[]; //the list of rooms the client is authorized to read from.
|
||||
@@ -16,8 +18,8 @@ class JWTTokenManager {
|
||||
return Jwt.verify(token, ADMIN_SOCKETS_TOKEN) as AdminSocketTokenData;
|
||||
}
|
||||
|
||||
public createAuthToken(identifier: string, accessToken?: string) {
|
||||
return Jwt.sign({ identifier, accessToken }, SECRET_KEY, { expiresIn: "30d" });
|
||||
public createAuthToken(identifier: string, accessToken?: string, username?: string, locale?: string) {
|
||||
return Jwt.sign({ identifier, accessToken, username, locale }, SECRET_KEY, { expiresIn: "30d" });
|
||||
}
|
||||
|
||||
public verifyJWTToken(token: string, ignoreExpiration: boolean = false): AuthTokenData {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import { WokaDetail, WokaDetailsResult, WokaList, wokaPartNames } from "../Enum/PlayerTextures";
|
||||
import { WokaServiceInterface } from "./WokaServiceInterface";
|
||||
|
||||
class LocalWokaService implements WokaServiceInterface {
|
||||
/**
|
||||
* Returns the list of all available Wokas & Woka Parts for the current user.
|
||||
*/
|
||||
async getWokaList(roomId: string, token: string): Promise<WokaList | undefined> {
|
||||
const wokaData: WokaList = await require("../../data/woka.json");
|
||||
if (!wokaData) {
|
||||
return undefined;
|
||||
}
|
||||
return wokaData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URL of all the images for the given texture ids.
|
||||
*
|
||||
* Key: texture id
|
||||
* Value: URL
|
||||
*
|
||||
* If one of the textures cannot be found, undefined is returned (and the user should be redirected to Woka choice page!)
|
||||
*/
|
||||
async fetchWokaDetails(textureIds: string[]): Promise<WokaDetailsResult | undefined> {
|
||||
const wokaData: WokaList = await require("../../data/woka.json");
|
||||
const textures = new Map<
|
||||
string,
|
||||
{
|
||||
url: string;
|
||||
layer: string;
|
||||
}
|
||||
>();
|
||||
const searchIds = new Set(textureIds);
|
||||
|
||||
for (const part of wokaPartNames) {
|
||||
const wokaPartType = wokaData[part];
|
||||
if (!wokaPartType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const collection of wokaPartType.collections) {
|
||||
for (const id of searchIds) {
|
||||
const texture = collection.textures.find((texture) => texture.id === id);
|
||||
|
||||
if (texture) {
|
||||
textures.set(id, {
|
||||
url: texture.url,
|
||||
layer: part,
|
||||
});
|
||||
searchIds.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (textureIds.length !== textures.size) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const details: WokaDetail[] = [];
|
||||
|
||||
textures.forEach((value, key) => {
|
||||
details.push({
|
||||
id: key,
|
||||
url: value.url,
|
||||
layer: value.layer,
|
||||
});
|
||||
});
|
||||
|
||||
return details;
|
||||
}
|
||||
}
|
||||
|
||||
export const localWokaService = new LocalWokaService();
|
||||
@@ -4,6 +4,9 @@ import {
|
||||
OPID_CLIENT_SECRET,
|
||||
OPID_CLIENT_ISSUER,
|
||||
OPID_CLIENT_REDIRECT_URL,
|
||||
OPID_USERNAME_CLAIM,
|
||||
OPID_LOCALE_CLAIM,
|
||||
OPID_SCOPE,
|
||||
} from "../Enum/EnvironmentVariable";
|
||||
|
||||
class OpenIDClient {
|
||||
@@ -25,8 +28,11 @@ class OpenIDClient {
|
||||
|
||||
public authorizationUrl(state: string, nonce: string, playUri?: string, redirect?: string) {
|
||||
return this.initClient().then((client) => {
|
||||
if (!OPID_SCOPE.includes("email") || !OPID_SCOPE.includes("openid")) {
|
||||
throw new Error("Invalid scope, 'email' and 'openid' are required in OPID_SCOPE.");
|
||||
}
|
||||
return client.authorizationUrl({
|
||||
scope: "openid email",
|
||||
scope: OPID_SCOPE,
|
||||
prompt: "login",
|
||||
state: state,
|
||||
nonce: nonce,
|
||||
@@ -36,7 +42,10 @@ class OpenIDClient {
|
||||
});
|
||||
}
|
||||
|
||||
public getUserInfo(code: string, nonce: string): Promise<{ email: string; sub: string; access_token: string }> {
|
||||
public getUserInfo(
|
||||
code: string,
|
||||
nonce: string
|
||||
): Promise<{ email: string; sub: string; access_token: string; username: string; locale: string }> {
|
||||
return this.initClient().then((client) => {
|
||||
return client.callback(OPID_CLIENT_REDIRECT_URL, { code }, { nonce }).then((tokenSet) => {
|
||||
return client.userinfo(tokenSet).then((res) => {
|
||||
@@ -45,6 +54,8 @@ class OpenIDClient {
|
||||
email: res.email as string,
|
||||
sub: res.sub,
|
||||
access_token: tokenSet.access_token as string,
|
||||
username: res[OPID_USERNAME_CLAIM] as string,
|
||||
locale: res[OPID_LOCALE_CLAIM] as string,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PusherRoom } from "../Model/PusherRoom";
|
||||
import { CharacterLayer, ExSocketInterface } from "../Model/Websocket/ExSocketInterface";
|
||||
import { ExSocketInterface } from "../Model/Websocket/ExSocketInterface";
|
||||
import {
|
||||
AdminMessage,
|
||||
AdminPusherToBackMessage,
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
ErrorMessage,
|
||||
WorldFullMessage,
|
||||
PlayerDetailsUpdatedMessage,
|
||||
InvalidTextureMessage,
|
||||
} from "../Messages/generated/messages_pb";
|
||||
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
|
||||
import { ADMIN_API_URL, JITSI_ISS, JITSI_URL, SECRET_JITSI_KEY } from "../Enum/EnvironmentVariable";
|
||||
@@ -52,7 +53,8 @@ import Debug from "debug";
|
||||
import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface";
|
||||
import { WebSocket } from "uWebSockets.js";
|
||||
import { isRoomRedirect } from "../Messages/JsonMessages/RoomRedirect";
|
||||
import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture";
|
||||
//import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture";
|
||||
import { compressors } from "hyper-express";
|
||||
|
||||
const debug = Debug("socket");
|
||||
|
||||
@@ -174,10 +176,13 @@ export class SocketManager implements ZoneEventListener {
|
||||
|
||||
for (const characterLayer of client.characterLayers) {
|
||||
const characterLayerMessage = new CharacterLayerMessage();
|
||||
characterLayerMessage.setName(characterLayer.name);
|
||||
characterLayerMessage.setName(characterLayer.id);
|
||||
if (characterLayer.url !== undefined) {
|
||||
characterLayerMessage.setUrl(characterLayer.url);
|
||||
}
|
||||
if (characterLayer.layer !== undefined) {
|
||||
characterLayerMessage.setLayer(characterLayer.layer);
|
||||
}
|
||||
|
||||
joinRoomMessage.addCharacterlayer(characterLayerMessage);
|
||||
}
|
||||
@@ -544,36 +549,6 @@ export class SocketManager implements ZoneEventListener {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the characterLayers received from the front (as an array of string) with the custom textures from the back.
|
||||
*/
|
||||
static mergeCharacterLayersAndCustomTextures(
|
||||
characterLayers: string[],
|
||||
memberTextures: CharacterTexture[]
|
||||
): CharacterLayer[] {
|
||||
const characterLayerObjs: CharacterLayer[] = [];
|
||||
for (const characterLayer of characterLayers) {
|
||||
if (characterLayer.startsWith("customCharacterTexture")) {
|
||||
const customCharacterLayerId: number = +characterLayer.substr(22);
|
||||
for (const memberTexture of memberTextures) {
|
||||
if (memberTexture.id == customCharacterLayerId) {
|
||||
characterLayerObjs.push({
|
||||
name: characterLayer,
|
||||
url: memberTexture.url,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
characterLayerObjs.push({
|
||||
name: characterLayer,
|
||||
url: undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
return characterLayerObjs;
|
||||
}
|
||||
|
||||
public onUserEnters(user: UserDescriptor, listener: ExSocketInterface): void {
|
||||
const subMessage = new SubMessage();
|
||||
subMessage.setUserjoinedmessage(user.toUserJoinedMessage());
|
||||
@@ -619,7 +594,7 @@ export class SocketManager implements ZoneEventListener {
|
||||
emitInBatch(listener, subMessage);
|
||||
}
|
||||
|
||||
public emitWorldFullMessage(client: WebSocket) {
|
||||
public emitWorldFullMessage(client: compressors.WebSocket) {
|
||||
const errorMessage = new WorldFullMessage();
|
||||
|
||||
const serverToClientMessage = new ServerToClientMessage();
|
||||
@@ -630,7 +605,7 @@ export class SocketManager implements ZoneEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
public emitTokenExpiredMessage(client: WebSocket) {
|
||||
public emitTokenExpiredMessage(client: compressors.WebSocket) {
|
||||
const errorMessage = new TokenExpiredMessage();
|
||||
|
||||
const serverToClientMessage = new ServerToClientMessage();
|
||||
@@ -641,7 +616,18 @@ export class SocketManager implements ZoneEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
public emitConnexionErrorMessage(client: WebSocket, message: string) {
|
||||
public emitInvalidTextureMessage(client: compressors.WebSocket) {
|
||||
const errorMessage = new InvalidTextureMessage();
|
||||
|
||||
const serverToClientMessage = new ServerToClientMessage();
|
||||
serverToClientMessage.setInvalidtexturemessage(errorMessage);
|
||||
|
||||
if (!client.disconnecting) {
|
||||
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
||||
}
|
||||
}
|
||||
|
||||
public emitConnexionErrorMessage(client: compressors.WebSocket, message: string) {
|
||||
const errorMessage = new WorldConnexionMessage();
|
||||
errorMessage.setMessage(message);
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { ADMIN_API_URL } from "../Enum/EnvironmentVariable";
|
||||
import { adminWokaService } from "./AdminWokaService";
|
||||
import { localWokaService } from "./LocalWokaService";
|
||||
|
||||
export const wokaService = ADMIN_API_URL ? adminWokaService : localWokaService;
|
||||
@@ -0,0 +1,8 @@
|
||||
import { WokaDetailsResult, WokaList } from "../Enum/PlayerTextures";
|
||||
|
||||
export interface WokaServiceInterface {
|
||||
/**
|
||||
* Returns the list of all available Wokas for the current user.
|
||||
*/
|
||||
getWokaList(roomId: string, token: string): Promise<WokaList | undefined>;
|
||||
}
|
||||
Reference in New Issue
Block a user