Skip to content

Commit

Permalink
Add option to disable match creation for unauthenticated users
Browse files Browse the repository at this point in the history
  • Loading branch information
pixup1 committed Dec 19, 2024
1 parent aa7f7b2 commit 5bd85cf
Show file tree
Hide file tree
Showing 21 changed files with 576 additions and 200 deletions.
37 changes: 26 additions & 11 deletions backend/src/auth.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
import { Request } from 'express';
import { IAuthResponse, IAuthResponseOptional } from '../../common'
import { generate as shortUuid } from 'short-uuid';
import * as MatchService from './matchService';
import * as Storage from './storage';

const tokens: Map<string, ITokenContent> = new Map();

export interface IAuthResponse {
type: 'GLOBAL' | 'MATCH';
comment?: string;
}

export type IAuthResponseOptional =
| IAuthResponse
| {
type: 'UNAUTHORIZED';
};

export type ExpressRequest<T extends IAuthResponse | IAuthResponseOptional> = Request & {
user: T;
};
Expand Down Expand Up @@ -127,3 +117,28 @@ export const isAuthorized = async (

return false;
};

export const getTokenType = async (
token: string,
): Promise<IAuthResponseOptional> => {
const t = getGlobalToken(token);
if (t) {
return {
type: 'GLOBAL',
comment: t.comment,
};
}

const allMatches = await MatchService.getAll();
for (const match of allMatches.live.concat(allMatches.notLive)) {
if (match.tmtSecret === token) {
return {
type: 'MATCH'
};
}
}

return {
type: 'UNAUTHORIZED',
}
}
21 changes: 21 additions & 0 deletions backend/src/authController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Controller, Get, NoSecurity, Route, Request, Security, Post } from '@tsoa/runtime';
import { IAuthResponseOptional } from '../../common';
import * as Auth from './auth';

@Route('/api/auth')
export class AuthController extends Controller {
/**
* Get the authentication type for a token.
*/
@Get()
@NoSecurity()
async getAuthType(
@Request() req: Auth.ExpressRequest<IAuthResponseOptional>,
): Promise<IAuthResponseOptional> {
const token = req.get('authorization');
if (!token) {
return { type: 'UNAUTHORIZED' };
}
return Auth.getTokenType(token);
}
}
68 changes: 68 additions & 0 deletions backend/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { IConfig, IConfigUpdateDto } from "../../common";
import { checkAndNormalizeLogAddress } from "./match";
import * as Storage from './storage';

const FILE_NAME = 'config.json';

// These are not defaults, they're temporary until setup() is called
let config: IConfig = {
tmtLogAddress: null,
allowUnregisteredMatchCreation: false
};

const defaults = (): IConfig => {
let tmtLogAdress = null;
if (!process.env['TMT_LOG_ADDRESS']) {
console.warn('Environment variable TMT_LOG_ADDRESS is not set');
console.warn('Every match must be init with tmtLogAddress');
} else {
tmtLogAdress = checkAndNormalizeLogAddress(process.env['TMT_LOG_ADDRESS']);
if (!tmtLogAdress) {
throw 'invalid environment variable: TMT_LOG_ADDRESS';
}
}

return {
tmtLogAddress: tmtLogAdress,
allowUnregisteredMatchCreation: false
}
};

export const get = async (): Promise<IConfig> => {
return config;
}

export const set = async (data: IConfigUpdateDto) => {
let tmtLogAdress = null;
if ('tmtLogAddress' in data && data.tmtLogAddress) {
tmtLogAdress = checkAndNormalizeLogAddress(data.tmtLogAddress);
if (!tmtLogAdress) {
throw 'invalid tmtLogAddress';
}
} else if ('tmtLogAddress' in data) {
tmtLogAdress = null;
} else {
tmtLogAdress = config.tmtLogAddress;
}

let allowUnregisteredMatchCreation = data.allowUnregisteredMatchCreation ?? config.allowUnregisteredMatchCreation;

config = {
tmtLogAddress: tmtLogAdress,
allowUnregisteredMatchCreation: allowUnregisteredMatchCreation
};

await write();

return config;
};

const write = async () => {
await Storage.write(FILE_NAME, config);
};

export const setup = async () => {
// Only get the defaults if the config does not already exist
const data = await Storage.read(FILE_NAME, defaults());
set(data)
};
22 changes: 15 additions & 7 deletions backend/src/configController.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
import { Controller, Get, NoSecurity, Route, Security } from '@tsoa/runtime';
import { TMT_LOG_ADDRESS } from '.';
import { IConfig } from '../../common';
import { Body, Controller, Get, NoSecurity, Patch, Route, Security } from '@tsoa/runtime';
import { IConfig, IConfigUpdateDto } from '../../common';
import * as Config from './config';

@Route('/api/config')
@Security('bearer_token')
export class ConfigController extends Controller {
/**
* Get some internal config variables. Currently only the set TMT_LOG_ADDRESS.
* Get some internal config variables.
*/
@Get()
@NoSecurity()
async getConfig(): Promise<IConfig> {
return {
tmtLogAddress: TMT_LOG_ADDRESS,
};
return Config.get();
}

/**
* Update the configuration.
*/
@Patch()
async updateConfig(
@Body() requestBody: IConfigUpdateDto,
): Promise<IConfig> {
return await Config.set(requestBody);
}
}
5 changes: 3 additions & 2 deletions backend/src/debugController.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Controller, Get, Route, Security } from '@tsoa/runtime';
import { COMMIT_SHA, IMAGE_BUILD_TIMESTAMP, PORT, TMT_LOG_ADDRESS, VERSION } from '.';
import { COMMIT_SHA, IMAGE_BUILD_TIMESTAMP, PORT, VERSION } from '.';
import { IDebugResponse } from '../../common';
import { Settings } from './settings';
import { STORAGE_FOLDER } from './storage';
import * as Config from './config';
import * as WebSocket from './webSocket';

@Route('/api/debug')
Expand All @@ -24,7 +25,7 @@ export class DebugController extends Controller {
tmtImageBuildTimestamp: IMAGE_BUILD_TIMESTAMP,
tmtStorageFolder: STORAGE_FOLDER,
tmtPort: PORT,
tmtLogAddress: TMT_LOG_ADDRESS,
tmtLogAddress: (await Config.get()).tmtLogAddress,
tmtSayPrefix: Settings.SAY_PREFIX,
webSockets: WebSocket.getClients(),
};
Expand Down
16 changes: 2 additions & 14 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,17 @@ import { existsSync, readFileSync } from 'fs';
import http from 'http';
import path from 'path';
import * as Auth from './auth';
import * as Config from './config';
import * as Election from './election';
import * as ManagedGameServers from './managedGameServers';
import * as Match from './match';
import { checkAndNormalizeLogAddress } from './match';
import * as MatchMap from './matchMap';
import * as MatchService from './matchService';
import * as Presets from './presets';
import { RegisterRoutes } from './routes';
import * as Storage from './storage';
import * as WebSocket from './webSocket';

export const TMT_LOG_ADDRESS: string | null = (() => {
if (!process.env['TMT_LOG_ADDRESS']) {
console.warn('Environment variable TMT_LOG_ADDRESS is not set');
console.warn('Every match must be init with tmtLogAddress');
return null;
}
const addr = checkAndNormalizeLogAddress(process.env['TMT_LOG_ADDRESS']);
if (!addr) {
throw 'invalid environment variable: TMT_LOG_ADDRESS';
}
return addr;
})();

const APP_DIR = (() => {
if (__dirname.endsWith(path.join('/backend/dist/backend/src'))) {
// in production: __dirname = /app/backend/dist/backend/src
Expand Down Expand Up @@ -128,6 +115,7 @@ const main = async () => {
await Auth.setup();
await WebSocket.setup(httpServer);
await ManagedGameServers.setup();
await Config.setup();
await Presets.setup();
Match.registerCommandHandlers();
MatchMap.registerCommandHandlers();
Expand Down
11 changes: 0 additions & 11 deletions backend/src/loginController.ts

This file was deleted.

7 changes: 4 additions & 3 deletions backend/src/match.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ValidateError } from '@tsoa/runtime';
import { generate as shortUuid } from 'short-uuid';
import { COMMIT_SHA, IMAGE_BUILD_TIMESTAMP, TMT_LOG_ADDRESS, VERSION } from '.';
import { COMMIT_SHA, IMAGE_BUILD_TIMESTAMP, VERSION } from '.';
import {
IMatch,
IMatchCreateDto,
Expand All @@ -17,6 +17,7 @@ import {
} from '../../common';
import { addChangeListener } from './changeListener';
import * as commands from './commands';
import * as Config from './config';
import * as Election from './election';
import * as Events from './events';
import * as GameServer from './gameServer';
Expand Down Expand Up @@ -66,7 +67,7 @@ export const createFromData = async (data: IMatch, logMessage?: string) => {
throw 'invalid tmtLogAddress';
}
match.data.tmtLogAddress = la;
} else if (!TMT_LOG_ADDRESS) {
} else if ((await Config.get()).tmtLogAddress) {
throw 'tmtLogAddress must be set';
}

Expand Down Expand Up @@ -254,7 +255,7 @@ export const checkAndNormalizeLogAddress = (url: string): string | null => {
};

const ensureLogAddressIsRegistered = async (match: Match) => {
const logAddress = `${match.data.tmtLogAddress || TMT_LOG_ADDRESS}/api/matches/${
const logAddress = `${match.data.tmtLogAddress || (await Config.get()).tmtLogAddress}/api/matches/${
match.data.id
}/server/log/${match.data.logSecret}`;

Expand Down
12 changes: 10 additions & 2 deletions backend/src/matchesController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import {
IMatchResponse,
IMatchUpdateDto,
} from '../../common';
import { ExpressRequest, IAuthResponse, IAuthResponseOptional } from './auth';
import { ExpressRequest, isAuthorized } from './auth';
import { IAuthResponse, IAuthResponseOptional } from '../../common';
import * as Config from './config';
import * as Events from './events';
import * as Match from './match';
import * as MatchMap from './matchMap';
Expand Down Expand Up @@ -58,7 +60,13 @@ export class MatchesController extends Controller {
async createMatch(
@Body() requestBody: IMatchCreateDto,
@Request() req: ExpressRequest<IAuthResponseOptional>
): Promise<IMatch> {
): Promise<IMatch | void> {
if ((await Config.get()).allowUnregisteredMatchCreation === false) {
if (!isAuthorized(req.get('authorization'))) {
this.setStatus(401);
return;
}
}
if (requestBody.gameServer === null) {
checkRconCommands(requestBody.rconCommands, req.user.type === 'GLOBAL');
}
Expand Down
3 changes: 2 additions & 1 deletion backend/src/presetsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
SuccessResponse,
} from '@tsoa/runtime';
import { IPreset, IPresetCreateDto } from '../../common';
import { ExpressRequest, IAuthResponse } from './auth';
import { ExpressRequest } from './auth';
import { IAuthResponse } from '../../common';
import * as Presets from './presets';

@Route('/api/presets')
Expand Down
Loading

0 comments on commit 5bd85cf

Please sign in to comment.