Skip to content

Commit

Permalink
Merge pull request #1745 from gettakaro/main-promotion
Browse files Browse the repository at this point in the history
  • Loading branch information
niekcandaele authored Nov 1, 2024
2 parents b69ff46 + 92b2693 commit 247d78f
Show file tree
Hide file tree
Showing 63 changed files with 2,537 additions and 1,045 deletions.
328 changes: 182 additions & 146 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"@types/chai-as-promised": "8.0.1",
"@types/eslint__js": "8.42.3",
"@types/expect": "24.3.0",
"@types/express": "4.17.21",
"@types/express": "5.0.0",
"@types/fs-extra": "11.0.4",
"@types/he": "1.2.3",
"@types/jest": "29.5.13",
Expand All @@ -63,7 +63,7 @@
"@types/safe-regex": "1.1.6",
"@types/sinon": "17.0.3",
"@types/sinon-chai": "3.2.12",
"@types/supertest": "2.0.16",
"@types/supertest": "6.0.2",
"@types/uuid": "10.0.0",
"chai": "4.5.0",
"chai-as-promised": "8.0.0",
Expand Down
27 changes: 24 additions & 3 deletions packages/app-api/src/controllers/BanController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ITakaroQuery } from '@takaro/db';
import { APIOutput, apiResponse } from '@takaro/http';
import { BanCreateDTO, BanOutputDTO, BanUpdateDTO } from '../service/Ban/dto.js';
import { AuthenticatedRequest, AuthService } from '../service/AuthService.js';
import { Body, Get, Post, JsonController, UseBefore, Req, Params, Res, Put } from 'routing-controllers';
import { Body, Get, Post, JsonController, UseBefore, Req, Params, Res, Put, Delete } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { ParamId } from '../lib/validators.js';
import { PERMISSIONS } from '@takaro/auth';
Expand Down Expand Up @@ -62,11 +62,16 @@ export class BanSearchInputDTO extends ITakaroQuery<BanOutputDTO> {

@OpenAPI({
security: [{ domainAuth: [] }],
tags: ['Player'],
})
@JsonController()
@JsonController('/player')
export class BanController {
@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_PLAYERS]))
@ResponseSchema(BanOutputArrayDTOAPI)
@OpenAPI({
description: 'Search for bans',
summary: 'Search for bans',
})
@Post('/ban/search')
async search(@Req() req: AuthenticatedRequest, @Res() res: Response, @Body() query: BanSearchInputDTO) {
const service = new BanService(req.domainId);
Expand All @@ -84,6 +89,10 @@ export class BanController {

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_PLAYERS]))
@ResponseSchema(BanOutputDTOAPI)
@OpenAPI({
description: 'Get a single ban',
summary: 'Get a single ban',
})
@Get('/ban/:id')
async getOne(@Req() req: AuthenticatedRequest, @Params() params: ParamId) {
const service = new BanService(req.domainId);
Expand All @@ -92,6 +101,10 @@ export class BanController {

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_PLAYERS]))
@ResponseSchema(BanOutputDTOAPI)
@OpenAPI({
description: 'Create a new ban, creating a ban via the API will always make it takaro managed.',
summary: 'Ban player',
})
@Post('/ban')
async create(@Req() req: AuthenticatedRequest, @Body() data: BanCreateDTO) {
const service = new BanService(req.domainId);
Expand All @@ -102,6 +115,10 @@ export class BanController {

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_PLAYERS]))
@ResponseSchema(BanOutputDTOAPI)
@OpenAPI({
description: 'Update an existing ban, updating a ban via the API will always make it takaro managed.',
summary: 'Update ban',
})
@Put('/ban/:id')
async update(@Req() req: AuthenticatedRequest, @Params() params: ParamId, @Body() data: BanUpdateDTO) {
const service = new BanService(req.domainId);
Expand All @@ -112,7 +129,11 @@ export class BanController {

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_PLAYERS]))
@ResponseSchema(APIOutput)
@Post('/ban/:id/delete')
@OpenAPI({
description: 'Unban player. This will remove the ban from Takaro and the gameserver(s)',
summary: 'Unban player',
})
@Delete('/ban/:id')
async delete(@Req() req: AuthenticatedRequest, @Params() params: ParamId) {
const service = new BanService(req.domainId);
await service.delete(params.id);
Expand Down
3 changes: 3 additions & 0 deletions packages/app-api/src/controllers/GameServerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ export class GameServerController {
@ResponseSchema(APIOutput)
@OpenAPI({
description: 'Ban a player from a gameserver. Requires gameserver to be online and reachable.',
deprecated: true,
})
@Post('/gameserver/:gameServerId/player/:playerId/ban')
async banPlayer(@Req() req: AuthenticatedRequest, @Params() params: PogParam, @Body() data: BanPlayerInputDTO) {
Expand All @@ -473,6 +474,7 @@ export class GameServerController {
@ResponseSchema(APIOutput)
@OpenAPI({
description: 'Unban a player from a gameserver. Requires gameserver to be online and reachable.',
deprecated: true,
})
@Post('/gameserver/:gameServerId/player/:playerId/unban')
async unbanPlayer(@Req() req: AuthenticatedRequest, @Params() params: PogParam) {
Expand All @@ -485,6 +487,7 @@ export class GameServerController {
@ResponseSchema(BanPlayerOutputDTO)
@OpenAPI({
description: 'List bans for a gameserver. Requires gameserver to be online and reachable.',
deprecated: true,
})
@Get('/gameserver/:id/bans')
async listBans(@Req() req: AuthenticatedRequest, @Params() params: ParamId) {
Expand Down
2 changes: 1 addition & 1 deletion packages/app-api/src/controllers/UserController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export class UserController {
}

@Get('/me')
@UseBefore(AuthService.getAuthMiddleware([]))
@UseBefore(AuthService.getAuthMiddleware([], false))
@ResponseSchema(MeOutoutDTOAPI)
@OpenAPI({
summary: 'Get the current logged in user',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ const tests = [

const players = (await this.client.player.playerControllerSearch()).data.data;
expect(players).to.have.length(7);

/* const listings = (await this.client.shopListing.shopListingControllerSearch()).data.data;
expect(listings).to.have.length(4);
expect(listings[0]).to.have.property('quality', null); */
},
}),
new IntegrationTest({
Expand Down
2 changes: 1 addition & 1 deletion packages/app-api/src/domainInit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ async function main() {
const results = await Promise.allSettled(domains.results.map(ctx.wrap('domainInit', domainInit)));
const rejected = results.map((r) => (r.status === 'rejected' ? r.reason : null)).filter(Boolean);
if (rejected.length) {
log.error('Failed to initialize some domains', { errors: rejected });
log.error('Failed to initialize some domains', { errors: JSON.stringify(rejected) });
}

process.exit(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,22 @@ describe('adminAuth', () => {
});

it('Rejects requests with no credentials', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const response = await supertest(http.expressInstance).get('/test');
expect(response.status).to.be.equal(401);
await supertest(http.expressInstance).get('/test').expect(401);
});

it('Accepts requests with valid credentials', async () => {
const response = await supertest(http.expressInstance)
// @ts-expect-error Supertest typings are wrong
.get('/test')
// @ts-expect-error Supertest typings are wrong
.set('X-Takaro-Admin-Token', config.get('adminClientSecret'));

expect(response.status).to.be.equal(200);
});

it('Rejects requests with invalid credentials', async () => {
const response = await supertest(http.expressInstance)
// @ts-expect-error Supertest typings are wrong
.get('/test')
// @ts-expect-error Supertest typings are wrong
.set('X-Takaro-Admin-Token', 'foobar');
expect(response.status).to.be.equal(403);
});
Expand Down
18 changes: 16 additions & 2 deletions packages/app-api/src/service/AuthService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Routes, RESTGetAPICurrentUserGuildsResult } from 'discord-api-types/v10
import oauth from 'passport-oauth2';
import { DiscordService } from './DiscordService.js';
import { domainStateMiddleware } from '../middlewares/domainStateMiddleware.js';
import { DomainService } from './DomainService.js';
import { DOMAIN_STATES, DomainService } from './DomainService.js';

interface DiscordUserInfo {
id: string;
Expand Down Expand Up @@ -193,7 +193,15 @@ export class AuthService extends DomainScoped {
log.warn(`No domain found for identity ${identity.id}`);
throw new errors.UnauthorizedError();
}
domainId = domains[0].id;
// Find the first active domain
domainId = domains.find((d) => d.state === DOMAIN_STATES.ACTIVE)?.id;

if (!domainId && domains.length) {
log.warn(
`No active domain found for identity (but domains found: ${domains.map((d) => ({ id: d.id, state: d.state })).join(',')})`,
);
throw new errors.BadRequestError('Domain is disabled. Please contact support.');
}

// Set the domain cookie
if (req.res?.cookie)
Expand All @@ -215,6 +223,9 @@ export class AuthService extends DomainScoped {
} catch (error) {
// Not an ory session, throw a sanitized error
log.warn(error);
// If we explicitly throw a BadRequestError, we want to pass it through
// So the client gets a meaningful error message
if (error instanceof errors.BadRequestError) throw error;
throw new errors.UnauthorizedError();
}
}
Expand Down Expand Up @@ -250,6 +261,9 @@ export class AuthService extends DomainScoped {
if (domainStateCheck) return domainStateMiddleware(req, _res, next);
return next();
} catch (error) {
// If we explicitly throw a BadRequestError, we want to pass it through
// So the client gets a meaningful error message
if (error instanceof errors.BadRequestError) return next(error);
log.error('Unexpected error in auth middleware', error);
return next(new errors.ForbiddenError());
}
Expand Down
4 changes: 3 additions & 1 deletion packages/app-api/src/service/Ban/dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export class BanOutputDTO extends TakaroModelDTO<BanOutputDTO> {

export class BanCreateDTO extends TakaroDTO<BanCreateDTO> {
@IsUUID('4')
gameServerId: string;
@IsOptional()
gameServerId?: string;
@IsUUID('4')
playerId: string;
@IsBoolean()
Expand All @@ -38,6 +39,7 @@ export class BanCreateDTO extends TakaroDTO<BanCreateDTO> {
@IsOptional()
reason?: string;
}

export class BanUpdateDTO extends TakaroDTO<BanUpdateDTO> {
@IsUUID('4')
gameServerId: string;
Expand Down
6 changes: 4 additions & 2 deletions packages/app-api/src/service/Ban/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ export class BanService extends TakaroService<BanModel, BanOutputDTO, BanCreateD
// Some game servers still need a date, so we default to 1000 years from now
const until = item.until || '3021-01-01T00:00:00.000Z';

await gameServerService.banPlayer(item.gameServerId, item.playerId, reason, until);
if (item.gameServerId) {
await gameServerService.banPlayer(item.gameServerId, item.playerId, reason, until);
}

if (item.isGlobal) {
const allGameservers = await gameServerService.find({});
Expand Down Expand Up @@ -84,7 +86,7 @@ export class BanService extends TakaroService<BanModel, BanOutputDTO, BanCreateD
const gameServerService = new GameServerService(this.domainId);
const { player, pogs } = await playerService.resolveFromId(playerId);

await Promise.allSettled(
await Promise.all(
pogs.map(async (pog) => {
return gameServerService.banPlayer(pog.gameServerId, player.id, ban.reason, ban.until);
}),
Expand Down
Loading

0 comments on commit 247d78f

Please sign in to comment.