fix permission issue

This commit is contained in:
Hongbo Wu 2023-09-05 13:51:11 +08:00
parent 4a55e801b4
commit b72acf8e6f
15 changed files with 103 additions and 132 deletions

View file

@ -63,7 +63,7 @@ import { createPageSaveRequest } from '../../services/create_page_save_request'
import {
addLabelsToLibraryItem,
findLabelsByIds,
getLabelsAndCreateIfNotExist,
findOrCreateLabels,
} from '../../services/labels'
import {
createLibraryItem,
@ -340,10 +340,7 @@ export const createArticleResolver = authorized<
libraryItemToSave.archivedAt =
state === ArticleSavingRequestStatus.Archived ? new Date() : null
if (inputLabels) {
libraryItemToSave.labels = await getLabelsAndCreateIfNotExist(
inputLabels,
uid
)
libraryItemToSave.labels = await findOrCreateLabels(inputLabels, uid)
}
let libraryItemToReturn: LibraryItem
@ -862,7 +859,7 @@ export const setFavoriteArticleResolver = authorized<
return { errorCodes: [SetFavoriteArticleErrorCode.BadRequest] }
}
const labels = await getLabelsAndCreateIfNotExist([label], uid)
const labels = await findOrCreateLabels([label], uid)
// adds Favorites label to page
await addLabelsToLibraryItem(labels, id, uid)

View file

@ -29,7 +29,7 @@ import {
} from '../../generated/graphql'
import { labelRepository } from '../../repository/label'
import {
getLabelsAndCreateIfNotExist,
findOrCreateLabels,
saveLabelsInHighlight,
saveLabelsInLibraryItem,
} from '../../services/labels'
@ -127,7 +127,11 @@ export const deleteLabelResolver = authorized<
})
return {
label: deleteResult.raw as Label,
label: {
id: labelId,
name: '',
color: '',
},
}
} catch (error) {
log.error('error deleting label', error)
@ -160,7 +164,7 @@ export const setLabelsResolver = authorized<
if (labels && labels.length > 0) {
// for new clients that send label names
// create labels if they don't exist
labelsSet = await getLabelsAndCreateIfNotExist(labels, uid)
labelsSet = await findOrCreateLabels(labels, uid)
} else if (labelIds && labelIds.length > 0) {
// for old clients that send labelIds
labelsSet = await authTrx(async (tx) => {
@ -260,7 +264,7 @@ export const setLabelsForHighlightResolver = authorized<
if (labels && labels.length > 0) {
// for new clients that send label names
// create labels if they don't exist
labelsSet = await getLabelsAndCreateIfNotExist(labels, uid)
labelsSet = await findOrCreateLabels(labels, uid)
} else if (labelIds && labelIds.length > 0) {
// for old clients that send labelIds
labelsSet = await authTrx(async (tx) => {

View file

@ -1,5 +1,4 @@
import { NewsletterEmail } from '../../entity/newsletter_email'
import { User } from '../../entity/user'
import { env } from '../../env'
import {
CreateNewsletterEmailError,
@ -13,7 +12,6 @@ import {
NewsletterEmailsErrorCode,
NewsletterEmailsSuccess,
} from '../../generated/graphql'
import { getRepository } from '../../repository'
import {
createNewsletterEmail,
deleteNewsletterEmail,
@ -80,10 +78,9 @@ export const deleteNewsletterEmailResolver = authorized<
DeleteNewsletterEmailSuccess,
DeleteNewsletterEmailError,
MutationDeleteNewsletterEmailArgs
>(async (_parent, args, { claims, log }) => {
log.info('deleteNewsletterEmailResolver')
>(async (_parent, args, { authTrx, uid, log }) => {
analytics.track({
userId: claims.uid,
userId: uid,
event: 'newsletter_email_address_deleted',
properties: {
env: env.server.apiEnv,
@ -91,12 +88,14 @@ export const deleteNewsletterEmailResolver = authorized<
})
try {
const newsletterEmail = await getRepository(NewsletterEmail).findOne({
where: {
id: args.newsletterEmailId,
},
relations: ['user', 'subscriptions'],
})
const newsletterEmail = await authTrx((t) =>
t.getRepository(NewsletterEmail).findOne({
where: {
id: args.newsletterEmailId,
},
relations: ['user', 'subscriptions'],
})
)
if (!newsletterEmail) {
return {
@ -104,12 +103,6 @@ export const deleteNewsletterEmailResolver = authorized<
}
}
if (newsletterEmail.user.id !== claims.uid) {
return {
errorCodes: [DeleteNewsletterEmailErrorCode.Unauthorized],
}
}
// unsubscribe all before deleting
await unsubscribeAll(newsletterEmail)

View file

@ -11,7 +11,6 @@ import {
RecentEmailsErrorCode,
RecentEmailsSuccess,
} from '../../generated/graphql'
import { getRepository } from '../../repository'
import { updateReceivedEmail } from '../../services/received_emails'
import { saveNewsletter } from '../../services/save_newsletter_email'
import { authorized } from '../../utils/helpers'
@ -21,34 +20,20 @@ import { sendEmail } from '../../utils/sendEmail'
export const recentEmailsResolver = authorized<
RecentEmailsSuccess,
RecentEmailsError
>(async (_, __, { claims, log }) => {
log.info('Getting recent emails', {
labels: {
source: 'resolver',
resolver: 'recentEmailsResolver',
uid: claims.uid,
},
})
>(async (_, __, { authTrx, log }) => {
try {
const recentEmails = await getRepository(ReceivedEmail).find({
where: { user: { id: claims.uid } },
order: { createdAt: 'DESC' },
take: 20,
})
const recentEmails = await authTrx((t) =>
t.getRepository(ReceivedEmail).find({
order: { createdAt: 'DESC' },
take: 20,
})
)
return {
recentEmails,
}
} catch (error) {
log.error('Error getting recent emails', {
error,
labels: {
source: 'resolver',
resolver: 'recentEmailsResolver',
uid: claims.uid,
},
})
log.error('Error getting recent emails', error)
return {
errorCodes: [RecentEmailsErrorCode.BadRequest],
@ -60,22 +45,14 @@ export const markEmailAsItemResolver = authorized<
MarkEmailAsItemSuccess,
MarkEmailAsItemError,
MutationMarkEmailAsItemArgs
>(async (_, { recentEmailId }, { claims, log }) => {
log.info('Marking email as item', {
recentEmailId,
labels: {
source: 'resolver',
resolver: 'markEmailAsItemResolver',
uid: claims.uid,
},
})
>(async (_, { recentEmailId }, { authTrx, uid, log }) => {
try {
const recentEmail = await getRepository(ReceivedEmail).findOneBy({
id: recentEmailId,
user: { id: claims.uid },
type: 'non-article',
})
const recentEmail = await authTrx((t) =>
t.getRepository(ReceivedEmail).findOneBy({
id: recentEmailId,
type: 'non-article',
})
)
if (!recentEmail) {
log.info('no recent email', recentEmailId)
@ -84,13 +61,14 @@ export const markEmailAsItemResolver = authorized<
}
}
const newsletterEmail = await getRepository(NewsletterEmail).findOne({
where: {
address: ILike(recentEmail.to),
user: { id: claims.uid },
},
relations: ['user'],
})
const newsletterEmail = await authTrx((t) =>
t.getRepository(NewsletterEmail).findOne({
where: {
address: ILike(recentEmail.to),
},
relations: ['user'],
})
)
if (!newsletterEmail) {
log.info('no newsletter email for', {
id: recentEmail.id,
@ -127,7 +105,7 @@ export const markEmailAsItemResolver = authorized<
await updateReceivedEmail(recentEmail.id, 'article')
const text = `A recent email marked as a library item
by: ${claims.uid}
by: ${uid}
from: ${recentEmail.from}
subject: ${recentEmail.subject}`
@ -143,14 +121,7 @@ export const markEmailAsItemResolver = authorized<
success,
}
} catch (error) {
log.error('Error marking email as item', {
error,
labels: {
source: 'resolver',
resolver: 'markEmailAsItemResolver',
uid: claims.uid,
},
})
log.error('Error marking email as item', error)
return {
errorCodes: [MarkEmailAsItemErrorCode.BadRequest],

View file

@ -63,15 +63,14 @@ export const subscriptionsResolver = authorized<
sort?.by === SortBy.UpdatedTime ? 'lastFetchedAt' : 'createdAt'
const sortOrder = sort?.order === SortOrder.Ascending ? 'ASC' : 'DESC'
const queryBuilder = await authTrx((t) =>
t
const subscriptions = await authTrx(async (t) => {
const queryBuilder = t
.getRepository(Subscription)
.createQueryBuilder('subscription')
.leftJoinAndSelect('subscription.newsletterEmail', 'newsletterEmail')
.where({
user: { id: uid },
})
)
if (type && type == SubscriptionType.Newsletter) {
queryBuilder.andWhere({
@ -95,9 +94,10 @@ export const subscriptionsResolver = authorized<
)
}
const subscriptions = await queryBuilder
.orderBy('subscription.' + sortBy, sortOrder)
.getMany()
return queryBuilder
.orderBy('subscription.' + sortBy, sortOrder)
.getMany()
})
return {
subscriptions,

View file

@ -82,18 +82,7 @@ export const deleteWebhookResolver = authorized<
MutationDeleteWebhookArgs
>(async (_, { id }, { authTrx, uid, log }) => {
try {
const deletedWebhook = await authTrx(async (t) => {
const webhook = await t.getRepository(Webhook).findOne({
where: { id },
relations: ['user'],
})
if (!webhook) {
throw new Error('Webhook not found')
}
return t.getRepository(Webhook).remove(webhook)
})
await authTrx(async (t) => t.getRepository(Webhook).delete(id))
analytics.track({
userId: uid,
@ -105,7 +94,16 @@ export const deleteWebhookResolver = authorized<
})
return {
webhook: webhookDataToResponse(deletedWebhook),
webhook: {
id,
url: '',
eventTypes: [],
method: 'POST',
contentType: 'application/json',
enabled: false,
createdAt: new Date(),
updatedAt: new Date(),
},
}
} catch (error) {
log.error('Error deleting webhook', error)

View file

@ -8,7 +8,7 @@ import { homePageURL } from '../env'
import { RecommendationGroup, User as GraphqlUser } from '../generated/graphql'
import { entityManager, getRepository } from '../repository'
import { userDataToUser } from '../utils/helpers'
import { getLabelsAndCreateIfNotExist } from './labels'
import { findOrCreateLabels } from './labels'
import { createRule } from './rules'
export const createGroup = async (input: {
@ -226,10 +226,7 @@ export const createLabelAndRuleForGroup = async (
userId: string,
groupName: string
) => {
const labels = await getLabelsAndCreateIfNotExist(
[{ name: groupName }],
userId
)
const labels = await findOrCreateLabels([{ name: groupName }], userId)
// create a rule to add the label to all pages in the group
const addLabelPromise = createRule(userId, {

View file

@ -22,7 +22,7 @@ import { libraryItemRepository } from '../repository/library_item'
// export const labelsLoader = new DataLoader(batchGetLabelsFromLinkIds)
export const getLabelsAndCreateIfNotExist = async (
export const findOrCreateLabels = async (
labels: CreateLabelInput[],
userId: string
): Promise<Label[]> => {

View file

@ -285,7 +285,7 @@ export const findLibraryItemByUrl = async (
.leftJoinAndSelect('library_item.labels', 'labels')
.leftJoinAndSelect('library_item.highlights', 'highlights')
.where('library_item.user_id = :userId', { userId })
.andWhere('library_item.url = :url', { url })
.andWhere('library_item.original_url = :url', { url })
.getOne(),
undefined,
userId

View file

@ -56,7 +56,7 @@ export const getNewsletterEmails = async (
status: SubscriptionStatus.Active,
}
)
.where('newsletter_email.user_id = :userId', { userId })
.where('newsletter_email.user = :userId', { userId })
.orderBy('newsletter_email.createdAt', 'DESC')
.getMany()
)

View file

@ -22,8 +22,10 @@ import {
parsePreparedContent,
parseUrlMetadata,
} from '../utils/parser'
import { findOrCreateLabels } from './labels'
import { createLibraryItem } from './library_item'
import { updateReceivedEmail } from './received_emails'
import { saveSubscription } from './subscriptions'
export type SaveEmailInput = {
userId: string
@ -105,30 +107,30 @@ export const saveEmail = async (
publishedAt: validatedDate(
parseResult.parsedContent?.publishedDate ?? undefined
),
subscription: {
name: input.author,
unsubscribeMailTo: input.unsubMailTo,
unsubscribeHttpUrl: input.unsubHttpUrl,
user: { id: input.userId },
newsletterEmail: { id: input.newsletterEmailId },
icon: siteIcon,
lastFetchedAt: new Date(),
},
state: LibraryItemState.Succeeded,
siteIcon,
siteName: parseResult.parsedContent?.siteName ?? undefined,
wordCount: wordsCount(content),
labels: [
{
...newsletterLabel,
internal: true,
user: { id: input.userId },
},
],
},
input.userId
)
if (input.newsletterEmailId) {
await saveSubscription({
userId: input.userId,
name: input.author,
unsubscribeMailTo: input.unsubMailTo,
unsubscribeHttpUrl: input.unsubHttpUrl,
icon: siteIcon,
newsletterEmailId: input.newsletterEmailId,
})
}
if (newsletterLabel) {
// add newsletter label
await findOrCreateLabels([newsletterLabel], input.userId)
}
await updateReceivedEmail(input.receivedEmailId, 'article')
// create a task to update thumbnail and pre-cache all images

View file

@ -8,7 +8,7 @@ import {
} from '../generated/graphql'
import { logger } from '../utils/logger'
import { getStorageFileDetails } from '../utils/uploads'
import { getLabelsAndCreateIfNotExist } from './labels'
import { findOrCreateLabels } from './labels'
import { updateLibraryItem } from './library_item'
import { findUploadFileById, setFileUploadComplete } from './upload_file'
@ -40,7 +40,7 @@ export const saveFile = async (
input.state === ArticleSavingRequestStatus.Archived ? new Date() : null
// add labels to page
const labels = input.labels
? await getLabelsAndCreateIfNotExist(input.labels, user.id)
? await findOrCreateLabels(input.labels, user.id)
: undefined
if (input.state || input.labels) {
const updated = await updateLibraryItem(

View file

@ -30,7 +30,7 @@ import { logger } from '../utils/logger'
import { parsePreparedContent } from '../utils/parser'
import { createPageSaveRequest } from './create_page_save_request'
import { saveHighlight } from './highlights'
import { getLabelsAndCreateIfNotExist } from './labels'
import { findOrCreateLabels } from './labels'
import { createLibraryItem, updateLibraryItem } from './library_item'
// where we can use APIs to fetch their underlying content.
@ -117,14 +117,14 @@ export const savePage = async (
input.state === ArticleSavingRequestStatus.Archived ? new Date() : null
// add labels to page
itemToSave.labels = input.labels
? await getLabelsAndCreateIfNotExist(input.labels, user.id)
? await findOrCreateLabels(input.labels, user.id)
: undefined
// check if the page already exists
const existingLibraryItem = await authTrx((t) =>
t.getRepository(LibraryItem).findOne({
where: { user: { id: user.id }, originalUrl: itemToSave.originalUrl },
relations: ['subscriptions'],
relations: ['subscription'],
})
)
if (existingLibraryItem) {

View file

@ -9,7 +9,7 @@ import { sendEmail } from '../utils/sendEmail'
interface SaveSubscriptionInput {
userId: string
name: string
newsletterEmail: NewsletterEmail
newsletterEmailId: string
unsubscribeMailTo?: string
unsubscribeHttpUrl?: string
icon?: string
@ -92,7 +92,7 @@ export const getSubscriptionByName = async (
export const saveSubscription = async ({
userId,
name,
newsletterEmail,
newsletterEmailId,
unsubscribeMailTo,
unsubscribeHttpUrl,
icon,
@ -121,7 +121,7 @@ export const saveSubscription = async ({
return tx.getRepository(Subscription).save({
...subscriptionData,
name,
newsletterEmail: { id: newsletterEmail.id },
newsletterEmail: { id: newsletterEmailId },
user: { id: userId },
type: SubscriptionType.Newsletter,
})

View file

@ -8,21 +8,25 @@ ALTER TABLE omnivore.features ENABLE ROW LEVEL SECURITY;
CREATE POLICY features_policy on omnivore.features
USING (user_id = omnivore.get_current_user_id())
WITH CHECK (user_id = omnivore.get_current_user_id());
GRANT SELECT, INSERT, UPDATE ON omnivore.features TO omnivore_user;
ALTER TABLE omnivore.filters ENABLE ROW LEVEL SECURITY;
CREATE POLICY filters_policy on omnivore.filters
USING (user_id = omnivore.get_current_user_id())
WITH CHECK (user_id = omnivore.get_current_user_id());
GRANT SELECT, INSERT, UPDATE, DELETE ON omnivore.filters TO omnivore_user;
ALTER TABLE omnivore.integrations ENABLE ROW LEVEL SECURITY;
CREATE POLICY integrations_policy on omnivore.integrations
USING (user_id = omnivore.get_current_user_id())
WITH CHECK (user_id = omnivore.get_current_user_id());
GRANT SELECT, INSERT, UPDATE, DELETE ON omnivore.integrations TO omnivore_user;
ALTER TABLE omnivore.newsletter_emails ENABLE ROW LEVEL SECURITY;
CREATE POLICY newsletter_emails_policy on omnivore.newsletter_emails
USING (user_id = omnivore.get_current_user_id())
WITH CHECK (user_id = omnivore.get_current_user_id());
GRANT SELECT, INSERT, DELETE ON omnivore.newsletter_emails TO omnivore_user;
CREATE POLICY labels_policy on omnivore.labels
USING (user_id = omnivore.get_current_user_id())
@ -32,25 +36,30 @@ ALTER TABLE omnivore.received_emails ENABLE ROW LEVEL SECURITY;
CREATE POLICY received_emails_policy on omnivore.received_emails
USING (user_id = omnivore.get_current_user_id())
WITH CHECK (user_id = omnivore.get_current_user_id());
GRANT SELECT, INSERT, UPDATE ON omnivore.received_emails TO omnivore_user;
ALTER TABLE omnivore.rules ENABLE ROW LEVEL SECURITY;
CREATE POLICY rules_policy on omnivore.rules
USING (user_id = omnivore.get_current_user_id())
WITH CHECK (user_id = omnivore.get_current_user_id());
GRANT SELECT, INSERT, UPDATE, DELETE ON omnivore.rules TO omnivore_user;
ALTER TABLE omnivore.webhooks ENABLE ROW LEVEL SECURITY;
CREATE POLICY webhooks_policy on omnivore.webhooks
USING (user_id = omnivore.get_current_user_id())
WITH CHECK (user_id = omnivore.get_current_user_id());
GRANT SELECT, INSERT, UPDATE, DELETE ON omnivore.webhooks TO omnivore_user;
CREATE POLICY user_device_tokens_policy on omnivore.user_device_tokens
USING (user_id = omnivore.get_current_user_id())
WITH CHECK (user_id = omnivore.get_current_user_id());
GRANT SELECT, INSERT, DELETE ON omnivore.user_device_tokens TO omnivore_user;
ALTER TABLE omnivore.search_history ENABLE ROW LEVEL SECURITY;
CREATE POLICY search_history_policy on omnivore.search_history
USING (user_id = omnivore.get_current_user_id())
WITH CHECK (user_id = omnivore.get_current_user_id());
GRANT SELECT, INSERT, DELETE ON omnivore.search_history TO omnivore_user;
ALTER TABLE omnivore.abuse_report DROP COLUMN page_id;
ALTER TABLE omnivore.abuse_report RENAME COLUMN elastic_page_id TO library_item_id;