mirror of
https://github.com/stonith404/pingvin-share.git
synced 2025-02-19 01:55:48 -05:00
fix: store only 10 share tokens in the cookies and clear the expired ones
This commit is contained in:
parent
414bcecbb5
commit
e5a0c649e3
2 changed files with 53 additions and 11 deletions
|
@ -10,9 +10,11 @@ import {
|
||||||
Res,
|
Res,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
|
import { JwtService } from "@nestjs/jwt";
|
||||||
import { Throttle } from "@nestjs/throttler";
|
import { Throttle } from "@nestjs/throttler";
|
||||||
import { User } from "@prisma/client";
|
import { User } from "@prisma/client";
|
||||||
import { Request, Response } from "express";
|
import { Request, Response } from "express";
|
||||||
|
import * as moment from "moment";
|
||||||
import { GetUser } from "src/auth/decorator/getUser.decorator";
|
import { GetUser } from "src/auth/decorator/getUser.decorator";
|
||||||
import { AdministratorGuard } from "src/auth/guard/isAdmin.guard";
|
import { AdministratorGuard } from "src/auth/guard/isAdmin.guard";
|
||||||
import { JwtGuard } from "src/auth/guard/jwt.guard";
|
import { JwtGuard } from "src/auth/guard/jwt.guard";
|
||||||
|
@ -29,7 +31,10 @@ import { ShareTokenSecurity } from "./guard/shareTokenSecurity.guard";
|
||||||
import { ShareService } from "./share.service";
|
import { ShareService } from "./share.service";
|
||||||
@Controller("shares")
|
@Controller("shares")
|
||||||
export class ShareController {
|
export class ShareController {
|
||||||
constructor(private shareService: ShareService) {}
|
constructor(
|
||||||
|
private shareService: ShareService,
|
||||||
|
private jwtService: JwtService,
|
||||||
|
) {}
|
||||||
|
|
||||||
@Get("all")
|
@Get("all")
|
||||||
@UseGuards(JwtGuard, AdministratorGuard)
|
@UseGuards(JwtGuard, AdministratorGuard)
|
||||||
|
@ -121,10 +126,13 @@ export class ShareController {
|
||||||
@Post(":id/token")
|
@Post(":id/token")
|
||||||
async getShareToken(
|
async getShareToken(
|
||||||
@Param("id") id: string,
|
@Param("id") id: string,
|
||||||
|
@Req() request: Request,
|
||||||
@Res({ passthrough: true }) response: Response,
|
@Res({ passthrough: true }) response: Response,
|
||||||
@Body() body: SharePasswordDto,
|
@Body() body: SharePasswordDto,
|
||||||
) {
|
) {
|
||||||
const token = await this.shareService.getShareToken(id, body.password);
|
const token = await this.shareService.getShareToken(id, body.password);
|
||||||
|
|
||||||
|
this.clearShareTokenCookies(request, response);
|
||||||
response.cookie(`share_${id}_token`, token, {
|
response.cookie(`share_${id}_token`, token, {
|
||||||
path: "/",
|
path: "/",
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
|
@ -132,4 +140,32 @@ export class ShareController {
|
||||||
|
|
||||||
return { token };
|
return { token };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps the 10 most recent share token cookies and deletes the rest and all expired ones
|
||||||
|
*/
|
||||||
|
private clearShareTokenCookies(request: Request, response: Response) {
|
||||||
|
const shareTokenCookies = Object.entries(request.cookies)
|
||||||
|
.filter(([key]) => key.startsWith("share_") && key.endsWith("_token"))
|
||||||
|
.map(([key, value]) => ({
|
||||||
|
key,
|
||||||
|
payload: this.jwtService.decode(value),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const expiredTokens = shareTokenCookies.filter(
|
||||||
|
(cookie) => cookie.payload.exp < moment().unix(),
|
||||||
|
);
|
||||||
|
const validTokens = shareTokenCookies.filter(
|
||||||
|
(cookie) => cookie.payload.exp >= moment().unix(),
|
||||||
|
);
|
||||||
|
|
||||||
|
expiredTokens.forEach((cookie) => response.clearCookie(cookie.key));
|
||||||
|
|
||||||
|
if (validTokens.length > 10) {
|
||||||
|
validTokens
|
||||||
|
.sort((a, b) => a.payload.exp - b.payload.exp)
|
||||||
|
.slice(0, -10)
|
||||||
|
.forEach((cookie) => response.clearCookie(cookie.key));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
Injectable,
|
Injectable,
|
||||||
NotFoundException,
|
NotFoundException,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { JwtService } from "@nestjs/jwt";
|
import { JwtService, JwtSignOptions } from "@nestjs/jwt";
|
||||||
import { Share, User } from "@prisma/client";
|
import { Share, User } from "@prisma/client";
|
||||||
import * as archiver from "archiver";
|
import * as archiver from "archiver";
|
||||||
import * as argon from "argon2";
|
import * as argon from "argon2";
|
||||||
|
@ -328,15 +328,21 @@ export class ShareService {
|
||||||
const { expiration } = await this.prisma.share.findUnique({
|
const { expiration } = await this.prisma.share.findUnique({
|
||||||
where: { id: shareId },
|
where: { id: shareId },
|
||||||
});
|
});
|
||||||
return this.jwtService.sign(
|
|
||||||
{
|
const tokenPayload = {
|
||||||
shareId,
|
shareId,
|
||||||
},
|
iat: moment().unix(),
|
||||||
{
|
};
|
||||||
expiresIn: moment(expiration).diff(new Date(), "seconds") + "s",
|
|
||||||
secret: this.config.get("internal.jwtSecret"),
|
const tokenOptions: JwtSignOptions = {
|
||||||
},
|
secret: this.config.get("internal.jwtSecret"),
|
||||||
);
|
};
|
||||||
|
|
||||||
|
if (!moment(expiration).isSame(0)) {
|
||||||
|
tokenOptions.expiresIn = moment(expiration).diff(new Date(), "seconds");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.jwtService.sign(tokenPayload, tokenOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyShareToken(shareId: string, token: string) {
|
async verifyShareToken(shareId: string, token: string) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue