Check snippet read permissions in controllers

This commit is contained in:
Paweł Malak
2021-10-29 13:58:55 +02:00
parent 6fd696b440
commit addae9087a
9 changed files with 84 additions and 17 deletions

View File

@@ -0,0 +1,34 @@
import { Response, NextFunction } from 'express';
import { asyncWrapper } from '../../middleware';
import { UserInstance, UserModel } from '../../models';
import { UserInfoRequest } from '../../typescript/interfaces';
interface RequestBody {}
interface ResponseBody {
data: Omit<UserInstance, 'password'>;
}
/**
* @description Get user profile by token
* @route /api/auth/me
* @request POST
* @access Public
*/
export const getProfile = asyncWrapper(
async (
req: UserInfoRequest<RequestBody>,
res: Response<ResponseBody>,
next: NextFunction
): Promise<void> => {
const user = (await UserModel.findOne({
where: { id: req.user.id },
attributes: { exclude: ['password'] },
raw: true
})) as UserInstance;
res.status(200).json({
data: user
});
}
);

View File

@@ -2,6 +2,7 @@ import { Response, NextFunction } from 'express';
import { QueryTypes } from 'sequelize';
import { sequelize } from '../../db';
import { asyncWrapper } from '../../middleware';
import { UserInfoRequest } from '../../typescript/interfaces';
/**
* @description Count tags
@@ -10,13 +11,23 @@ import { asyncWrapper } from '../../middleware';
* @access Private
*/
export const countTags = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
async (
req: UserInfoRequest,
res: Response,
next: NextFunction
): Promise<void> => {
let where = !req.user.isAdmin
? `WHERE snippets.createdBy = ${req.user.id}`
: '';
const result = await sequelize.query(
`SELECT
COUNT(tags.name) as count,
tags.name
FROM snippets_tags
INNER JOIN tags ON snippets_tags.tag_id = tags.id
INNER JOIN snippets ON snippets_tags.snippet_id = snippets.id
${where}
GROUP BY tags.name
ORDER BY name ASC`,
{

View File

@@ -1,8 +1,8 @@
import { Response, NextFunction } from 'express';
import { asyncWrapper } from '../../middleware';
import { SnippetModel, Snippet_TagModel } from '../../models';
import { Snippet, UserInfoRequest } from '../../typescript/interfaces';
import { tagParser, createTags, ErrorResponse } from '../../utils';
import { UserInfoRequest } from '../../typescript/interfaces';
import { ErrorResponse } from '../../utils';
interface Params {
id: number;

View File

@@ -1,6 +1,7 @@
import { Request, Response, NextFunction } from 'express';
import { Response, NextFunction } from 'express';
import { asyncWrapper } from '../../middleware';
import { SnippetModel, TagModel } from '../../models';
import { UserInfoRequest } from '../../typescript/interfaces';
/**
* @description Get all snippets
@@ -8,7 +9,13 @@ import { SnippetModel, TagModel } from '../../models';
* @request GET
*/
export const getAllSnippets = asyncWrapper(
async (req: Request, res: Response, next: NextFunction): Promise<void> => {
async (
req: UserInfoRequest,
res: Response,
next: NextFunction
): Promise<void> => {
let where = req.user.isAdmin ? {} : { createdBy: req.user.id };
const snippets = await SnippetModel.findAll({
include: {
model: TagModel,
@@ -17,7 +24,8 @@ export const getAllSnippets = asyncWrapper(
through: {
attributes: []
}
}
},
where
});
const populatedSnippets = snippets.map(snippet => {

View File

@@ -1,7 +1,8 @@
import { Request, Response, NextFunction } from 'express';
import { Response, NextFunction } from 'express';
import { asyncWrapper } from '../../middleware';
import { SnippetModel, TagModel } from '../../models';
import { Op } from 'sequelize';
import { UserInfoRequest } from '../../typescript/interfaces';
interface Body {
query: string;
@@ -17,7 +18,7 @@ interface Body {
*/
export const searchSnippets = asyncWrapper(
async (
req: Request<{}, {}, Body>,
req: UserInfoRequest<Body>,
res: Response,
next: NextFunction
): Promise<void> => {
@@ -49,6 +50,9 @@ export const searchSnippets = asyncWrapper(
},
{
language: languageFilter
},
{
createdBy: req.user.id
}
]
},

View File

@@ -1,7 +1,7 @@
declare global {
namespace NodeJS {
interface ProcessEnv {
PORT: number;
PORT: string;
NODE_ENV: string;
JWT_SECRET: string;
}

View File

@@ -1,12 +1,20 @@
import { NextFunction, Request, Response } from 'express';
import { NextFunction, Response } from 'express';
import { asyncWrapper } from '.';
import { ErrorResponse } from '../utils';
import { verify } from 'jsonwebtoken';
import { Token, UserInfoRequest } from '../typescript/interfaces';
import { UserModel } from '../models';
interface Query {
token?: string;
}
export const authenticate = asyncWrapper(
async (req: UserInfoRequest, res: Response, next: NextFunction) => {
async (
req: UserInfoRequest<{}, {}, Query>,
res: Response,
next: NextFunction
) => {
let token: string | null = null;
// Check if token was provided
@@ -14,6 +22,8 @@ export const authenticate = asyncWrapper(
if (req.headers.authorization.startsWith('Bearer ')) {
token = req.headers.authorization.split(' ')[1];
}
} else if (req.query.token) {
token = req.query.token;
}
if (token) {

View File

@@ -21,7 +21,7 @@ snippetRouter
requireBody('title', 'language', 'code', 'tags'),
createSnippet
)
.get(getAllSnippets);
.get(authenticate, getAllSnippets);
snippetRouter
.route('/:id')
@@ -29,6 +29,6 @@ snippetRouter
.put(authenticate, updateSnippet)
.delete(authenticate, deleteSnippet);
snippetRouter.route('/statistics/count').get(countTags);
snippetRouter.route('/raw/:id').get(getRawCode);
snippetRouter.route('/search').post(searchSnippets);
snippetRouter.route('/statistics/count').get(authenticate, countTags);
snippetRouter.route('/raw/:id').get(authenticate, getRawCode);
snippetRouter.route('/search').post(authenticate, searchSnippets);

View File

@@ -1,7 +1,7 @@
import { Request } from 'express';
export interface UserInfoRequest<body = {}, params = {}>
extends Request<params, {}, body> {
export interface UserInfoRequest<body = {}, params = {}, query = {}>
extends Request<params, {}, body, query> {
user: {
id: number;
email: string;