mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
revert rls on old tables
This commit is contained in:
parent
d5c247a98d
commit
9e5196406e
15 changed files with 179 additions and 41 deletions
|
|
@ -49,11 +49,12 @@ export const labelRepository = entityManager.getRepository(Label).extend({
|
|||
.getOne()
|
||||
},
|
||||
|
||||
findByNames(names: string[]) {
|
||||
findByNames(names: string[], userId: string) {
|
||||
return this.createQueryBuilder()
|
||||
.where('LOWER(name) IN (:...names)', {
|
||||
names: names.map((n) => n.toLowerCase()),
|
||||
})
|
||||
.andWhere('user_id = :userId', { userId })
|
||||
.getMany()
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -794,7 +794,7 @@ export const bulkActionResolver = authorized<
|
|||
return { errorCodes: [BulkActionErrorCode.BadRequest] }
|
||||
}
|
||||
|
||||
labels = await findLabelsByIds(labelIds)
|
||||
labels = await findLabelsByIds(labelIds, uid)
|
||||
}
|
||||
|
||||
// parse query
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ export const labelsResolver = authorized<LabelsSuccess, LabelsError>(
|
|||
|
||||
const labels = await authTrx(async (tx) => {
|
||||
return tx.withRepository(labelRepository).find({
|
||||
where: {
|
||||
user: { id: uid },
|
||||
},
|
||||
order: {
|
||||
position: 'ASC',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -20,10 +20,13 @@ import { sendEmail } from '../../utils/sendEmail'
|
|||
export const recentEmailsResolver = authorized<
|
||||
RecentEmailsSuccess,
|
||||
RecentEmailsError
|
||||
>(async (_, __, { authTrx, log }) => {
|
||||
>(async (_, __, { authTrx, log, uid }) => {
|
||||
try {
|
||||
const recentEmails = await authTrx((t) =>
|
||||
t.getRepository(ReceivedEmail).find({
|
||||
where: {
|
||||
user: { id: uid },
|
||||
},
|
||||
order: { createdAt: 'DESC' },
|
||||
take: 20,
|
||||
})
|
||||
|
|
@ -50,6 +53,7 @@ export const markEmailAsItemResolver = authorized<
|
|||
const recentEmail = await authTrx((t) =>
|
||||
t.getRepository(ReceivedEmail).findOneBy({
|
||||
id: recentEmailId,
|
||||
user: { id: uid },
|
||||
type: 'non-article',
|
||||
})
|
||||
)
|
||||
|
|
@ -64,6 +68,7 @@ export const markEmailAsItemResolver = authorized<
|
|||
const newsletterEmail = await authTrx((t) =>
|
||||
t.getRepository(NewsletterEmail).findOne({
|
||||
where: {
|
||||
user: { id: uid },
|
||||
address: ILike(recentEmail.to),
|
||||
},
|
||||
relations: ['user'],
|
||||
|
|
|
|||
|
|
@ -28,13 +28,11 @@ export const setDeviceTokenResolver = authorized<
|
|||
SetDeviceTokenSuccess,
|
||||
SetDeviceTokenError,
|
||||
MutationSetDeviceTokenArgs
|
||||
>(async (_parent, { input }, { claims: { uid }, log }) => {
|
||||
log.info('setDeviceTokenResolver', input)
|
||||
|
||||
>(async (_parent, { input }, { uid, log }) => {
|
||||
const { id, token } = input
|
||||
|
||||
if (!id && !token) {
|
||||
log.info('id or token is required')
|
||||
log.error('id or token is required')
|
||||
|
||||
return {
|
||||
errorCodes: [SetDeviceTokenErrorCode.BadRequest],
|
||||
|
|
@ -44,7 +42,7 @@ export const setDeviceTokenResolver = authorized<
|
|||
try {
|
||||
// when token is null, we are deleting it
|
||||
if (!token && id) {
|
||||
const deviceToken = await findDeviceTokenById(id)
|
||||
const deviceToken = await findDeviceTokenById(id, uid)
|
||||
if (!deviceToken) {
|
||||
log.error('device token not found', id)
|
||||
|
||||
|
|
@ -107,7 +105,7 @@ export const setDeviceTokenResolver = authorized<
|
|||
token
|
||||
) {
|
||||
// duplicate token
|
||||
const deviceToken = await findDeviceTokenByToken(token)
|
||||
const deviceToken = await findDeviceTokenByToken(token, uid)
|
||||
|
||||
if (!deviceToken) {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@ export const findApiKeys = async (
|
|||
'createdAt',
|
||||
'usedAt',
|
||||
],
|
||||
where,
|
||||
where: {
|
||||
...where,
|
||||
user: { id: userId },
|
||||
},
|
||||
order: {
|
||||
usedAt: { direction: 'DESC', nulls: 'last' },
|
||||
createdAt: 'DESC',
|
||||
|
|
|
|||
|
|
@ -45,7 +45,11 @@ export const findIntegration = async (
|
|||
userId: string
|
||||
) => {
|
||||
return authTrx(
|
||||
async (t) => t.getRepository(Integration).findOneBy(where),
|
||||
async (t) =>
|
||||
t.getRepository(Integration).findOneBy({
|
||||
...where,
|
||||
user: { id: userId },
|
||||
}),
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
|
|
@ -56,7 +60,11 @@ export const findIntegrations = async (
|
|||
where?: FindOptionsWhere<Integration> | FindOptionsWhere<Integration>[]
|
||||
) => {
|
||||
return authTrx(
|
||||
async (t) => t.getRepository(Integration).find({ where }),
|
||||
async (t) =>
|
||||
t.getRepository(Integration).findBy({
|
||||
...where,
|
||||
user: { id: userId },
|
||||
}),
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ export const findOrCreateLabels = async (
|
|||
const labelRepo = tx.withRepository(labelRepository)
|
||||
// find existing labels
|
||||
const labelEntities = await labelRepo.findByNames(
|
||||
labels.map((l) => l.name)
|
||||
labels.map((l) => l.name),
|
||||
userId
|
||||
)
|
||||
|
||||
const existingLabelsInLowerCase = labelEntities.map((l) =>
|
||||
|
|
@ -99,7 +100,7 @@ export const addLabelsToLibraryItem = async (
|
|||
async (tx) => {
|
||||
const libraryItem = await tx
|
||||
.withRepository(libraryItemRepository)
|
||||
.findOneByOrFail({ id: libraryItemId })
|
||||
.findOneByOrFail({ id: libraryItemId, user: { id: userId } })
|
||||
|
||||
if (libraryItem.labels) {
|
||||
labels.push(...libraryItem.labels)
|
||||
|
|
@ -156,12 +157,20 @@ export const saveLabelsInHighlight = async (
|
|||
)
|
||||
}
|
||||
|
||||
export const findLabelsByIds = async (ids: string[]): Promise<Label[]> => {
|
||||
return authTrx(async (tx) => {
|
||||
return tx.withRepository(labelRepository).findBy({
|
||||
id: In(ids),
|
||||
})
|
||||
})
|
||||
export const findLabelsByIds = async (
|
||||
ids: string[],
|
||||
userId: string
|
||||
): Promise<Label[]> => {
|
||||
return authTrx(
|
||||
async (tx) => {
|
||||
return tx.withRepository(labelRepository).findBy({
|
||||
id: In(ids),
|
||||
user: { id: userId },
|
||||
})
|
||||
},
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
}
|
||||
|
||||
export const createLabel = async (
|
||||
|
|
@ -219,7 +228,10 @@ export const findLabelsByUserId = async (userId: string): Promise<Label[]> => {
|
|||
|
||||
export const findLabelById = async (id: string, userId: string) => {
|
||||
return authTrx(
|
||||
async (tx) => tx.withRepository(labelRepository).findOneBy({ id }),
|
||||
async (tx) =>
|
||||
tx
|
||||
.withRepository(labelRepository)
|
||||
.findOneBy({ id, user: { id: userId } }),
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
|
|
|
|||
|
|
@ -32,23 +32,27 @@ export const updateReceivedEmail = async (
|
|||
userId: string
|
||||
) => {
|
||||
return authTrx(
|
||||
(t) => t.getRepository(ReceivedEmail).update(id, { type }),
|
||||
(t) =>
|
||||
t
|
||||
.getRepository(ReceivedEmail)
|
||||
.update({ id, user: { id: userId } }, { type }),
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
}
|
||||
|
||||
export const deleteReceivedEmail = async (id: string, userId?: string) => {
|
||||
export const deleteReceivedEmail = async (id: string, userId: string) => {
|
||||
return authTrx(
|
||||
(t) => t.getRepository(ReceivedEmail).delete(id),
|
||||
(t) => t.getRepository(ReceivedEmail).delete({ id, user: { id: userId } }),
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
}
|
||||
|
||||
export const findReceivedEmailById = async (id: string, userId?: string) => {
|
||||
export const findReceivedEmailById = async (id: string, userId: string) => {
|
||||
return authTrx(
|
||||
(t) => t.getRepository(ReceivedEmail).findOneBy({ id }),
|
||||
(t) =>
|
||||
t.getRepository(ReceivedEmail).findOneBy({ id, user: { id: userId } }),
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export const createRule = async (
|
|||
): Promise<Rule> => {
|
||||
const existingRule = await authTrx((t) =>
|
||||
t.getRepository(Rule).findOneBy({
|
||||
user: { id: userId },
|
||||
name: ILike(rule.name),
|
||||
})
|
||||
)
|
||||
|
|
@ -32,11 +33,11 @@ export const createRule = async (
|
|||
)
|
||||
}
|
||||
|
||||
export const deleteRule = async (id: string, userId?: string) => {
|
||||
export const deleteRule = async (id: string, userId: string) => {
|
||||
return authTrx(
|
||||
async (t) => {
|
||||
const repo = t.getRepository(Rule)
|
||||
const rule = await repo.findOneByOrFail({ id })
|
||||
const rule = await repo.findOneByOrFail({ id, user: { id: userId } })
|
||||
await repo.delete(id)
|
||||
return rule
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,19 +6,28 @@ import { analytics } from '../utils/analytics'
|
|||
|
||||
export const findDeviceTokenById = async (
|
||||
id: string,
|
||||
userId?: string
|
||||
userId: string
|
||||
): Promise<UserDeviceToken | null> => {
|
||||
return authTrx(
|
||||
(t) => t.getRepository(UserDeviceToken).findOneBy({ id }),
|
||||
(t) =>
|
||||
t.getRepository(UserDeviceToken).findOneBy({ id, user: { id: userId } }),
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
}
|
||||
|
||||
export const findDeviceTokenByToken = async (
|
||||
token: string
|
||||
token: string,
|
||||
userId: string
|
||||
): Promise<UserDeviceToken | null> => {
|
||||
return authTrx((t) => t.getRepository(UserDeviceToken).findOneBy({ token }))
|
||||
return authTrx(
|
||||
(t) =>
|
||||
t
|
||||
.getRepository(UserDeviceToken)
|
||||
.findOneBy({ token, user: { id: userId } }),
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
}
|
||||
|
||||
export const findDeviceTokensByUserId = async (
|
||||
|
|
@ -70,7 +79,9 @@ export const deleteDeviceToken = async (
|
|||
})
|
||||
|
||||
return authTrx(async (t) => {
|
||||
const result = await t.getRepository(UserDeviceToken).delete(id)
|
||||
const result = await t
|
||||
.getRepository(UserDeviceToken)
|
||||
.delete({ id, user: { id: userId } })
|
||||
|
||||
return !!result.affected
|
||||
})
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export const createWebhook = async (
|
|||
)
|
||||
}
|
||||
|
||||
export const findWebhooks = async (userId?: string) => {
|
||||
export const findWebhooks = async (userId: string) => {
|
||||
return authTrx(
|
||||
(tx) => tx.getRepository(Webhook).findBy({ user: { id: userId } }),
|
||||
undefined,
|
||||
|
|
@ -34,19 +34,19 @@ export const findWebhooks = async (userId?: string) => {
|
|||
)
|
||||
}
|
||||
|
||||
export const findWebhookById = async (id: string, userId?: string) => {
|
||||
export const findWebhookById = async (id: string, userId: string) => {
|
||||
return authTrx(
|
||||
(tx) => tx.getRepository(Webhook).findOneBy({ id }),
|
||||
(tx) => tx.getRepository(Webhook).findOneBy({ id, user: { id: userId } }),
|
||||
undefined,
|
||||
userId
|
||||
)
|
||||
}
|
||||
|
||||
export const deleteWebhook = async (id: string, userId?: string) => {
|
||||
export const deleteWebhook = async (id: string, userId: string) => {
|
||||
return authTrx(
|
||||
async (tx) => {
|
||||
const repo = tx.getRepository(Webhook)
|
||||
const webhook = await repo.findOneByOrFail({ id })
|
||||
const webhook = await repo.findOneByOrFail({ id, user: { id: userId } })
|
||||
await repo.delete(id)
|
||||
return webhook
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,9 +3,12 @@ import 'mocha'
|
|||
import { User } from '../../src/entity/user'
|
||||
import { UserDeviceToken } from '../../src/entity/user_device_tokens'
|
||||
import { SetDeviceTokenErrorCode } from '../../src/generated/graphql'
|
||||
import { getRepository } from '../../src/repository'
|
||||
import { deleteUser } from '../../src/services/user'
|
||||
import { createDeviceToken, deleteDeviceTokens, findDeviceTokenById } from '../../src/services/user_device_tokens'
|
||||
import {
|
||||
createDeviceToken,
|
||||
deleteDeviceTokens,
|
||||
findDeviceTokenById,
|
||||
} from '../../src/services/user_device_tokens'
|
||||
import { createTestDeviceToken, createTestUser } from '../db'
|
||||
import { generateFakeUuid, graphqlRequest, request } from '../util'
|
||||
|
||||
|
|
@ -76,7 +79,8 @@ describe('Device tokens API', () => {
|
|||
it('responds with status code 200 and deletes the token', async () => {
|
||||
const response = await graphqlRequest(query, authToken).expect(200)
|
||||
const deviceToken = await findDeviceTokenById(
|
||||
response.body.data.setDeviceToken.deviceToken.id
|
||||
response.body.data.setDeviceToken.deviceToken.id,
|
||||
user.id
|
||||
)
|
||||
expect(deviceToken).to.be.null
|
||||
})
|
||||
|
|
|
|||
38
packages/db/migrations/0126.do.revert_rls.sql
Executable file
38
packages/db/migrations/0126.do.revert_rls.sql
Executable file
|
|
@ -0,0 +1,38 @@
|
|||
-- Type: DO
|
||||
-- Name: revert_rls
|
||||
-- Description: Revert rls on rows
|
||||
|
||||
BEGIN;
|
||||
|
||||
ALTER TABLE omnivore.filters DISABLE ROW LEVEL SECURITY;
|
||||
DROP POLICY filters_policy on omnivore.filters;
|
||||
|
||||
ALTER TABLE omnivore.integrations DISABLE ROW LEVEL SECURITY;
|
||||
DROP POLICY integrations_policy on omnivore.integrations;
|
||||
|
||||
DROP POLICY labels_policy on omnivore.labels;
|
||||
CREATE POLICY read_labels on omnivore.labels
|
||||
FOR SELECT TO omnivore_user
|
||||
USING (true);
|
||||
CREATE POLICY create_labels on omnivore.labels
|
||||
FOR INSERT TO omnivore_user
|
||||
WITH CHECK (true);
|
||||
|
||||
ALTER TABLE omnivore.received_emails DISABLE ROW LEVEL SECURITY;
|
||||
DROP POLICY received_emails_policy on omnivore.received_emails;
|
||||
|
||||
ALTER TABLE omnivore.rules DISABLE ROW LEVEL SECURITY;
|
||||
DROP POLICY rules_policy on omnivore.rules;
|
||||
|
||||
ALTER TABLE omnivore.webhooks DISABLE ROW LEVEL SECURITY;
|
||||
DROP POLICY webhooks_policy on omnivore.webhooks;
|
||||
|
||||
DROP POLICY user_device_tokens_policy on omnivore.user_device_tokens;
|
||||
CREATE POLICY read_user_device_tokens on omnivore.user_device_tokens
|
||||
FOR SELECT TO omnivore_user
|
||||
USING (true);
|
||||
CREATE POLICY create_user_device_tokens on omnivore.user_device_tokens
|
||||
FOR INSERT TO omnivore_user
|
||||
WITH CHECK (true);
|
||||
|
||||
COMMIT;
|
||||
50
packages/db/migrations/0126.undo.revert_rls.sql
Executable file
50
packages/db/migrations/0126.undo.revert_rls.sql
Executable file
|
|
@ -0,0 +1,50 @@
|
|||
-- Type: UNDO
|
||||
-- Name: revert_rls
|
||||
-- Description: Revert rls on rows
|
||||
|
||||
BEGIN;
|
||||
|
||||
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;
|
||||
|
||||
DROP POLICY read_labels ON omnivore.labels;
|
||||
DROP POLICY create_labels ON omnivore.labels;
|
||||
CREATE POLICY labels_policy on omnivore.labels
|
||||
USING (user_id = omnivore.get_current_user_id())
|
||||
WITH CHECK (user_id = omnivore.get_current_user_id());
|
||||
|
||||
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;
|
||||
|
||||
DROP POLICY read_user_device_tokens ON omnivore.user_device_tokens;
|
||||
DROP POLICY create_user_device_tokens ON omnivore.user_device_tokens;
|
||||
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;
|
||||
|
||||
COMMIT;
|
||||
Loading…
Reference in a new issue