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

[#034] 인가 구현 #35

Merged
merged 11 commits into from
Apr 9, 2024
81 changes: 80 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,19 @@
"@nestjs/config": "^3.2.0",
"@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.2.0",
"@nestjs/passport": "^10.0.3",
"@nestjs/platform-express": "^10.0.0",
"@nestjs/typeorm": "^10.0.2",
"@types/passport-jwt": "^4.0.1",
"axios": "^1.6.8",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"jest-fetch-mock": "^3.0.3",
"jsonwebtoken": "^9.0.2",
"jwks-rsa": "^3.1.0",
"jest-fetch-mock": "^3.0.3",
"mysql2": "^3.9.2",
"nest-winston": "^1.9.4",
"passport-jwt": "^4.0.1",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
"typeorm": "^0.3.20",
Expand Down
19 changes: 16 additions & 3 deletions src/algorithm/algorithm.controller.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
import { Body, Controller, Delete, Param, Patch, Post } from '@nestjs/common';
import {
Body,
Controller,
Delete,
Param,
Patch,
Post,
UseGuards,
} from '@nestjs/common';
import { AlgorithmService } from './algorithm.service';
import { CreateAlgorithmDto } from './createAlgorithm.dto';
import { JwtAuthGuard } from '../auth/guard/jwt-auth.guard';
import { UserId } from '../decorator/user-id.decorator';
import { OwnershipGuard } from '../auth/guard/ownership.guard';

@UseGuards(JwtAuthGuard)
@Controller('api/stat/algorithm')
export class AlgorithmController {
constructor(private readonly algorithmService: AlgorithmService) {}

@Post()
async algorithmCreate(@Body() body: CreateAlgorithmDto) {
const userId = 'user';
async algorithmCreate(@Body() body: CreateAlgorithmDto, @UserId() userId) {
await this.algorithmService.createAlgorithm(userId, body.bojId);
}

@UseGuards(OwnershipGuard)
@Patch(':id')
async algorithmModify(
@Param('id') userId,
Expand All @@ -20,6 +32,7 @@ export class AlgorithmController {
await this.algorithmService.modifyAlgorithm(userId, body.bojId);
}

@UseGuards(OwnershipGuard)
@Delete(':id')
async algorithmRemove(@Param('id') userId) {
await this.algorithmService.removeAlgorithm(userId);
Expand Down
3 changes: 2 additions & 1 deletion src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { AuthService } from './auth.service';
import { JwtConfig } from '../Config/jwt.config';
import { JwtModule } from '@nestjs/jwt';
import { UserModule } from '../user/user.module';
import { JwtStrategy } from './strategy/jwt.strategy';

@Module({
imports: [JwtModule.registerAsync({ useClass: JwtConfig }), UserModule],
controllers: [AuthController],
providers: [AuthService],
providers: [AuthService, JwtStrategy],
})
export class AuthModule {}
5 changes: 5 additions & 0 deletions src/auth/guard/jwt-auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
20 changes: 20 additions & 0 deletions src/auth/guard/ownership.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {
CanActivate,
ExecutionContext,
ForbiddenException,
Injectable,
} from '@nestjs/common';

@Injectable()
export class OwnershipGuard implements CanActivate {
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const requestedUserId = request.params.id;
const userId = request.user.userId;
if (requestedUserId !== userId) {
throw new ForbiddenException('수정할 권한이 없습니다.');
}

return true;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ownership 에서는 AuthGuard 를 통과한 요청들에 request.user.userId 가 담겨있다. 이 userId 와 파라미터의 Id 를 비교해 해당 리소스를 소유한 사람인지 비교한다

19 changes: 19 additions & 0 deletions src/auth/strategy/jwt.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: configService.get('JWT_SECRET'),
});
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jwt 를 만드는 라이브러리랑 검증하는 라이브러리랑 달라서 그런데 혹시 postman 으로 했을때 통과됐나요? jwt token 은 @nestjs/jwt 에서 generate 되고 검증은 @nestjs/passport 를 사용해서 혹시나해서, 테스트 통과했으면 좋습니다~

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 통과 됩니다. passport 에서 jwt strategy를 사용해서 jwt 확인 가능합니다

async validate(payload: any) {
return { userId: payload.userId };
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request 의 user 필드에 userId를 담는다

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

export const UserId = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
// Guard 이후에 실행되므로 jwtToken의 유효성은 보장
const request = ctx.switchToHttp().getRequest();
return request.user.userId;
},
);
12 changes: 8 additions & 4 deletions src/github/github.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,28 @@ import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
Query,
UseGuards,
} from '@nestjs/common';
import { GithubService } from './github.service';
import { CreateGithubDto } from './createGitub.dto';
import { JwtAuthGuard } from '../auth/guard/jwt-auth.guard';
import { UserId } from '../decorator/user-id.decorator';
import { OwnershipGuard } from '../auth/guard/ownership.guard';

@UseGuards(JwtAuthGuard)
@Controller('api/stat/github')
export class GithubController {
constructor(private readonly githubService: GithubService) {}

@Post()
public async gitHubCreate(@Body() body: CreateGithubDto) {
const userId = '123';
public async gitHubCreate(@Body() body: CreateGithubDto, @UserId() userId) {
await this.githubService.createGithub(body, userId);
}

@UseGuards(OwnershipGuard)
@Patch(':id')
public async gitHubModify(
@Param('id') userId: string,
Expand All @@ -29,6 +32,7 @@ export class GithubController {
await this.githubService.modifyGithub(body, userId);
}

@UseGuards(OwnershipGuard)
@Delete(':id')
public async gitHubDelete(@Param('id') userId: string) {
await this.githubService.deleteGithub(userId);
Expand Down
Loading