Skip to content
Merged
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
12 changes: 8 additions & 4 deletions packages/wabe/src/database/DatabaseController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ export class DatabaseController<T extends WabeTypes> {
return (id: string) =>
this.getObject({
className,
context: contextWithRoot(context),
context,
id,
_skipHooks: true,
})
Expand All @@ -646,7 +646,7 @@ export class DatabaseController<T extends WabeTypes> {
return ({ where, ids }: { where?: WhereType<DevWabeTypes, any>; ids: string[] }) =>
this.getObjects({
className,
context: contextWithRoot(context),
context,
// @ts-expect-error
where: where ? where : { id: { in: ids } },
_skipHooks: true,
Expand Down Expand Up @@ -1196,10 +1196,12 @@ export class DatabaseController<T extends WabeTypes> {
if (this._isEmptySelect(select as Record<string, unknown>)) return null

const selectWithoutPrivateFields = select ? selectFieldsWithoutPrivateFields(select) : undefined
const readbackContext =
String(className) === 'User' ? contextWithRoot(context as WabeContext<T>) : context

return this.getObject({
className,
context: contextWithRoot(context),
context: readbackContext,
select: selectWithoutPrivateFields,
id: res.id,
})
Expand Down Expand Up @@ -1286,10 +1288,12 @@ export class DatabaseController<T extends WabeTypes> {
if (this._isEmptySelect(select as Record<string, unknown>)) return []

const selectWithoutPrivateFields = select ? selectFieldsWithoutPrivateFields(select) : undefined
const readbackContext =
String(className) === 'User' ? contextWithRoot(context as WabeContext<T>) : context

return this.getObjects({
className,
context: contextWithRoot(context),
context: readbackContext,
select: selectWithoutPrivateFields,
// @ts-expect-error
where: { id: { in: ids } },
Expand Down
104 changes: 104 additions & 0 deletions packages/wabe/src/hooks/HookObject.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,110 @@ describe('HookObject', () => {
)
})

it('should not bypass ACL when fetching pointer/relation in hooks', async () => {
const database = wabe.controllers.database as any

const document = await database.createObject({
className: 'TestDocument',
data: { name: 'ACL protected' },
context: { wabe, isRoot: true },
select: { id: true },
})

await database.updateObject({
className: 'TestDocument',
id: document?.id,
context: { wabe, isRoot: true },
data: {
acl: {
users: [{ userId: 'allowed-user', read: true, write: true }],
roles: [],
},
},
select: {},
})

const container = await database.createObject({
className: 'TestPointerContainer',
data: { document: document?.id },
context: { wabe, isRoot: true },
select: { id: true, document: true },
})

const hookObject = new HookObject<DevWabeTypes, any>({
className: 'TestPointerContainer',
newData: {} as any,
context: {
wabe,
isRoot: false,
user: {
id: 'denied-user',
},
} as any,
operationType: OperationType.AfterCreate,
object: {
id: container?.id,
document: container?.document,
} as any,
select: {},
})

await expect(hookObject.fetchPointerOrRelation('document')).rejects.toThrow('Object not found')
})

it('should return empty relation in hooks when ACL denies linked objects', async () => {
const database = wabe.controllers.database as any

const document = await database.createObject({
className: 'TestDocument',
data: { name: 'ACL protected relation' },
context: { wabe, isRoot: true },
select: { id: true },
})

await database.updateObject({
className: 'TestDocument',
id: document?.id,
context: { wabe, isRoot: true },
data: {
acl: {
users: [{ userId: 'allowed-user', read: true, write: true }],
roles: [],
},
},
select: {},
})

const container = await database.createObject({
className: 'TestPointerContainer',
data: { documents: [document?.id] },
context: { wabe, isRoot: true },
select: { id: true, documents: true },
})

const hookObject = new HookObject<DevWabeTypes, any>({
className: 'TestPointerContainer',
newData: {} as any,
context: {
wabe,
isRoot: false,
user: {
id: 'denied-user',
},
} as any,
operationType: OperationType.AfterCreate,
object: {
id: container?.id,
documents: container?.documents,
} as any,
select: {},
})

const relation = await hookObject.fetchPointerOrRelation('documents')

expect(relation).toEqual([])
})

it('should return correctly value depends on the update state of the field', () => {
const userData = { name: 'John Doe' }

Expand Down
5 changes: 2 additions & 3 deletions packages/wabe/src/hooks/HookObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ export class HookObject<T extends WabeTypes, K extends keyof WabeTypes['types']>

const databaseController = this.context.wabe.controllers.database
const targetClassName = schemaField.class as keyof T['types']
const rootContext = contextWithRoot(this.context)
const select = options?.select as Select | undefined
const rawValue = (this.object as Record<string, any>)[fieldName]

Expand All @@ -180,7 +179,7 @@ export class HookObject<T extends WabeTypes, K extends keyof WabeTypes['types']>
? await databaseController.getObject({
className: targetClassName,
id: pointerId,
context: rootContext,
context: this.context,
select: select as any,
})
: null
Expand All @@ -193,7 +192,7 @@ export class HookObject<T extends WabeTypes, K extends keyof WabeTypes['types']>
: await databaseController.getObjects({
className: targetClassName,
where: { id: { in: relationIds } } as any,
context: rootContext,
context: this.context,
select: select as any,
})
}
Expand Down
Loading
Loading