mirror of
https://github.com/usatiuk/photos.git
synced 2025-10-28 15:27:49 +01:00
access photos via jwt
This commit is contained in:
@@ -5,6 +5,8 @@ import { IAPIResponse } from "~types";
|
||||
import * as fs from "fs/promises";
|
||||
import send = require("koa-send");
|
||||
import { getHash, getSize } from "~util";
|
||||
import * as jwt from "jsonwebtoken";
|
||||
import { config } from "~config";
|
||||
|
||||
export const photosRouter = new Router();
|
||||
|
||||
@@ -191,6 +193,39 @@ photosRouter.get("/photos/byID/:id", async (ctx) => {
|
||||
} as IPhotosByIDGetRespBody;
|
||||
});
|
||||
|
||||
photosRouter.get("/photos/showByID/:id/:token", async (ctx) => {
|
||||
const { id, token } = ctx.params as {
|
||||
id: number | undefined;
|
||||
token: string | undefined;
|
||||
};
|
||||
|
||||
if (!(id && token)) {
|
||||
ctx.throw(400);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
jwt.verify(token, config.jwtSecret) as IPhotoJSON;
|
||||
} catch (e) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
|
||||
const photoJson = jwt.decode(token) as IPhotoJSON;
|
||||
const { user } = photoJson;
|
||||
|
||||
const photo = await Photo.findOne({
|
||||
id,
|
||||
user: { id: user },
|
||||
});
|
||||
|
||||
if (!photo || !(await photo.isUploaded())) {
|
||||
ctx.throw(404);
|
||||
return;
|
||||
}
|
||||
|
||||
await send(ctx, photo.getPath());
|
||||
});
|
||||
|
||||
photosRouter.get("/photos/showByID/:id", async (ctx) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
@@ -216,6 +251,37 @@ photosRouter.get("/photos/showByID/:id", async (ctx) => {
|
||||
await send(ctx, photo.getPath());
|
||||
});
|
||||
|
||||
export type IPhotoShowToken = string;
|
||||
export type IPhotosGetShowTokenByID = IAPIResponse<IPhotoShowToken>;
|
||||
photosRouter.get("/photos/getShowByIDToken/:id", async (ctx) => {
|
||||
if (!ctx.state.user) {
|
||||
ctx.throw(401);
|
||||
}
|
||||
|
||||
const { id } = ctx.params as {
|
||||
id: number | undefined;
|
||||
};
|
||||
|
||||
if (!id) {
|
||||
ctx.throw(400);
|
||||
}
|
||||
|
||||
const { user } = ctx.state;
|
||||
|
||||
const photo = await Photo.findOne({ id, user });
|
||||
if (!photo || !(await photo.isUploaded())) {
|
||||
ctx.throw(404);
|
||||
return;
|
||||
}
|
||||
|
||||
const token = jwt.sign(photo.toJSON(), config.jwtSecret, {
|
||||
expiresIn: "1h",
|
||||
algorithm: "HS256",
|
||||
});
|
||||
|
||||
ctx.body = { error: false, data: token } as IPhotosGetShowTokenByID;
|
||||
});
|
||||
|
||||
export type IPhotosByIDDeleteRespBody = IAPIResponse<boolean>;
|
||||
photosRouter.delete("/photos/byID/:id", async (ctx) => {
|
||||
if (!ctx.state.user) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Photo, IPhotoJSON } from "~entity/Photo";
|
||||
import { IPhotosNewPostBody } from "~routes/photos";
|
||||
import * as fs from "fs/promises";
|
||||
import { constants as fsConstants } from "fs";
|
||||
import * as jwt from "jsonwebtoken";
|
||||
|
||||
import {
|
||||
catPath,
|
||||
@@ -20,6 +21,7 @@ import {
|
||||
seedDB,
|
||||
} from "./util";
|
||||
import { sleep } from "deasync";
|
||||
import { config } from "~config";
|
||||
|
||||
const callback = app.callback();
|
||||
|
||||
@@ -79,6 +81,50 @@ describe("photos", function () {
|
||||
);
|
||||
});
|
||||
|
||||
it("should show a photo using access token", async function () {
|
||||
const getTokenResp = await request(callback)
|
||||
.get(`/photos/getShowByIDToken/${seed.dogPhoto.id}`)
|
||||
.set({
|
||||
Authorization: `Bearer ${seed.user2.toJWT()}`,
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
expect(getTokenResp.body.error).to.be.false;
|
||||
const token = getTokenResp.body.data as string;
|
||||
|
||||
const response = await request(callback)
|
||||
.get(`/photos/showByID/${seed.dogPhoto.id}/${token}`)
|
||||
.expect(200);
|
||||
expect(parseInt(response.header["content-length"])).to.equal(
|
||||
dogFileSize,
|
||||
);
|
||||
|
||||
const tokenSelfSigned = jwt.sign(
|
||||
seed.dogPhoto.toJSON(),
|
||||
config.jwtSecret,
|
||||
{
|
||||
expiresIn: "1m",
|
||||
},
|
||||
);
|
||||
|
||||
const responseSS = await request(callback)
|
||||
.get(`/photos/showByID/${seed.dogPhoto.id}/${tokenSelfSigned}`)
|
||||
.expect(200);
|
||||
expect(parseInt(responseSS.header["content-length"])).to.equal(
|
||||
dogFileSize,
|
||||
);
|
||||
});
|
||||
|
||||
it("should not show a photo using expired access token", async function () {
|
||||
const token = jwt.sign(seed.dogPhoto.toJSON(), config.jwtSecret, {
|
||||
expiresIn: "0s",
|
||||
});
|
||||
|
||||
const response = await request(callback)
|
||||
.get(`/photos/showByID/${seed.dogPhoto.id}/${token}`)
|
||||
.expect(401);
|
||||
});
|
||||
|
||||
it("should not show a photo without jwt", async function () {
|
||||
const response = await request(callback)
|
||||
.get(`/photos/byID/${seed.dogPhoto.id}`)
|
||||
|
||||
Reference in New Issue
Block a user