diff --git a/backend/src/auth/auth.controller.ts b/backend/src/auth/auth.controller.ts index 20c876f..1a9dcf5 100644 --- a/backend/src/auth/auth.controller.ts +++ b/backend/src/auth/auth.controller.ts @@ -42,6 +42,7 @@ export class AuthController { ) { if (!this.config.get("ALLOW_REGISTRATION")) throw new ForbiddenException("Registration is not allowed"); + const result = await this.authService.signUp(dto); response = this.addTokensToResponse( diff --git a/backend/src/file/file.controller.ts b/backend/src/file/file.controller.ts index ca10cc1..40b9020 100644 --- a/backend/src/file/file.controller.ts +++ b/backend/src/file/file.controller.ts @@ -12,8 +12,6 @@ import { import { SkipThrottle } from "@nestjs/throttler"; import * as contentDisposition from "content-disposition"; import { Response } from "express"; -import { JwtGuard } from "src/auth/guard/jwt.guard"; -import { FileDownloadGuard } from "src/file/guard/fileDownload.guard"; import { CreateShareGuard } from "src/share/guard/createShare.guard"; import { ShareOwnerGuard } from "src/share/guard/shareOwner.guard"; import { ShareSecurityGuard } from "src/share/guard/shareSecurity.guard"; @@ -44,30 +42,8 @@ export class FileController { ); } - @Get(":fileId/download") - @UseGuards(ShareSecurityGuard) - async getFileDownloadUrl( - @Param("shareId") shareId: string, - @Param("fileId") fileId: string - ) { - const url = this.fileService.getFileDownloadUrl(shareId, fileId); - - return { url }; - } - - @Get("zip/download") - @UseGuards(ShareSecurityGuard) - async getZipArchiveDownloadURL( - @Param("shareId") shareId: string, - @Param("fileId") fileId: string - ) { - const url = this.fileService.getFileDownloadUrl(shareId, fileId); - - return { url }; - } - @Get("zip") - @UseGuards(FileDownloadGuard) + @UseGuards(ShareSecurityGuard) async getZip( @Res({ passthrough: true }) res: Response, @Param("shareId") shareId: string @@ -82,7 +58,7 @@ export class FileController { } @Get(":fileId") - @UseGuards(FileDownloadGuard) + @UseGuards(ShareSecurityGuard) async getFile( @Res({ passthrough: true }) res: Response, @Param("shareId") shareId: string, diff --git a/backend/src/file/file.service.ts b/backend/src/file/file.service.ts index bc16ce6..cf499e8 100644 --- a/backend/src/file/file.service.ts +++ b/backend/src/file/file.service.ts @@ -136,37 +136,5 @@ export class FileService { return fs.createReadStream(`./data/uploads/shares/${shareId}/archive.zip`); } - getFileDownloadUrl(shareId: string, fileId: string) { - const downloadToken = this.generateFileDownloadToken(shareId, fileId); - - return `${this.config.get( - "APP_URL" - )}/api/shares/${shareId}/files/${fileId}?token=${downloadToken}`; - } - - generateFileDownloadToken(shareId: string, fileId: string) { - if (fileId == "zip") fileId = undefined; - - return this.jwtService.sign( - { - shareId, - fileId, - }, - { - expiresIn: "10min", - secret: this.config.get("JWT_SECRET"), - } - ); - } - - verifyFileDownloadToken(shareId: string, token: string) { - try { - const claims = this.jwtService.verify(token, { - secret: this.config.get("JWT_SECRET"), - }); - return claims.shareId == shareId; - } catch { - return false; - } - } + } diff --git a/backend/src/file/guard/fileDownload.guard.ts b/backend/src/file/guard/fileDownload.guard.ts deleted file mode 100644 index e972b4e..0000000 --- a/backend/src/file/guard/fileDownload.guard.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common"; -import { Request } from "express"; -import { FileService } from "src/file/file.service"; - -@Injectable() -export class FileDownloadGuard implements CanActivate { - constructor(private fileService: FileService) {} - - async canActivate(context: ExecutionContext) { - const request: Request = context.switchToHttp().getRequest(); - - const token = request.query.token as string; - const { shareId } = request.params; - - return this.fileService.verifyFileDownloadToken(shareId, token); - } -} diff --git a/backend/src/share/guard/shareSecurity.guard.ts b/backend/src/share/guard/shareSecurity.guard.ts index 1d478a8..c2589ce 100644 --- a/backend/src/share/guard/shareSecurity.guard.ts +++ b/backend/src/share/guard/shareSecurity.guard.ts @@ -5,7 +5,6 @@ import { Injectable, NotFoundException, } from "@nestjs/common"; -import { Reflector } from "@nestjs/core"; import { Request } from "express"; import * as moment from "moment"; import { PrismaService } from "src/prisma/prisma.service"; @@ -14,14 +13,13 @@ import { ShareService } from "src/share/share.service"; @Injectable() export class ShareSecurityGuard implements CanActivate { constructor( - private reflector: Reflector, private shareService: ShareService, private prisma: PrismaService ) {} async canActivate(context: ExecutionContext) { const request: Request = context.switchToHttp().getRequest(); - const shareToken = request.get("X-Share-Token"); + const shareId = Object.prototype.hasOwnProperty.call( request.params, "shareId" @@ -29,6 +27,8 @@ export class ShareSecurityGuard implements CanActivate { ? request.params.shareId : request.params.id; + const shareToken = request.cookies[`share_${shareId}_token`]; + const share = await this.prisma.share.findUnique({ where: { id: shareId }, include: { security: true }, diff --git a/backend/src/share/share.controller.ts b/backend/src/share/share.controller.ts index 41735a8..b653412 100644 --- a/backend/src/share/share.controller.ts +++ b/backend/src/share/share.controller.ts @@ -7,14 +7,14 @@ import { Param, Post, Req, + Res, UseGuards, } from "@nestjs/common"; import { Throttle } from "@nestjs/throttler"; import { User } from "@prisma/client"; -import { Request } from "express"; +import { Request, Response } from "express"; import { GetUser } from "src/auth/decorator/getUser.decorator"; import { JwtGuard } from "src/auth/guard/jwt.guard"; -import { ConfigService } from "src/config/config.service"; import { CreateShareDTO } from "./dto/createShare.dto"; import { MyShareDTO } from "./dto/myShare.dto"; import { ShareDTO } from "./dto/share.dto"; @@ -27,10 +27,7 @@ import { ShareTokenSecurity } from "./guard/shareTokenSecurity.guard"; import { ShareService } from "./share.service"; @Controller("shares") export class ShareController { - constructor( - private shareService: ShareService, - private config: ConfigService - ) {} + constructor(private shareService: ShareService) {} @Get() @UseGuards(JwtGuard) @@ -88,10 +85,20 @@ export class ShareController { } @HttpCode(200) - @Throttle(10, 5 * 60) + @Throttle(20, 5 * 60) @UseGuards(ShareTokenSecurity) @Post(":id/token") - async getShareToken(@Param("id") id: string, @Body() body: SharePasswordDto) { - return this.shareService.getShareToken(id, body.password); + async getShareToken( + @Param("id") id: string, + @Res({ passthrough: true }) response: Response, + @Body() body: SharePasswordDto + ) { + const token = await this.shareService.getShareToken(id, body.password); + response.cookie(`share_${id}_token`, token, { + path: "/", + httpOnly: true, + }); + + return { token }; } } diff --git a/backend/src/share/share.service.ts b/backend/src/share/share.service.ts index a2f1891..9afff73 100644 --- a/backend/src/share/share.service.ts +++ b/backend/src/share/share.service.ts @@ -278,7 +278,7 @@ export class ShareService { const token = await this.generateShareToken(shareId); await this.increaseViewCount(share); - return { token }; + return token; } async generateShareToken(shareId: string) { diff --git a/frontend/src/services/share.service.ts b/frontend/src/services/share.service.ts index c866030..ebe72b3 100644 --- a/frontend/src/services/share.service.ts +++ b/frontend/src/services/share.service.ts @@ -27,21 +27,11 @@ const completeShare = async (id: string) => { }; const get = async (id: string): Promise => { - const shareToken = sessionStorage.getItem(`share_${id}_token`); - return ( - await api.get(`shares/${id}`, { - headers: { "X-Share-Token": shareToken ?? "" }, - }) - ).data; + return (await api.get(`shares/${id}`)).data; }; const getMetaData = async (id: string): Promise => { - const shareToken = sessionStorage.getItem(`share_${id}_token`); - return ( - await api.get(`shares/${id}/metaData`, { - headers: { "X-Share-Token": shareToken ?? "" }, - }) - ).data; + return (await api.get(`shares/${id}/metaData`)).data; }; const remove = async (id: string) => { @@ -53,26 +43,15 @@ const getMyShares = async (): Promise => { }; const getShareToken = async (id: string, password?: string) => { - const { token } = (await api.post(`/shares/${id}/token`, { password })).data; - - sessionStorage.setItem(`share_${id}_token`, token); + await api.post(`/shares/${id}/token`, { password }); }; const isShareIdAvailable = async (id: string): Promise => { return (await api.get(`shares/isShareIdAvailable/${id}`)).data.isAvailable; }; -const getFileDownloadUrl = async (shareId: string, fileId: string) => { - const shareToken = sessionStorage.getItem(`share_${shareId}_token`); - return ( - await api.get(`shares/${shareId}/files/${fileId}/download`, { - headers: { "X-Share-Token": shareToken ?? "" }, - }) - ).data.url; -}; - const downloadFile = async (shareId: string, fileId: string) => { - window.location.href = await getFileDownloadUrl(shareId, fileId); + window.location.href = `${window.location.origin}/api/shares/${shareId}/files/${fileId}`; }; const uploadFile = async (