Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,27 @@ describe('mongodb-atlas findOneAndUpdate', () => {
expect(result).toEqual(updatedDoc)
})

it('passes the transaction session to the permission pre-check read', async () => {
const id = new ObjectId()
const session = { id: 'txn-session' } as any
const existingDoc = { _id: id, title: 'Old', userId: 'user-1' }
const findOne = jest.fn().mockResolvedValue(existingDoc)
const findOneAndUpdate = jest.fn().mockResolvedValue(existingDoc)
const collection = { collectionName: 'todos', findOne, findOneAndUpdate }

const app = createAppWithCollection(collection)
const operators = MongoDbAtlas(app as any, {
rules: createRules(),
user: { id: 'user-1' }
})
.db('db')
.collection('todos')

await operators.findOneAndUpdate({ _id: id }, { $set: { title: 'New' } }, { session })

expect(findOne).toHaveBeenCalledWith({ $and: [{ _id: id }] }, { session })
})

it('rejects updates when write permission is denied', async () => {
const id = new ObjectId()
const existingDoc = { _id: id, title: 'Old', userId: 'user-1' }
Expand Down
31 changes: 22 additions & 9 deletions packages/flowerbase/src/services/mongodb-atlas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -881,7 +881,11 @@ const getOperators: GetOperatorsFunction = (
? normalizeQuery(formattedQuery)
: formattedQuery

const result = await collection.findOne(buildAndQuery(safeQuery))
// Read inside the active transaction so the pre-check sees writes
// made earlier in the same session (read-your-own-write).
const result = await collection.findOne(buildAndQuery(safeQuery), {
session: options?.session
})

if (!result) {
if (options?.upsert) {
Expand Down Expand Up @@ -970,7 +974,10 @@ const getOperators: GetOperatorsFunction = (
const normalizedData = Array.isArray(data)
? data
: normalizeUpdatePayload(data as Document)
const currentDoc = await collection.findOne(buildAndQuery(safeQuery))
// Read inside the active transaction (read-your-own-write).
const currentDoc = await collection.findOne(buildAndQuery(safeQuery), {
session: options?.session
})
const updatedPaths = Array.isArray(normalizedData)
? []
: getUpdatedPaths(normalizedData as Document)
Expand All @@ -991,11 +998,14 @@ const getOperators: GetOperatorsFunction = (
} else {
const [computedDoc] = Array.isArray(normalizedData)
? await collection
.aggregate([
{ $match: buildAndQuery(safeQuery) },
{ $limit: 1 },
...normalizedData
])
.aggregate(
[
{ $match: buildAndQuery(safeQuery) },
{ $limit: 1 },
...normalizedData
],
{ session: options?.session }
)
.toArray()
: [applyDocumentUpdateOperators(currentDoc, normalizedData as Document)]
docToCheck = computedDoc
Expand Down Expand Up @@ -1557,8 +1567,11 @@ const getOperators: GetOperatorsFunction = (
// Apply access control filters
const formattedQuery = getFormattedQuery(filters, query, user)

// Retrieve the document to check permissions before updating
const result = await collection.find({ $and: formattedQuery }).toArray()
// Retrieve the document to check permissions before updating.
// Read inside the active transaction (read-your-own-write).
const result = await collection
.find({ $and: formattedQuery }, { session: options?.session })
.toArray()
if (!result) {
throw new Error('Update not permitted')
}
Expand Down
Loading