From 8e3286179a460b96f625212cbb7816caa021579a Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 11 Sep 2020 13:49:43 -0700 Subject: [PATCH] API security updates. --- pages/api/account/[id].js | 22 +++++------- pages/api/account/password.js | 8 +++-- pages/api/website/[id]/active.js | 17 ++++++--- pages/api/website/[id]/events.js | 29 +++++++++------ pages/api/website/[id]/metrics.js | 29 +++++++++------ pages/api/website/[id]/pageviews.js | 35 ++++++++++-------- pages/api/website/[id]/rankings.js | 55 ++++++++++++++++------------- 7 files changed, 115 insertions(+), 80 deletions(-) diff --git a/pages/api/account/[id].js b/pages/api/account/[id].js index b8fe2245..c87f948b 100644 --- a/pages/api/account/[id].js +++ b/pages/api/account/[id].js @@ -9,24 +9,20 @@ export default async (req, res) => { const { id } = req.query; const user_id = +id; - if (req.method === 'GET') { - if (is_admin) { - const account = await getAccountById(user_id); - - return ok(res, account); - } - + if (is_admin) { return unauthorized(res); } + if (req.method === 'GET') { + const account = await getAccountById(user_id); + + return ok(res, account); + } + if (req.method === 'DELETE') { - if (is_admin) { - await deleteAccount(user_id); + await deleteAccount(user_id); - return ok(res); - } - - return unauthorized(res); + return ok(res); } return methodNotAllowed(res); diff --git a/pages/api/account/password.js b/pages/api/account/password.js index 32f87960..c9c955fa 100644 --- a/pages/api/account/password.js +++ b/pages/api/account/password.js @@ -1,14 +1,18 @@ import { getAccountById, updateAccount } from 'lib/queries'; import { useAuth } from 'lib/middleware'; -import { badRequest, methodNotAllowed, ok } from 'lib/response'; +import { badRequest, methodNotAllowed, ok, unauthorized } from 'lib/response'; import { checkPassword, hashPassword } from 'lib/crypto'; export default async (req, res) => { await useAuth(req, res); - const { user_id } = req.auth; + const { user_id, is_admin } = req.auth; const { current_password, new_password } = req.body; + if (is_admin) { + return unauthorized(res); + } + if (req.method === 'POST') { const account = await getAccountById(user_id); const valid = await checkPassword(current_password, account.password); diff --git a/pages/api/website/[id]/active.js b/pages/api/website/[id]/active.js index 06731408..492074a9 100644 --- a/pages/api/website/[id]/active.js +++ b/pages/api/website/[id]/active.js @@ -1,11 +1,18 @@ import { getActiveVisitors } from 'lib/queries'; -import { ok } from 'lib/response'; +import { methodNotAllowed, ok } from 'lib/response'; +import { useAuth } from 'lib/middleware'; export default async (req, res) => { - const { id } = req.query; - const website_id = +id; + await useAuth(req, res); - const result = await getActiveVisitors(website_id); + if (req.method === 'GET') { + const { id } = req.query; + const website_id = +id; - return ok(res, result); + const result = await getActiveVisitors(website_id); + + return ok(res, result); + } + + return methodNotAllowed(res); }; diff --git a/pages/api/website/[id]/events.js b/pages/api/website/[id]/events.js index d7ead27c..c230acd1 100644 --- a/pages/api/website/[id]/events.js +++ b/pages/api/website/[id]/events.js @@ -1,21 +1,28 @@ import moment from 'moment-timezone'; import { getEvents } from 'lib/queries'; -import { ok, badRequest } from 'lib/response'; +import { ok, badRequest, methodNotAllowed } from 'lib/response'; +import { useAuth } from 'lib/middleware'; const unitTypes = ['month', 'hour', 'day']; export default async (req, res) => { - const { id, start_at, end_at, unit, tz } = req.query; + await useAuth(req, res); - if (!moment.tz.zone(tz) || !unitTypes.includes(unit)) { - return badRequest(res); + if (req.method === 'GET') { + const { id, start_at, end_at, unit, tz } = req.query; + + if (!moment.tz.zone(tz) || !unitTypes.includes(unit)) { + return badRequest(res); + } + + const websiteId = +id; + const startDate = new Date(+start_at); + const endDate = new Date(+end_at); + + const events = await getEvents(websiteId, startDate, endDate, tz, unit); + + return ok(res, events); } - const websiteId = +id; - const startDate = new Date(+start_at); - const endDate = new Date(+end_at); - - const events = await getEvents(websiteId, startDate, endDate, tz, unit); - - return ok(res, events); + return methodNotAllowed(res); }; diff --git a/pages/api/website/[id]/metrics.js b/pages/api/website/[id]/metrics.js index 4b0d71e1..70a9d1e6 100644 --- a/pages/api/website/[id]/metrics.js +++ b/pages/api/website/[id]/metrics.js @@ -1,18 +1,25 @@ import { getMetrics } from 'lib/queries'; -import { ok } from 'lib/response'; +import { methodNotAllowed, ok } from 'lib/response'; +import { useAuth } from 'lib/middleware'; export default async (req, res) => { - const { id, start_at, end_at } = req.query; - const websiteId = +id; - const startDate = new Date(+start_at); - const endDate = new Date(+end_at); + await useAuth(req, res); - const metrics = await getMetrics(websiteId, startDate, endDate); + if (req.method === 'GET') { + const { id, start_at, end_at } = req.query; + const websiteId = +id; + const startDate = new Date(+start_at); + const endDate = new Date(+end_at); - const stats = Object.keys(metrics[0]).reduce((obj, key) => { - obj[key] = Number(metrics[0][key]) || 0; - return obj; - }, {}); + const metrics = await getMetrics(websiteId, startDate, endDate); - return ok(res, stats); + const stats = Object.keys(metrics[0]).reduce((obj, key) => { + obj[key] = Number(metrics[0][key]) || 0; + return obj; + }, {}); + + return ok(res, stats); + } + + return methodNotAllowed(res); }; diff --git a/pages/api/website/[id]/pageviews.js b/pages/api/website/[id]/pageviews.js index c90e1c6b..ff3b731e 100644 --- a/pages/api/website/[id]/pageviews.js +++ b/pages/api/website/[id]/pageviews.js @@ -1,24 +1,31 @@ import moment from 'moment-timezone'; import { getPageviews } from 'lib/queries'; -import { ok, badRequest } from 'lib/response'; +import { ok, badRequest, methodNotAllowed } from 'lib/response'; +import { useAuth } from 'lib/middleware'; const unitTypes = ['month', 'hour', 'day']; export default async (req, res) => { - const { id, start_at, end_at, unit, tz } = req.query; + await useAuth(req, res); - if (!moment.tz.zone(tz) || !unitTypes.includes(unit)) { - return badRequest(res); + if (req.method === 'GET') { + const { id, start_at, end_at, unit, tz } = req.query; + + if (!moment.tz.zone(tz) || !unitTypes.includes(unit)) { + return badRequest(res); + } + + const websiteId = +id; + const startDate = new Date(+start_at); + const endDate = new Date(+end_at); + + const [pageviews, uniques] = await Promise.all([ + getPageviews(websiteId, startDate, endDate, tz, unit, '*'), + getPageviews(websiteId, startDate, endDate, tz, unit, 'distinct session_id'), + ]); + + return ok(res, { pageviews, uniques }); } - const websiteId = +id; - const startDate = new Date(+start_at); - const endDate = new Date(+end_at); - - const [pageviews, uniques] = await Promise.all([ - getPageviews(websiteId, startDate, endDate, tz, unit, '*'), - getPageviews(websiteId, startDate, endDate, tz, unit, 'distinct session_id'), - ]); - - return ok(res, { pageviews, uniques }); + return methodNotAllowed(res); }; diff --git a/pages/api/website/[id]/rankings.js b/pages/api/website/[id]/rankings.js index 4e613d0d..158a4bc2 100644 --- a/pages/api/website/[id]/rankings.js +++ b/pages/api/website/[id]/rankings.js @@ -1,6 +1,7 @@ import { getRankings } from 'lib/queries'; -import { ok, badRequest } from 'lib/response'; -import { DOMAIN_REGEX } from '../../../../lib/constants'; +import { ok, badRequest, methodNotAllowed } from 'lib/response'; +import { DOMAIN_REGEX } from 'lib/constants'; +import { useAuth } from 'lib/middleware'; const sessionColumns = ['browser', 'os', 'device', 'country']; const pageviewColumns = ['url', 'referrer']; @@ -25,29 +26,35 @@ function getColumn(type) { } export default async (req, res) => { - const { id, type, start_at, end_at, domain } = req.query; - const websiteId = +id; - const startDate = new Date(+start_at); - const endDate = new Date(+end_at); + await useAuth(req, res); - if ( - type !== 'event' && - !sessionColumns.includes(type) && - !pageviewColumns.includes(type) && - domain && - DOMAIN_REGEX.test(domain) - ) { - return badRequest(res); + if (req.method === 'GET') { + const { id, type, start_at, end_at, domain } = req.query; + const websiteId = +id; + const startDate = new Date(+start_at); + const endDate = new Date(+end_at); + + if ( + type !== 'event' && + !sessionColumns.includes(type) && + !pageviewColumns.includes(type) && + domain && + DOMAIN_REGEX.test(domain) + ) { + return badRequest(res); + } + + const rankings = await getRankings( + websiteId, + startDate, + endDate, + getColumn(type), + getTable(type), + domain, + ); + + return ok(res, rankings); } - const rankings = await getRankings( - websiteId, - startDate, - endDate, - getColumn(type), - getTable(type), - domain, - ); - - return ok(res, rankings); + return methodNotAllowed(res); };