Skip to content
This repository has been archived by the owner on Jul 31, 2022. It is now read-only.

Commit

Permalink
Added Graphql-Auth-Guard and CurrentUser decorator
Browse files Browse the repository at this point in the history
Ditched custom auth guard instead of the improved GraphqlAuthGuard. You now can get the user who logged in by using @currentuser() user: any as function parameter
  • Loading branch information
JensUweB committed Jan 10, 2020
1 parent c393b63 commit 26c150e
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 65 deletions.
26 changes: 15 additions & 11 deletions schema.gpl
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,13 @@ input MartialArtsInput {

type Mutation {
"""Add a new club to the clubs array of a user"""
addClub(clubId: String!, userId: String!): UserDto!
addUserToClub(clubId: String!): UserDto!

"""Add a new martial art rank to a user"""
addMartialArtRankToUser(rankId: String!, userId: String!): UserDto!
deleteUser(id: String!): Boolean!
"""Add a new martial art rank to the current user"""
addMartialArtRankToUser(rankId: String!): UserDto!

"""Deletes the account of the current user"""
deleteUser: Boolean!

"""Creates a new martial art"""
createMartialArt(input: MartialArtsInput!): MartialArtsDto!
Expand All @@ -210,14 +212,14 @@ type Mutation {

"""Creates a new exam result"""
createExamResult(input: ExamResultInput!): ExamResultDto!

"""Delete all exam results related to the current user"""
deleteRelatedExamResults: Boolean!
}

type Query {
"""Searches for a user by a given email"""
getUserByEmail(email: String!): UserDto!

"""Searchs for a user by a given id"""
getUserById(id: String!): UserDto!
"""Returns an user object representing the current logged in user"""
getUser: UserDto!

"""Returns an array of martial art objects"""
getAllMartialArts: [MartialArtsDto!]!
Expand All @@ -242,15 +244,17 @@ type Query {

"""Returns an array of all exams. Including previous ones."""
getAllExams: [ExamDto!]!

"""Returns one exam with the given id"""
getExamById(id: String!): ExamDto!

"""
Returns an array of all exams. Only exams with an future starting date included.
"""
getPlannedExams: [ExamDto!]!

"""Returns an array with all exam results of a given user"""
getAllExamResults(userId: String!): [ExamResultDto!]!
"""Returns an array with all exam results of the current user"""
getAllExamResults: [ExamResultDto!]!

"""Returns one exam result with a given id"""
getExamResultById(id: String!): ExamResultDto!
Expand Down
2 changes: 1 addition & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { ExamResultModule } from './modules/examResult/examResult.module';
{
autoSchemaFile: 'schema.gpl',
installSubscriptionHandlers: true,
context: ({req}) => {return {request: req};}
context: ({req}) => ({req})
}),
MongooseModule.forRoot(`mongodb://admin:admin%[email protected]:27017/examadmin?authSource=admin&compressors=zlib&readPreference=primary&gssapiServiceName=mongodb&appname=MongoDB%20Compass%20Community&ssl=false`),
],
Expand Down
3 changes: 2 additions & 1 deletion src/modules/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import { AuthGuard } from "../guards/auth.guard";
secret: jwtConstants.secret,
signOptions: {expiresIn: 3600}
}),
MongooseModule.forFeature([{name: 'User', schema: UserSchema}])],
//MongooseModule.forFeature([{name: 'User', schema: UserSchema}])
],
providers: [AuthService, JwtStrategy, AuthResolver, AuthGuard],
exports: [AuthService, JwtModule, AuthGuard]
})
Expand Down
4 changes: 2 additions & 2 deletions src/modules/club/club.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { ClubService } from "./club.service";
import { ClubDto } from "./dto/club.dto";
import { ClubInput } from "./inputs/club.input";
import { NotFoundException, UseGuards } from "@nestjs/common";
import { AuthGuard } from "../guards/auth.guard";
import { GraphqlAuthGuard } from "../guards/graphql-auth.guard";

@UseGuards(AuthGuard)
@UseGuards(GraphqlAuthGuard)
@Resolver('Club')
export class ClubResolver {

Expand Down
4 changes: 4 additions & 0 deletions src/modules/decorators/user.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// user.decorator.ts
import { createParamDecorator } from '@nestjs/common';

export const User = createParamDecorator((data, [root, args, ctx, info]) => ctx.req.user);
6 changes: 3 additions & 3 deletions src/modules/exam/exam.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { ExamService } from "./exam.service";
import { ExamDto } from "./dto/exam.dto";
import { ExamInput } from "./inputs/exam.input";
import { UseGuards } from "@nestjs/common";
import { AuthGuard } from "../guards/auth.guard";
import { GraphqlAuthGuard } from "../guards/graphql-auth.guard";

@UseGuards(AuthGuard)
@UseGuards(GraphqlAuthGuard)
@Resolver('Exam')
export class ExamResolver {

Expand All @@ -19,7 +19,7 @@ export class ExamResolver {
return this.examService.findAll();
}

@Query(() => ExamDto, {description: ''})
@Query(() => ExamDto, {description: 'Returns one exam with the given id'})
async getExamById(@Args('id') id: string) {
return this.examService.findById(id);
}
Expand Down
16 changes: 11 additions & 5 deletions src/modules/examResult/examResult.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { ExamResultService } from "./examResult.service";
import { ExamResultDto } from "./dto/examResult.dto";
import { ExamResultInput } from "./inputs/examResult.input";
import { UseGuards } from "@nestjs/common";
import { AuthGuard } from "../guards/auth.guard";
import { User as CurrentUser } from "../decorators/user.decorator";
import { GraphqlAuthGuard } from "../guards/graphql-auth.guard";

@UseGuards(AuthGuard)
@UseGuards(GraphqlAuthGuard)
@Resolver('ExamResult')
export class ExamResultResolver {
constructor(private readonly erService: ExamResultService) {}
Expand All @@ -14,9 +15,9 @@ export class ExamResultResolver {
// Queries
// ===========================================================================

@Query(() => [ExamResultDto], {description: 'Returns an array with all exam results of a given user'})
async getAllExamResults(@Args('userId') userId: string) {
return this.erService.findAll(userId);
@Query(() => [ExamResultDto], {description: 'Returns an array with all exam results of the current user'})
async getAllExamResults(@CurrentUser() user: any) {
return this.erService.findAll(user.userId);
}

@Query(() => ExamResultDto, {description: 'Returns one exam result with a given id'})
Expand All @@ -32,6 +33,11 @@ export class ExamResultResolver {
async createExamResult(@Args('input') input: ExamResultInput) {
return this.erService.create(input);
}

@Mutation(() => Boolean, {description: 'Delete all exam results related to the current user'})
async deleteRelatedExamResults(@CurrentUser() user: any) {
return this.erService.deleteAllRelated(user.userId);
}
// ===========================================================================
// Subscriptions
// ===========================================================================
Expand Down
6 changes: 6 additions & 0 deletions src/modules/examResult/examResult.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ export class ExamResultService {

return examResult.save();
}

async deleteAllRelated(userId: string): Promise<any>{
const result = await this.erModel.deleteMany({'user._id': userId});
if(result) return true;
return false;
}
}
10 changes: 6 additions & 4 deletions src/modules/guards/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { CanActivate, Injectable, ExecutionContext, HttpException, UnauthorizedException } from "@nestjs/common";
import { CanActivate, Injectable, ExecutionContext } from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
import { GqlExecutionContext } from "@nestjs/graphql";
import { jwtConstants } from "../auth/constants";
import { deprecate } from "util";

@Injectable()
export class AuthGuard implements CanActivate {
constructor(readonly jwtService: JwtService/*, readonly userService: UsersService*/) { }
canActivate(context: ExecutionContext): boolean {
const ctx = GqlExecutionContext.create(context);
const { req } = ctx.getContext();
const request = ctx.getContext().request;
const Authorization = request.get('Authorization');

if (Authorization) {
const token = Authorization.replace('Bearer ', '');
const { userId, firstName } = this.jwtService.verify(token) as { userId: string; firstName: string } ;

request.authInfo = { userId, firstName };
//console.log(request.authInfo);
return !!userId;
}
}
Expand Down
9 changes: 6 additions & 3 deletions src/modules/guards/graphql-auth.guard.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { GqlExecutionContext } from '@nestjs/graphql';
import { Observable } from 'rxjs';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';

@Injectable()
export class GqlAuthGuard extends AuthGuard('jwt') {
getRequest(context: ExecutionContext) {
export class GraphqlAuthGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const ctx = GqlExecutionContext.create(context);
return ctx.getContext().req;
const { req } = ctx.getContext();
return super.canActivate(new ExecutionContextHost([req]));
}
}
4 changes: 2 additions & 2 deletions src/modules/martialArts/martialArts.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { MartialArtsDto } from "./dto/martialArts.dto";
import { MartialArtsInput } from "./inputs/martialArts.input";
import { RankDto } from "./dto/rank.dto";
import { UseGuards } from "@nestjs/common";
import { AuthGuard } from "../guards/auth.guard";
import { GraphqlAuthGuard } from "../guards/graphql-auth.guard";

@UseGuards(AuthGuard)
@UseGuards(GraphqlAuthGuard)
@Resolver('MartialArts')
export class MartialArtsResolver {

Expand Down
1 change: 0 additions & 1 deletion src/modules/user/user.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { UserService } from "./user.service";
import { MartialArtsModule } from "../martialArts/martialArts.module";
import { ClubModule } from "../club/club.module";
import { AuthModule } from "../auth/auth.module";

@Module({
imports: [
MongooseModule.forFeature([{name: 'User', schema: UserSchema}]),
Expand Down
63 changes: 31 additions & 32 deletions src/modules/user/user.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,55 @@
import { Resolver, Query, Mutation, Args } from "@nestjs/graphql";
import { Resolver, Query, Mutation, Args, GqlExecutionContext } from "@nestjs/graphql";
import { UserSchema } from './user.schema';
import { UserService } from "./user.service";
import { UserDto } from "./dto/user.dto";
import { UseGuards, NotFoundException, Req } from "@nestjs/common";
import { GqlAuthGuard } from '../guards/graphql-auth.guard';
import { AuthGuard } from "../guards/auth.guard";
import { ExecutionContext } from "graphql/execution/execute";
import { User as CurrentUser } from "../decorators/user.decorator";
import { GraphqlAuthGuard } from "../guards/graphql-auth.guard";


@UseGuards(AuthGuard)
@UseGuards(GraphqlAuthGuard)
@Resolver((of) => UserSchema)
export class UserResolver {

constructor(private readonly userService: UserService) {}
constructor(private readonly userService: UserService) { }

// ===========================================================================
// Queries
// ===========================================================================
@Query(() => UserDto, {description: 'Searches for a user by a given email'})
async getUserByEmail(@Args('email') email: string) {
const result = await this.userService.findByEmail(email);
if(result) return result;
return new NotFoundException('User not found!');
}
@Query(() => UserDto, {description: 'Searchs for a user by a given id'})
async getUserById(@Args('id') id: string) {
const result = await this.userService.findById(id);
if(result) return result;
return new NotFoundException('User not found!');
}

/* @Query(() => UserDto, { description: 'Searches for a user by a given email' })
async getUserByEmail(@Args('email') email: string) {
const result = await this.userService.findByEmail(email);
if (result) return result;
return new NotFoundException('User not found!');
} */
@Query(() => UserDto, { description: 'Returns an user object representing the current logged in user' })
async getUser(@CurrentUser() user: any) {
console.log(user);
const result = await this.userService.findById(user.userId);
if (result) return result;
return new NotFoundException('User not found!');
}

// ===========================================================================
// Mutations
// ===========================================================================
@Mutation(() => UserDto, {description: 'Add a new club to the clubs array of a user'})
async addClub(@Args('userId') userId: string, @Args('clubId') clubId: string) {
const result = await this.userService.addClub(userId, clubId);
if(result) return result;
return new NotFoundException('User not found!');
@Mutation(() => UserDto, { description: 'Add a new club to the clubs array of a user' })
async addUserToClub(@CurrentUser() user: any, @Args('clubId') clubId: string) {
const result = await this.userService.addClub(user.userId, clubId);
if (result) return result;
return new NotFoundException('User not found!');
}
@Mutation(() => UserDto, {description: 'Add a new martial art rank to a user'})
async addMartialArtRankToUser(@Args('userId') userId: string, @Args('rankId') rankId: string) {
const result = await this.userService.addMartialArtRank(userId, rankId);
if(result) return result;
@Mutation(() => UserDto, { description: 'Add a new martial art rank to the current user' })
async addMartialArtRankToUser(@CurrentUser() user: any, @Args('rankId') rankId: string) {
const result = await this.userService.addMartialArtRank(user.userId, rankId);
if (result) return result;
return new NotFoundException('User not found!');
}

@Mutation(() => Boolean)
async deleteUser(@Args('id') id: string){
return this.userService.deleteUser(id);
@Mutation(() => Boolean, {description: 'Deletes the account of the current user'})
async deleteUser(@CurrentUser() user: any) {
return this.userService.deleteUser(user.userId);
}

// ===========================================================================
Expand Down

0 comments on commit 26c150e

Please sign in to comment.