Skip to content

Commit

Permalink
Add public decorator
Browse files Browse the repository at this point in the history
Change controller's decorators structure
Add public decorator and override behavior for both auth and
is-reef-admin decorators
Add override-level-access to override the default decorator access for
some endpoints
  • Loading branch information
avalmas-programize committed Sep 10, 2020
1 parent 28deadb commit b210757
Show file tree
Hide file tree
Showing 11 changed files with 77 additions and 33 deletions.
3 changes: 0 additions & 3 deletions packages/api/src/auth/auth.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ import { FirebaseAuthGuard } from './firebase-auth.guard';
import { LevelsGuard } from './levels.guard';

export const Auth = (...levels: AdminLevel[]) => {
if (!levels) {
return UseGuards(FirebaseAuthGuard);
}
return applyDecorators(
SetMetadata('levels', levels),
UseGuards(FirebaseAuthGuard, LevelsGuard),
Expand Down
23 changes: 21 additions & 2 deletions packages/api/src/auth/firebase-auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
import { Injectable } from '@nestjs/common';
import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Reflector } from '@nestjs/core';
import { of } from 'rxjs';

@Injectable()
export class FirebaseAuthGuard extends AuthGuard('custom') {}
export class FirebaseAuthGuard extends AuthGuard('custom') {
constructor(private reflector: Reflector) {
super();
}

canActivate(context: ExecutionContext) {
const isPublic = this.reflector.get<boolean>(
'isPublic',
context.getHandler(),
);

if (isPublic) {
return of(true);
}

return super.canActivate(context);
}
}
14 changes: 13 additions & 1 deletion packages/api/src/auth/is-reef-admin.guard.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CanActivate, ExecutionContext } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Reflector } from '@nestjs/core';
import { Repository } from 'typeorm';
import { User, AdminLevel } from '../users/users.entity';
import { Reef } from '../reefs/reefs.entity';
Expand All @@ -8,9 +9,20 @@ export class IsReefAdminGuard implements CanActivate {
constructor(
@InjectRepository(Reef)
private reefRepository: Repository<Reef>,

private reflector: Reflector,
) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
const isPublic = this.reflector.get<boolean>(
'isPublic',
context.getHandler(),
);

if (isPublic) {
return true;
}

const request = context.switchToHttp().getRequest();
const { user }: { user: User } = request;
const reefId = parseInt(request.params.reef_id, 10);
Expand All @@ -19,7 +31,7 @@ export class IsReefAdminGuard implements CanActivate {
return true;
}

if (!Number.isNaN(reefId)) {
if (!Number.isNaN(reefId) && user.adminLevel === AdminLevel.ReefManager) {
const isReefAdmin = await this.reefRepository
.createQueryBuilder('reef')
.innerJoin('reef.admins', 'admins', 'admins.id = :userId', {
Expand Down
7 changes: 7 additions & 0 deletions packages/api/src/auth/levels.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ export class LevelsGuard implements CanActivate {
constructor(private reflector: Reflector) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
const isPublic = this.reflector.get<boolean>(
'isPublic',
context.getHandler(),
);
if (isPublic) {
return true;
}
const levels = this.reflector.get<AdminLevel[]>(
'levels',
context.getHandler(),
Expand Down
6 changes: 6 additions & 0 deletions packages/api/src/auth/override-level-access.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { applyDecorators, SetMetadata } from '@nestjs/common';
import { AdminLevel } from '../users/users.entity';

export const OverrideLevelAccess = (...levels: AdminLevel[]) => {
return applyDecorators(SetMetadata('levels', levels));
};
5 changes: 5 additions & 0 deletions packages/api/src/auth/public.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { applyDecorators, SetMetadata } from '@nestjs/common';

export const Public = () => {
return applyDecorators(SetMetadata('isPublic', true));
};
7 changes: 4 additions & 3 deletions packages/api/src/reef-pois/reef-pois.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,34 @@ import { FilterReefPoiDto } from './dto/filter-reef-poi.dto';
import { UpdateReefPoiDto } from './dto/update-reef-poi.dto';
import { AdminLevel } from '../users/users.entity';
import { Auth } from '../auth/auth.decorator';
import { Public } from '../auth/public.decorator';

@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Controller('pois')
export class ReefPoisController {
constructor(private poisService: ReefPoisService) {}

@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Post()
create(
@Body() createReefPoiDto: CreateReefPoiDto,
): Promise<ReefPointOfInterest> {
return this.poisService.create(createReefPoiDto);
}

@Public()
@Get()
find(
@Query() filterReefPoiDto: FilterReefPoiDto,
): Promise<ReefPointOfInterest[]> {
return this.poisService.find(filterReefPoiDto);
}

@Public()
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number): Promise<ReefPointOfInterest> {
return this.poisService.findOne(id);
}

@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
Expand All @@ -50,7 +52,6 @@ export class ReefPoisController {
return this.poisService.update(id, updateReefPoiDto);
}

@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise<void> {
return this.poisService.delete(id);
Expand Down
8 changes: 5 additions & 3 deletions packages/api/src/reefs/reefs.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,37 @@ import { FilterReefDto } from './dto/filter-reef.dto';
import { UpdateReefDto } from './dto/update-reef.dto';
import { AdminLevel } from '../users/users.entity';
import { Auth } from '../auth/auth.decorator';
import { Public } from '../auth/public.decorator';

@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Controller('reefs')
export class ReefsController {
constructor(private reefsService: ReefsService) {}

@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Post()
create(@Body() createReefDto: CreateReefDto): Promise<Reef> {
return this.reefsService.create(createReefDto);
}

@Public()
@Get()
find(@Query() filterReefDto: FilterReefDto): Promise<Reef[]> {
return this.reefsService.find(filterReefDto);
}

@Public()
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number): Promise<Reef> {
return this.reefsService.findOne(id);
}

@Public()
@Get(':id/daily_data')
// eslint-disable-next-line no-unused-vars
findDailyData(@Param('id') id: number) {
return this.reefsService.findDailyData(id);
}

@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
Expand All @@ -52,7 +55,6 @@ export class ReefsController {
return this.reefsService.update(id, updateReefDto);
}

@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise<void> {
return this.reefsService.delete(id);
Expand Down
7 changes: 4 additions & 3 deletions packages/api/src/regions/regions.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,30 @@ import { FilterRegionDto } from './dto/filter-region.dto';
import { UpdateRegionDto } from './dto/update-region.dto';
import { Auth } from '../auth/auth.decorator';
import { AdminLevel } from '../users/users.entity';
import { Public } from '../auth/public.decorator';

@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Controller('regions')
export class RegionsController {
constructor(private regionsService: RegionsService) {}

@Auth(AdminLevel.SuperAdmin)
@Post()
create(@Body() createRegionDto: CreateRegionDto): Promise<Region> {
return this.regionsService.create(createRegionDto);
}

@Public()
@Get()
find(@Query() filterRegionDto: FilterRegionDto): Promise<Region[]> {
return this.regionsService.find(filterRegionDto);
}

@Public()
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number): Promise<Region> {
return this.regionsService.findOne(id);
}

@Auth(AdminLevel.SuperAdmin)
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
Expand All @@ -46,7 +48,6 @@ export class RegionsController {
return this.regionsService.update(id, updateRegionDto);
}

@Auth(AdminLevel.SuperAdmin)
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise<void> {
return this.regionsService.delete(id);
Expand Down
20 changes: 6 additions & 14 deletions packages/api/src/surveys/surveys.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ import { EditSurveyDto } from './dto/edit-survey.dto';
import { EditSurveyMediaDto } from './dto/edit-survey-media.dto';
import { IsReefAdminGuard } from '../auth/is-reef-admin.guard';
import { AuthRequest } from '../auth/auth.types';
import { Public } from '../auth/public.decorator';

@UseGuards(IsReefAdminGuard)
@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Controller('reefs/:reef_id/surveys')
export class SurveysController {
constructor(private surveyService: SurveysService) {}

@UseGuards(IsReefAdminGuard)
@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Post('upload')
@AcceptFile('file', ['image', 'video'], 'surveys', 'reef')
upload(@UploadedFile('file') file: any): string {
Expand All @@ -39,8 +40,6 @@ export class SurveysController {
return `https://storage.googleapis.com/${process.env.GCS_BUCKET}/${file.filename}`;
}

@UseGuards(IsReefAdminGuard)
@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Post()
create(
@Body() createSurveyDto: CreateSurveyDto,
Expand All @@ -50,8 +49,6 @@ export class SurveysController {
return this.surveyService.create(createSurveyDto, req.user, reefId);
}

@UseGuards(IsReefAdminGuard)
@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Post(':id/media')
createMedia(
@Body() createSurveyMediaDto: CreateSurveyMediaDto,
Expand All @@ -60,23 +57,24 @@ export class SurveysController {
return this.surveyService.createMedia(createSurveyMediaDto, surveyId);
}

@Public()
@Get()
find(@Param('reef_id', ParseIntPipe) reefId: number): Promise<Survey[]> {
return this.surveyService.find(reefId);
}

@Public()
@Get(':id')
findOne(@Param('id', ParseIntPipe) surveyId: number): Promise<Survey> {
return this.surveyService.findOne(surveyId);
}

@Public()
@Get(':id/media')
findMedia(@Param('id', ParseIntPipe) surveyId): Promise<SurveyMedia[]> {
return this.surveyService.findMedia(surveyId);
}

@UseGuards(IsReefAdminGuard)
@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Put('media/:id')
updateMedia(
@Param('id', ParseIntPipe) mediaId: number,
Expand All @@ -85,8 +83,6 @@ export class SurveysController {
return this.surveyService.updateMedia(editSurveyMediaDto, mediaId);
}

@UseGuards(IsReefAdminGuard)
@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Put(':id')
update(
@Param('id', ParseIntPipe) surveyId: number,
Expand All @@ -95,15 +91,11 @@ export class SurveysController {
return this.surveyService.update(editSurveyDto, surveyId);
}

@UseGuards(IsReefAdminGuard)
@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Delete(':id')
delete(@Param('id', ParseIntPipe) surveyId: number): Promise<void> {
return this.surveyService.delete(surveyId);
}

@UseGuards(IsReefAdminGuard)
@Auth(AdminLevel.ReefManager, AdminLevel.SuperAdmin)
@Delete('media/:id')
deleteMedia(@Param('id', ParseIntPipe) mediaId: number): Promise<void> {
return this.surveyService.deleteMedia(mediaId);
Expand Down
10 changes: 6 additions & 4 deletions packages/api/src/users/users.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,26 @@ import { CreateUserDto } from './dto/create-user.dto';
import { Auth } from '../auth/auth.decorator';
import { AuthRequest } from '../auth/auth.types';
import { Reef } from '../reefs/reefs.entity';
import { OverrideLevelAccess } from '../auth/override-level-access.decorator';
import { Public } from '../auth/public.decorator';

@Auth()
@Controller('users')
export class UsersController {
constructor(private usersService: UsersService) {}

@Public()
@Post()
create(@Req() req: any, @Body() createUserDto: CreateUserDto): Promise<User> {
return this.usersService.create(req, createUserDto);
}

@Auth()
@Get('current')
getSelf(@Req() req: AuthRequest): Promise<User | undefined> {
return this.usersService.getSelf(req);
}

@Auth(AdminLevel.SuperAdmin)
@OverrideLevelAccess(AdminLevel.SuperAdmin)
@Put(':id/level')
setAdminLevel(
@Param('id', ParseIntPipe) id: number,
Expand All @@ -40,13 +43,12 @@ export class UsersController {
return this.usersService.setAdminLevel(id, adminLevel);
}

@Auth(AdminLevel.SuperAdmin)
@OverrideLevelAccess(AdminLevel.SuperAdmin)
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise<void> {
return this.usersService.delete(id);
}

@Auth()
@Get('current/administrated-reefs')
getAdministeredReefs(@Req() req: AuthRequest): Promise<Reef[]> {
return this.usersService.getAdministeredReefs(req);
Expand Down

0 comments on commit b210757

Please sign in to comment.