Skip to content

Commit

Permalink
fix(api): never trust the client. closes #70
Browse files Browse the repository at this point in the history
  • Loading branch information
seiyria committed Jun 24, 2023
1 parent 660703f commit 7f4027e
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 22 deletions.
11 changes: 5 additions & 6 deletions client/src/app/services/actions.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export class ActionsService {
) {}

doAction(action: INotificationAction, notification?: INotification) {
if (action.url) {
// we never send data here, it's inferred server side
this.http.post(`${environment.apiUrl}/${action.url}`, {}).subscribe();
}

if (notification) {
this.notificationService.clearActions(notification.id || '').subscribe();
this.store.dispatch(new ClearNotificationActions(notification.id || ''));
Expand All @@ -27,11 +32,5 @@ export class ActionsService {
if (action.action === 'navigate') {
this.router.navigate([action.actionData.url]);
}

if (action.url) {
this.http
.post(`${environment.apiUrl}/${action.url}`, action.urlData || {})
.subscribe();
}
}
}
7 changes: 0 additions & 7 deletions client/src/app/services/gameplay.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,6 @@ export class GameplayService {
});
}

wave(targetUserId: string, isWaveBack = true) {
return this.http.post(`${environment.apiUrl}/gameplay/wave`, {
targetUserId,
isWaveBack,
});
}

sellItem(instanceId: string) {
return this.http.post(`${environment.apiUrl}/gameplay/sellitem`, {
instanceId,
Expand Down
10 changes: 10 additions & 0 deletions server/src/modules/notification/notification.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,20 @@ export class Notification implements INotification {
liveAt = new Date(),
expiresAfterHours = 24 * 7,
) {
this._id = new ObjectId();

this.userId = userId;
this.text = text;
this.actions = actions;

if (this.actions) {
this.actions.forEach((action) => {
if (!action.url) return;

action.url = `${action.url}/${this.id}`;
});
}

this.read = false;

this.liveAt = liveAt;
Expand Down
7 changes: 7 additions & 0 deletions server/src/modules/notification/notification.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ export class NotificationService {
return (await this.notifications.find({ userId })).reverse();
}

async getNotificationForUser(
userId: string,
notificationId: string,
): Promise<Notification | null> {
return this.notifications.findOne({ userId, id: notificationId });
}

async getNotificationsForUserAfter(
userId: string,
after: Date,
Expand Down
19 changes: 13 additions & 6 deletions server/src/modules/player/gameplay.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
BadRequestException,
Body,
Controller,
Param,
Post,
UseGuards,
} from '@nestjs/common';
Expand Down Expand Up @@ -55,15 +56,21 @@ export class GameplayController {
@ApiOperation({ summary: 'Explore Event: Wave at another player' })
@Post('wave')
@RollbarHandler()
async wave(
async wave(@User() user): Promise<Partial<IFullUser | IPatchUser>> {
return this.gameplayService.waveToPlayerFromExplore(user.userId);
}

@UseGuards(JwtAuthGuard)
@ApiOperation({ summary: 'Explore Event: Wave at another player' })
@Post('wave/:id')
@RollbarHandler()
async waveFromNotification(
@User() user,
@Body('targetUserId') targetUserId: string,
@Body('isWaveBack') isWaveBack: boolean,
@Param('id') notificationId: string,
): Promise<Partial<IFullUser | IPatchUser>> {
return this.gameplayService.waveToPlayer(
return this.gameplayService.waveToPlayerFromNotification(
user.userId,
targetUserId,
isWaveBack,
notificationId,
);
}

Expand Down
37 changes: 34 additions & 3 deletions server/src/modules/player/gameplay.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
IFullUser,
IItem,
ILocation,
INotificationAction,
IPatchUser,
TrackedStat,
} from '@interfaces';
Expand Down Expand Up @@ -304,14 +305,44 @@ export class GameplayService {
return { player: playerPatches };
}

async waveToPlayer(
async waveToPlayerFromExplore(
userId: string,
targetUserId: string,
isWaveBack: boolean,
): Promise<Partial<IFullUser | IPatchUser>> {
const player = await this.playerService.getPlayerForUser(userId);
if (!player) throw new ForbiddenException('Player not found');

if (!player.action) throw new ForbiddenException('Player has no action');

return this.waveToPlayer(userId, player, player.action);
}

async waveToPlayerFromNotification(
userId: string,
notificationId: string,
): Promise<Partial<IFullUser | IPatchUser>> {
const player = await this.playerService.getPlayerForUser(userId);
if (!player) throw new ForbiddenException('Player not found');

const notification = await this.notificationService.getNotificationForUser(
userId,
notificationId,
);
if (!notification) throw new ForbiddenException('Notification not found');

const notificationAction = notification.actions?.[0];
if (!notificationAction)
throw new ForbiddenException('Notification has no actions');

return this.waveToPlayer(userId, player, notificationAction);
}

private async waveToPlayer(
userId: string,
player: Player,
action: INotificationAction,
) {
const { targetUserId, isWaveBack } = action?.urlData ?? {};

const otherPlayer = await this.playerService.getPlayerForUser(targetUserId);
if (!otherPlayer) throw new ForbiddenException('Target player not found');

Expand Down

0 comments on commit 7f4027e

Please sign in to comment.