From 013b460e39de774bd773c90e2adefca20a8b0955 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Tue, 21 Mar 2023 15:56:30 +0800 Subject: [PATCH] Check the permission to access page in elasticsearch --- packages/api/src/resolvers/highlight/index.ts | 50 +++++++++++-------- packages/api/src/resolvers/reminders/index.ts | 22 ++++---- packages/api/src/routers/article_router.ts | 7 +++ packages/api/src/routers/svc/integrations.ts | 12 +++-- packages/api/src/routers/text_to_speech.ts | 31 ++++++------ 5 files changed, 74 insertions(+), 48 deletions(-) diff --git a/packages/api/src/resolvers/highlight/index.ts b/packages/api/src/resolvers/highlight/index.ts index 55c2a7879..ec5c4f787 100644 --- a/packages/api/src/resolvers/highlight/index.ts +++ b/packages/api/src/resolvers/highlight/index.ts @@ -1,7 +1,15 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { authorized, unescapeHtml } from '../../utils/helpers' +import { + addHighlightToPage, + deleteHighlight, + getHighlightById, + updateHighlight, +} from '../../elastic/highlights' +import { getPageById, updatePage } from '../../elastic/pages' +import { Highlight as HighlightData } from '../../elastic/types' +import { env } from '../../env' import { CreateHighlightError, CreateHighlightErrorCode, @@ -26,16 +34,8 @@ import { UpdateHighlightSuccess, User, } from '../../generated/graphql' -import { env } from '../../env' import { analytics } from '../../utils/analytics' -import { Highlight as HighlightData } from '../../elastic/types' -import { getPageById, updatePage } from '../../elastic/pages' -import { - addHighlightToPage, - deleteHighlight, - getHighlightById, - updateHighlight, -} from '../../elastic/highlights' +import { authorized, unescapeHtml } from '../../utils/helpers' const highlightDataToHighlight = (highlight: HighlightData): Highlight => ({ ...highlight, @@ -58,16 +58,11 @@ export const createHighlightResolver = authorized< errorCodes: [CreateHighlightErrorCode.NotFound], } } - - analytics.track({ - userId: claims.uid, - event: 'highlight_created', - properties: { - pageId, - env: env.server.apiEnv, - }, - }) - + if (page.userId !== claims.uid) { + return { + errorCodes: [CreateHighlightErrorCode.Unauthorized], + } + } if (input.annotation && input.annotation.length > 4000) { return { errorCodes: [CreateHighlightErrorCode.BadData], @@ -108,6 +103,15 @@ export const createHighlightResolver = authorized< }, }) + analytics.track({ + userId: claims.uid, + event: 'highlight_created', + properties: { + pageId, + env: env.server.apiEnv, + }, + }) + return { highlight: highlightDataToHighlight(highlight) } } catch (err) { log.error('Error creating highlight', err) @@ -130,7 +134,11 @@ export const mergeHighlightResolver = authorized< errorCodes: [MergeHighlightErrorCode.NotFound], } } - + if (page.userId !== claims.uid) { + return { + errorCodes: [MergeHighlightErrorCode.Unauthorized], + } + } const articleHighlights = page.highlights /* Compute merged annotation form the order of highlights appearing on page */ diff --git a/packages/api/src/resolvers/reminders/index.ts b/packages/api/src/resolvers/reminders/index.ts index 66bc76a71..02c5cd236 100644 --- a/packages/api/src/resolvers/reminders/index.ts +++ b/packages/api/src/resolvers/reminders/index.ts @@ -1,4 +1,7 @@ -import { authorized } from '../../utils/helpers' +import { DateTime } from 'luxon' +import { getPageById } from '../../elastic/pages' +import { Page } from '../../elastic/types' +import { env } from '../../env' import { CreateReminderError, CreateReminderErrorCode, @@ -17,14 +20,11 @@ import { UpdateReminderErrorCode, UpdateReminderSuccess, } from '../../generated/graphql' -import { deleteTask, enqueueReminder } from '../../utils/createTask' -import { analytics } from '../../utils/analytics' -import { env } from '../../env' -import { DataModels } from '../types' -import { DateTime } from 'luxon' import { setLinkArchived } from '../../services/archive_link' -import { getPageById } from '../../elastic/pages' -import { Page } from '../../elastic/types' +import { analytics } from '../../utils/analytics' +import { deleteTask, enqueueReminder } from '../../utils/createTask' +import { authorized } from '../../utils/helpers' +import { DataModels } from '../types' const validScheduleTime = (str: string): Date | undefined => { const scheduleTime = DateTime.fromISO(str, { setZone: true }).set({ @@ -166,7 +166,11 @@ export const reminderResolver = authorized< errorCodes: [ReminderErrorCode.NotFound], } } - + if (page.userId !== uid) { + return { + errorCodes: [ReminderErrorCode.Unauthorized], + } + } const reminder = await models.reminder.getCreatedByParameters(uid, { elasticPageId: page.id, }) diff --git a/packages/api/src/routers/article_router.ts b/packages/api/src/routers/article_router.ts index d3543ab0f..988a94a2b 100644 --- a/packages/api/src/routers/article_router.ts +++ b/packages/api/src/routers/article_router.ts @@ -111,6 +111,13 @@ export function articleRouter() { if (!page) { return res.status(404).send('Page not found') } + if (page.userId !== uid) { + logger.info('User is not allowed to access speech of the article', { + userId: uid, + articleId, + }) + return res.status(401).send({ errorCode: 'UNAUTHORIZED' }) + } const speechFile = htmlToSpeechFile({ title: page.title, content: page.content, diff --git a/packages/api/src/routers/svc/integrations.ts b/packages/api/src/routers/svc/integrations.ts index 108c5efc7..03ccea762 100644 --- a/packages/api/src/routers/svc/integrations.ts +++ b/packages/api/src/routers/svc/integrations.ts @@ -3,12 +3,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import express from 'express' import { EntityType, readPushSubscription } from '../../datalayer/pubsub' -import { getRepository } from '../../entity/utils' -import { Integration, IntegrationType } from '../../entity/integration' -import { buildLogger } from '../../utils/logger' -import { syncWithIntegration } from '../../services/integrations' import { getPageById, searchPages } from '../../elastic/pages' import { Page } from '../../elastic/types' +import { Integration, IntegrationType } from '../../entity/integration' +import { getRepository } from '../../entity/utils' +import { syncWithIntegration } from '../../services/integrations' +import { buildLogger } from '../../utils/logger' import { DateFilter } from '../../utils/search' export interface Message { @@ -89,6 +89,10 @@ export function integrationsServiceRouter() { res.status(200).send('No page found') return } + if (page.userId !== userId) { + logger.info('Page does not belong to user', { id, userId }) + return res.status(200).send('Page does not belong to user') + } // sync updated page with integration logger.info('syncing updated page with integration', { integrationId: integration.id, diff --git a/packages/api/src/routers/text_to_speech.ts b/packages/api/src/routers/text_to_speech.ts index 24cbcd0cf..4c86e7250 100644 --- a/packages/api/src/routers/text_to_speech.ts +++ b/packages/api/src/routers/text_to_speech.ts @@ -1,22 +1,22 @@ /* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import express from 'express' -import cors from 'cors' -import { corsConfig } from '../utils/corsConfig' -import { getRepository, setClaims } from '../entity/utils' -import { getPageById } from '../elastic/pages' -import { Speech, SpeechState } from '../entity/speech' -import { buildLogger } from '../utils/logger' -import { getClaimsByToken } from '../utils/auth' -import { shouldSynthesize } from '../services/speech' -import { readPushSubscription } from '../datalayer/pubsub' -import { AppDataSource } from '../server' -import { enqueueTextToSpeech } from '../utils/createTask' import { htmlToSpeechFile } from '@omnivore/text-to-speech-handler' -import { UserPersonalization } from '../entity/user_personalization' +import cors from 'cors' +import express from 'express' +import { readPushSubscription } from '../datalayer/pubsub' +import { getPageById } from '../elastic/pages' import { ArticleSavingRequestStatus } from '../elastic/types' +import { Speech, SpeechState } from '../entity/speech' +import { UserPersonalization } from '../entity/user_personalization' +import { getRepository, setClaims } from '../entity/utils' +import { AppDataSource } from '../server' import { FeatureName, getFeature } from '../services/features' +import { shouldSynthesize } from '../services/speech' +import { getClaimsByToken } from '../utils/auth' +import { corsConfig } from '../utils/corsConfig' +import { enqueueTextToSpeech } from '../utils/createTask' +import { buildLogger } from '../utils/logger' const DEFAULT_VOICE = 'Larry' const DEFAULT_COMPLIMENTARY_VOICE = 'Evelyn' @@ -59,7 +59,10 @@ export function textToSpeechRouter() { logger.info('No page found', { id }) return res.status(200).send('No page found') } - + if (page.userId !== userId) { + logger.info('Page does not belong to user', { id, userId }) + return res.status(200).send('Page does not belong to user') + } if (page.state === ArticleSavingRequestStatus.Processing) { logger.info('Page is still processing, try again later', { id }) return res.status(400).send('Page is still processing')