From d30c34deec50f9648ce993743b5481df1f6faf47 Mon Sep 17 00:00:00 2001 From: Max Leiter Date: Mon, 21 Mar 2022 17:42:37 -0700 Subject: [PATCH] Add usage of SECRET_KEY to secure API routes --- client/pages/api/raw/[id].ts | 4 +++- client/pages/api/revalidate.ts | 24 ---------------------- client/pages/mine.tsx | 14 +++++++++++-- client/pages/post/private/[id].tsx | 3 ++- server/src/lib/models/Post.ts | 2 +- server/src/routes/files.ts | 12 +++++++++-- server/src/routes/posts.ts | 31 +++++++++++++++++++++++++++-- server/src/routes/users.ts | 32 +----------------------------- 8 files changed, 58 insertions(+), 64 deletions(-) delete mode 100644 client/pages/api/revalidate.ts diff --git a/client/pages/api/raw/[id].ts b/client/pages/api/raw/[id].ts index f8f4d512..4cfe4e52 100644 --- a/client/pages/api/raw/[id].ts +++ b/client/pages/api/raw/[id].ts @@ -5,9 +5,11 @@ const getRawFile = async (req: NextApiRequest, res: NextApiResponse) => { const file = await fetch(`${process.env.API_URL}/files/raw/${id}`, { headers: { 'Accept': 'text/plain', - 'x-secret-key': process.env.SECRET_KEY || '' + 'x-secret-key': process.env.SECRET_KEY || '', + 'Authorization': `Bearer ${req.cookies['drift-token']}`, } }) + res.setHeader("Content-Type", "text/plain") res.setHeader('Cache-Control', 's-maxage=86400'); diff --git a/client/pages/api/revalidate.ts b/client/pages/api/revalidate.ts deleted file mode 100644 index 07c62794..00000000 --- a/client/pages/api/revalidate.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { NextApiRequest, NextApiResponse } from "next" - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - if (req.headers['x-secret-key'] !== process.env.SECRET_KEY) { - return res.status(401).send('Unauthorized') - } - - const { path } = req.query - - if (!path || typeof path !== 'string') { - return res.status(400).json({ - error: "Missing path" - }) - } - - try { - await res.unstable_revalidate(path) - return res.json({ revalidated: true }) - } catch (err) { - // If there was an error, Next.js will continue - // to show the last successfully generated page - return res.status(500).send('Error revalidating') - } -} diff --git a/client/pages/mine.tsx b/client/pages/mine.tsx index 9e502c20..429c9dc6 100644 --- a/client/pages/mine.tsx +++ b/client/pages/mine.tsx @@ -31,14 +31,24 @@ export const getServerSideProps: GetServerSideProps = async ({ req }) => { } } - const posts = await fetch('http://localhost:3000/server-api/users/mine', { + const posts = await fetch(process.env.API_URL + `/posts/mine`, { method: "GET", headers: { "Content-Type": "application/json", - "Authorization": `Bearer ${driftToken}` + "Authorization": `Bearer ${driftToken}`, + "x-secret-key": process.env.SECRET_KEY || '' } }) + if (!posts.ok || posts.status !== 200) { + return { + redirect: { + destination: '/', + permanent: false, + } + } + } + return { props: { posts: await posts.json(), diff --git a/client/pages/post/private/[id].tsx b/client/pages/post/private/[id].tsx index 2dfdb3c0..c9b43583 100644 --- a/client/pages/post/private/[id].tsx +++ b/client/pages/post/private/[id].tsx @@ -92,7 +92,8 @@ export const getServerSideProps: GetServerSideProps = async (context) => { method: "GET", headers: { "Content-Type": "application/json", - "Authorization": `Bearer ${driftToken}` + "Authorization": `Bearer ${driftToken}`, + "x-secret-key": process.env.SECRET_KEY || "", } }) diff --git a/server/src/lib/models/Post.ts b/server/src/lib/models/Post.ts index 4bc6dc4b..2be1b024 100644 --- a/server/src/lib/models/Post.ts +++ b/server/src/lib/models/Post.ts @@ -38,7 +38,7 @@ export class Post extends Model { @BelongsToMany(() => User, () => PostAuthor) users?: User[]; - @HasMany(() => File) + @HasMany(() => File, { constraints: false }) files?: File[]; @CreatedAt diff --git a/server/src/routes/files.ts b/server/src/routes/files.ts index 13efe551..70a43cde 100644 --- a/server/src/routes/files.ts +++ b/server/src/routes/files.ts @@ -1,6 +1,5 @@ import { Router } from 'express' import secretKey from '../lib/middleware/secret-key'; -// import { Movie } from '../models/Post' import { File } from '../lib/models/File' export const files = Router() @@ -13,7 +12,16 @@ files.get("/raw/:id", secretKey, async (req, res, next) => { }, attributes: ["title", "content"], }) - res.json(file); + + // TODO: JWT-checkraw files + if (file?.post?.visibility === "private") { + // jwt(req as UserJwtRequest, res, () => { + // res.json(file); + // }) + res.json(file); + } else { + res.json(file); + } } catch (e) { next(e); diff --git a/server/src/routes/posts.ts b/server/src/routes/posts.ts index 70f54bf6..fde52b6b 100644 --- a/server/src/routes/posts.ts +++ b/server/src/routes/posts.ts @@ -69,6 +69,35 @@ posts.get("/", secretKey, async (req, res, next) => { } }); +posts.get("/mine", jwt, secretKey, async (req: UserJwtRequest, res, next) => { + if (!req.user) { + return res.status(401).json({ error: "Unauthorized" }) + } + + try { + const user = await User.findByPk(req.user.id, { + include: [ + { + model: Post, + as: "posts", + include: [ + { + model: File, + as: "files" + } + ] + }, + ], + }) + if (!user) { + return res.status(404).json({ error: "User not found" }) + } + return res.json(user.posts?.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())) + } catch (error) { + next(error) + } +}) + posts.get("/:id", secretKey, async (req, res, next) => { try { const post = await Post.findOne({ @@ -95,7 +124,6 @@ posts.get("/:id", secretKey, async (req, res, next) => { if (post.visibility === 'public' || post?.visibility === 'unlisted') { res.json(post); } else if (post.visibility === 'private') { - console.log("here") jwt(req as UserJwtRequest, res, () => { res.json(post); }) @@ -107,4 +135,3 @@ posts.get("/:id", secretKey, async (req, res, next) => { next(e); } }); - diff --git a/server/src/routes/users.ts b/server/src/routes/users.ts index 67c793f5..aedcd1e1 100644 --- a/server/src/routes/users.ts +++ b/server/src/routes/users.ts @@ -1,9 +1,7 @@ import { Router } from 'express' // import { Movie } from '../models/Post' import { User } from '../lib/models/User' -import { File } from '../lib/models/File' -import jwt, { UserJwtRequest } from '../lib/middleware/jwt' -import { Post } from '../lib/models/Post' +import jwt from '../lib/middleware/jwt' export const users = Router() @@ -16,31 +14,3 @@ users.get('/', jwt, async (req, res, next) => { } }) -users.get("/mine", jwt, async (req: UserJwtRequest, res, next) => { - if (!req.user) { - return res.status(401).json({ error: "Unauthorized" }) - } - - try { - const user = await User.findByPk(req.user.id, { - include: [ - { - model: Post, - as: "posts", - include: [ - { - model: File, - as: "files" - } - ] - }, - ], - }) - if (!user) { - return res.status(404).json({ error: "User not found" }) - } - return res.json(user.posts?.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())) - } catch (error) { - next(error) - } -})