Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: permissions for shop #1073

Merged
merged 6 commits into from
Aug 17, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions packages/app-api/src/controllers/GameServerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
ModuleInstallationOutputDTO,
ModuleInstallDTO,
} from '../service/GameServerService.js';
import { AuthenticatedRequest, AuthService } from '../service/AuthService.js';
import { AuthenticatedRequest, AuthService, checkPermissions } from '../service/AuthService.js';
import {
Body,
Get,
Expand All @@ -48,6 +48,7 @@ import { IdUuidDTO, IdUuidDTOAPI, ParamId, PogParam } from '../lib/validators.js
import { PERMISSIONS } from '@takaro/auth';
import { Response } from 'express';
import { PlayerOnGameserverOutputDTOAPI } from './PlayerOnGameserverController.js';
import { UserService } from '../service/UserService.js';

class GameServerTypesOutputDTOAPI extends APIOutput<GameServerOutputDTO[]> {
@Type(() => GameServerOutputDTO)
Expand Down Expand Up @@ -210,7 +211,7 @@ class ImportOutputDTOAPI extends APIOutput<ImportOutputDTO> {
})
@JsonController()
export class GameServerController {
@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS]))
@UseBefore(AuthService.getAuthMiddleware([]))
@ResponseSchema(GameServerOutputArrayDTOAPI)
@OpenAPI({
description: 'Fetch gameservers',
Expand All @@ -230,7 +231,7 @@ export class GameServerController {
});
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS]))
@UseBefore(AuthService.getAuthMiddleware([]))
@ResponseSchema(GameServerTypesOutputDTOAPI)
@OpenAPI({
description: 'Fetch gameserver types (7dtd, Rust, ...)',
Expand All @@ -241,15 +242,18 @@ export class GameServerController {
return apiResponse(await service.getTypes());
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS]))
@UseBefore(AuthService.getAuthMiddleware([]))
@ResponseSchema(GameServerOutputDTOAPI)
@OpenAPI({
description: 'Fetch a gameserver by id',
})
@Get('/gameserver/:id')
async getOne(@Req() req: AuthenticatedRequest, @Params() params: ParamId) {
const service = new GameServerService(req.domainId);
return apiResponse(await service.findOne(params.id));
const userService = new UserService(req.domainId);
const user = await userService.findOne(req.user.id);
const hasManageServerPermission = checkPermissions([PERMISSIONS.MANAGE_GAMESERVERS], user);
return apiResponse(await service.findOne(params.id, hasManageServerPermission));
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.MANAGE_GAMESERVERS]))
Expand Down Expand Up @@ -286,7 +290,7 @@ export class GameServerController {
return apiResponse(new IdUuidDTO({ id: params.id }));
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS]))
@UseBefore(AuthService.getAuthMiddleware([]))
@ResponseSchema(GameServerTestReachabilityDTOAPI)
@OpenAPI({
description: 'Test if Takaro can connect to a gameserver. Will do a thorough check and report details.',
Expand All @@ -298,7 +302,7 @@ export class GameServerController {
return apiResponse(res);
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS]))
@UseBefore(AuthService.getAuthMiddleware([]))
@ResponseSchema(GameServerTestReachabilityDTOAPI)
@OpenAPI({
description: 'Test if Takaro can connect to a gameserver. Will do a thorough check and report details.',
Expand All @@ -310,7 +314,7 @@ export class GameServerController {
return apiResponse(res);
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS]))
@UseBefore(AuthService.getAuthMiddleware([]))
@ResponseSchema(ModuleInstallationOutputDTOAPI)
@OpenAPI({
description: 'Get a module installation by id',
Expand All @@ -322,7 +326,7 @@ export class GameServerController {
return apiResponse(res);
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS]))
@UseBefore(AuthService.getAuthMiddleware([]))
@ResponseSchema(ModuleInstallationOutputArrayDTOAPI)
@OpenAPI({
description: 'Get all module installations for a gameserver',
Expand Down
30 changes: 29 additions & 1 deletion packages/app-api/src/controllers/PlayerController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { ParamId } from '../lib/validators.js';
import { PERMISSIONS } from '@takaro/auth';
import { Response } from 'express';
import { ParamIdAndRoleId } from '../lib/validators.js';
import { errors } from '@takaro/util';
import { TakaroDTO, errors } from '@takaro/util';
import { UserService } from '../service/UserService.js';
import { PlayerOnGameserverOutputArrayDTOAPI } from './PlayerOnGameserverController.js';

export class PlayerOutputDTOAPI extends APIOutput<PlayerOutputWithRolesDTO> {
@Type(() => PlayerOutputWithRolesDTO)
Expand Down Expand Up @@ -80,6 +82,15 @@ class PlayerRoleAssignChangeDTO {
expiresAt?: string;
}

class PlayerMeOutputDTO extends TakaroDTO<PlayerMeOutputDTO> {
@ValidateNested()
@Type(() => PlayerOutputWithRolesDTO)
player: PlayerOutputWithRolesDTOAPI;
@ValidateNested({ each: true })
@Type(() => PlayerOnGameserverOutputArrayDTOAPI)
pogs: PlayerOnGameserverOutputArrayDTOAPI[];
}

@OpenAPI({
security: [{ domainAuth: [] }],
})
Expand All @@ -102,6 +113,23 @@ export class PlayerController {
});
}

@UseBefore(AuthService.getAuthMiddleware([]))
@ResponseSchema(PlayerMeOutputDTO)
@OpenAPI({
summary: 'Get current player',
description:
'Get the player that is currently authenticated. This is a low-privilege route, returning limited data.',
})
@Get('/player/me')
async getMe(@Req() req: AuthenticatedRequest) {
const service = new PlayerService(req.domainId);
const userService = new UserService(req.domainId);
const user = await userService.findOne(req.user.id);
if (!user.playerId) throw new errors.NotFoundError('Player not found, please link your player account.');
const res = await service.resolveFromId(user.playerId);
return apiResponse(new PlayerMeOutputDTO(res));
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_PLAYERS]))
@ResponseSchema(PlayerOutputWithRolesDTOAPI)
@Get('/player/:id')
Expand Down
4 changes: 2 additions & 2 deletions packages/app-api/src/controllers/SettingsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class SettingsSetDTO {
})
@JsonController()
export class SettingsController {
@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_SETTINGS]))
@UseBefore(AuthService.getAuthMiddleware([]))
@ResponseSchema(SettingsOutputDTOAPI)
@Get('/settings/:key')
async getOne(
Expand All @@ -82,7 +82,7 @@ export class SettingsController {
return apiResponse(await service.get(params.key));
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_SETTINGS]))
@UseBefore(AuthService.getAuthMiddleware([]))
@ResponseSchema(SettingsOutputArrayDTOAPI)
@Get('/settings')
async get(@Req() req: AuthenticatedRequest, @QueryParams() query: GetSettingsInput) {
Expand Down
12 changes: 6 additions & 6 deletions packages/app-api/src/controllers/StatsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,23 +83,23 @@ export class EventsCountInputDTO extends BaseStatsInputDTO {
})
@JsonController()
export class StatsController {
@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS, PERMISSIONS.READ_PLAYERS]))
@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_PLAYERS]))
@ResponseSchema(StatsOutputDTOAPI)
@Get('/stats/ping')
async getPingStats(@Req() req: AuthenticatedRequest, @QueryParams() query: PogStatsInputDTO) {
const service = new StatsService(req.domainId);
return apiResponse(await service.getPing(query.playerId, query.gameServerId, query.startDate, query.endDate));
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS, PERMISSIONS.READ_PLAYERS]))
@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_PLAYERS]))
@ResponseSchema(StatsOutputDTOAPI)
@Get('/stats/currency')
async getCurrencyStats(@Req() req: AuthenticatedRequest, @QueryParams() query: PogStatsInputDTO) {
const service = new StatsService(req.domainId);
return apiResponse(await service.getCurrency(query.playerId, query.gameServerId, query.startDate, query.endDate));
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS]))
@UseBefore(AuthService.getAuthMiddleware([]))
@ResponseSchema(StatsOutputDTOAPI)
@OpenAPI({
description: 'The roundtrip time for reachability tests between Takaro and the game server',
Expand All @@ -110,15 +110,15 @@ export class StatsController {
return apiResponse(await service.getLatency(query.gameServerId, query.startDate, query.endDate));
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS, PERMISSIONS.READ_PLAYERS]))
@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_PLAYERS]))
@ResponseSchema(StatsOutputDTOAPI)
@Get('/stats/players-online')
async getPlayerOnlineStats(@Req() req: AuthenticatedRequest, @QueryParams() query: PlayersOnlineInputDTO) {
const service = new StatsService(req.domainId);
return apiResponse(await service.getPlayersOnline(query.gameServerId, query.startDate, query.endDate));
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS, PERMISSIONS.READ_PLAYERS]))
@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_PLAYERS]))
@ResponseSchema(StatsOutputDTOAPI)
@Get('/stats/activity')
async getActivityStats(@Req() req: AuthenticatedRequest, @QueryParams() query: ActivityInputDTO) {
Expand All @@ -128,7 +128,7 @@ export class StatsController {
);
}

@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_GAMESERVERS, PERMISSIONS.READ_PLAYERS]))
@UseBefore(AuthService.getAuthMiddleware([PERMISSIONS.READ_PLAYERS]))
@ResponseSchema(StatsOutputDTOAPI)
@OpenAPI({
summary: 'Get event count over time',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const tests = [
name: 'Assign role to player',
setup: SetupGameServerPlayers.setup,
test: async function () {
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.READ_GAMESERVERS]);
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.MANAGE_GAMESERVERS]);

const role = await this.client.role.roleControllerCreate({
name: 'Test role',
Expand All @@ -26,7 +26,7 @@ const tests = [
name: 'Assigning the same role twice should fail',
setup: SetupGameServerPlayers.setup,
test: async function () {
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.READ_GAMESERVERS]);
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.MANAGE_GAMESERVERS]);
const role = await this.client.role.roleControllerCreate({
name: 'Test role',
permissions,
Expand All @@ -43,7 +43,7 @@ const tests = [
name: 'Assigning the same role for different gameservers should work',
setup: SetupGameServerPlayers.setup,
test: async function () {
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.READ_GAMESERVERS]);
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.MANAGE_GAMESERVERS]);
const role = await this.client.role.roleControllerCreate({
name: 'Test role',
permissions,
Expand All @@ -64,7 +64,7 @@ const tests = [
name: 'Assigning the same role for same gameserver should fail',
setup: SetupGameServerPlayers.setup,
test: async function () {
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.READ_GAMESERVERS]);
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.MANAGE_GAMESERVERS]);
const role = await this.client.role.roleControllerCreate({
name: 'Test role',
permissions,
Expand All @@ -85,7 +85,7 @@ const tests = [
name: 'Can assign an expiring role to a player',
setup: SetupGameServerPlayers.setup,
test: async function () {
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.READ_GAMESERVERS]);
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.MANAGE_GAMESERVERS]);
const role = await this.client.role.roleControllerCreate({
name: 'Test role',
permissions,
Expand All @@ -108,7 +108,7 @@ const tests = [
name: 'Expired roles get deleted',
setup: SetupGameServerPlayers.setup,
test: async function () {
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.READ_GAMESERVERS]);
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.MANAGE_GAMESERVERS]);
const role = await this.client.role.roleControllerCreate({
name: 'Test role',
permissions,
Expand All @@ -135,7 +135,7 @@ const tests = [
// Assign a different role for gameserver with expiry
// -> Global role should still be there

const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.READ_GAMESERVERS]);
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.MANAGE_GAMESERVERS]);
const globalRole = await this.client.role.roleControllerCreate({
name: 'Global role',
permissions,
Expand Down Expand Up @@ -173,7 +173,7 @@ const tests = [
// Assign a different role for gameserver with no expiry
// -> Global role should be gone

const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.READ_GAMESERVERS]);
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.MANAGE_GAMESERVERS]);
const globalRole = await this.client.role.roleControllerCreate({
name: 'Global role',
permissions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const tests = [
name: 'Can assign an expiring role to a user',
setup: userSetup,
test: async function () {
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.READ_GAMESERVERS]);
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.MANAGE_GAMESERVERS]);
const role = await this.client.role.roleControllerCreate({
name: 'Test role',
permissions,
Expand All @@ -47,7 +47,7 @@ const tests = [
name: 'Expired roles get deleted',
setup: userSetup,
test: async function () {
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.READ_GAMESERVERS]);
const permissions = await this.client.permissionCodesToInputs([PERMISSIONS.MANAGE_GAMESERVERS]);
const role = await this.client.role.roleControllerCreate({
name: 'Test role',
permissions,
Expand Down
2 changes: 1 addition & 1 deletion packages/app-api/src/db/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
}>;

abstract find(filters: ITakaroQuery<OutputDTO>): Promise<PaginatedOutput<OutputDTO>>;
abstract findOne(id: string | number): Promise<OutputDTO>;
abstract findOne(id: string | number, ...args: any[]): Promise<OutputDTO>;

Check warning on line 30 in packages/app-api/src/db/base.ts

View workflow job for this annotation

GitHub Actions / Run Prettier and Commit Changes

Unexpected any. Specify a different type

Check warning on line 30 in packages/app-api/src/db/base.ts

View workflow job for this annotation

GitHub Actions / node-ci (18.18)

Unexpected any. Specify a different type
abstract create(item: CreateInputDTO): Promise<OutputDTO>;
abstract update(id: string, item: UpdateDTO): Promise<OutputDTO>;
abstract delete(id: string): Promise<boolean>;
Expand Down
9 changes: 6 additions & 3 deletions packages/app-api/src/db/gameserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,19 @@ export class GameServerRepo extends ITakaroRepo<
};
}

async findOne(id: string): Promise<GameServerOutputDTO> {
async findOne(id: string, decryptConnectionInfo: boolean): Promise<GameServerOutputDTO> {
const { query } = await this.getModel();
const data = await query.findById(id);

if (!data) {
throw new errors.NotFoundError(`Record with id ${id} not found`);
}

const connectionInfo = JSON.parse(await decrypt(data.connectionInfo as unknown as string));
if (!decryptConnectionInfo) {
return new GameServerOutputDTO(data);
}

const connectionInfo = JSON.parse(await decrypt(data.connectionInfo as unknown as string));
return new GameServerOutputDTO({ ...data, connectionInfo });
}

Expand Down Expand Up @@ -169,7 +172,7 @@ export class GameServerRepo extends ITakaroRepo<
});

await query.updateAndFetchById(id, updateData);
return this.findOne(id);
return this.findOne(id, true);
}

async getModuleInstallation(gameserverId: string, moduleId: string) {
Expand Down
2 changes: 1 addition & 1 deletion packages/app-api/src/service/Base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
abstract get repo(): NOT_DOMAIN_SCOPED_ITakaroRepo<Model, OutputDTO, CreateInputDTO, UpdateDTO>;

abstract find(filters: ITakaroQuery<OutputDTO>): Promise<PaginatedOutput<OutputDTO>>;
abstract findOne(id: string | number): Promise<OutputDTO | undefined>;
abstract findOne(id: string | number, ...args: any[]): Promise<OutputDTO | undefined>;

Check warning on line 17 in packages/app-api/src/service/Base.ts

View workflow job for this annotation

GitHub Actions / Run Prettier and Commit Changes

Unexpected any. Specify a different type

Check warning on line 17 in packages/app-api/src/service/Base.ts

View workflow job for this annotation

GitHub Actions / node-ci (18.18)

Unexpected any. Specify a different type
abstract create(item: CreateInputDTO): Promise<OutputDTO>;
abstract update(id: string, item: UpdateDTO): Promise<OutputDTO | undefined>;
abstract delete(id: string): Promise<string>;
Expand Down
8 changes: 4 additions & 4 deletions packages/app-api/src/service/GameServerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,8 @@ export class GameServerService extends TakaroService<
return this.repo.find(filters);
}

findOne(id: string): Promise<GameServerOutputDTO> {
return this.repo.findOne(id);
findOne(id: string, decryptConnectionInfo: boolean): Promise<GameServerOutputDTO> {
return this.repo.findOne(id, decryptConnectionInfo);
}

async create(item: GameServerCreateDTO): Promise<GameServerOutputDTO> {
Expand Down Expand Up @@ -226,7 +226,7 @@ export class GameServerService extends TakaroService<
const reachability = await instance.testReachability();
gameServerLatency.set({ gameserver: id, domain: this.domainId }, reachability.latency ?? 0);

const currentServer = await this.findOne(id);
const currentServer = await this.findOne(id, true);

if (currentServer.reachable !== reachability.connectable) {
this.log.info(`Updating reachability for ${id} to ${reachability.connectable}`);
Expand Down Expand Up @@ -366,7 +366,7 @@ export class GameServerService extends TakaroService<
}

async getGame(id: string): Promise<IGameServer> {
const gameserver = await this.repo.findOne(id);
const gameserver = await this.repo.findOne(id, true);
let gameInstance = gameClassCache.get(id);

if (gameInstance) {
Expand Down
3 changes: 1 addition & 2 deletions packages/e2e/src/web-main/smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import { TEST_IDS } from './testIds.js';
// When a user certain specific READ_* permissions, they should be able to see the page
const items = [
{
permission: [PERMISSIONS.ReadEvents, PERMISSIONS.ReadGameservers, PERMISSIONS.ReadPlayers, PERMISSIONS.ReadUsers],
permission: [PERMISSIONS.ReadEvents, PERMISSIONS.ReadPlayers, PERMISSIONS.ReadUsers],
linkName: 'Events',
path: 'events',
},
{ permission: [PERMISSIONS.ReadPlayers], linkName: 'Players', path: 'players' },
{ permission: [PERMISSIONS.ReadGameservers], linkName: 'Game servers', path: 'gameservers' },
{ permission: [PERMISSIONS.ReadUsers], linkName: 'Users', path: 'users' },
{ permission: [PERMISSIONS.ReadModules], linkName: 'Modules', path: 'modules' },
{ permission: [PERMISSIONS.ReadVariables], linkName: 'Variables', path: 'variables' },
Expand Down
Loading
Loading