Skip to content

Commit

Permalink
fix: field-level access is incorrectly rejected when there're no allo…
Browse files Browse the repository at this point in the history
…w rules (#1510)
  • Loading branch information
ymc9 authored Jun 14, 2024
1 parent 9c7527f commit 484b920
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 15 deletions.
19 changes: 17 additions & 2 deletions packages/schema/src/plugins/enhancer/policy/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,8 +403,23 @@ export function generateEntityCheckerFunction(
statements.push(`if (${compiled}) { return true; }`);
});

// default: deny unless for 'postUpdate'
statements.push(kind === 'postUpdate' ? 'return true;' : 'return false;');
if (kind === 'postUpdate') {
// 'postUpdate' rule defaults to allow
statements.push('return true;');
} else {
if (forField) {
// if there's no allow rule, for field-level rules, by default we allow
if (allows.length === 0) {
statements.push('return true;');
} else {
// if there's any allow rule, we deny unless any allow rule evaluates to true
statements.push(`return false;`);
}
} else {
// for other cases, defaults to deny
statements.push(`return false;`);
}
}

const func = sourceFile.addFunction({
name: `$check_${model.name}${forField ? '$' + forField.name : ''}${fieldOverride ? '$override' : ''}_${kind}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ describe('Policy: field-level policy', () => {
id Int @id @default(autoincrement())
x Int
y Int @allow('read', x > 0)
z Int @deny('read', x <= 0)
owner User @relation(fields: [ownerId], references: [id])
ownerId Int
Expand All @@ -40,92 +41,106 @@ describe('Policy: field-level policy', () => {
const db = enhance();
let r;

// y is unreadable
// y and x are unreadable

r = await db.model.create({
data: { id: 1, x: 0, y: 0, ownerId: 1 },
data: { id: 1, x: 0, y: 0, z: 0, ownerId: 1 },
});
expect(r.x).toEqual(0);
expect(r.y).toBeUndefined();
expect(r.z).toBeUndefined();

r = await db.model.findUnique({ where: { id: 1 } });
expect(r.y).toBeUndefined();
expect(r.z).toBeUndefined();

r = await db.user.findUnique({ where: { id: 1 }, select: { models: true } });
expect(r.models[0].y).toBeUndefined();
expect(r.models[0].z).toBeUndefined();

r = await db.user.findUnique({ where: { id: 1 }, select: { models: { select: { y: true } } } });
expect(r.models[0].y).toBeUndefined();
expect(r.models[0].z).toBeUndefined();

r = await db.user.findUnique({ where: { id: 1 } }).models();
expect(r[0].y).toBeUndefined();
expect(r[0].z).toBeUndefined();

r = await db.user.findUnique({ where: { id: 1 } }).models({ select: { y: true } });
expect(r[0].y).toBeUndefined();
expect(r[0].z).toBeUndefined();

r = await db.model.findUnique({ select: { x: true }, where: { id: 1 } });
expect(r.x).toEqual(0);
expect(r.y).toBeUndefined();
expect(r.z).toBeUndefined();

r = await db.model.findUnique({ select: { y: true }, where: { id: 1 } });
expect(r.x).toBeUndefined();
expect(r.y).toBeUndefined();
expect(r.z).toBeUndefined();

r = await db.model.findUnique({ select: { x: false, y: true }, where: { id: 1 } });
expect(r.x).toBeUndefined();
expect(r.y).toBeUndefined();
expect(r.z).toBeUndefined();

r = await db.model.findUnique({ select: { x: true, y: true }, where: { id: 1 } });
expect(r.x).toEqual(0);
expect(r.y).toBeUndefined();
expect(r.z).toBeUndefined();

r = await db.model.findUnique({ include: { owner: true }, where: { id: 1 } });
expect(r.x).toEqual(0);
expect(r.owner).toBeTruthy();
expect(r.y).toBeUndefined();
expect(r.z).toBeUndefined();

// y is readable

r = await db.model.create({
data: { id: 2, x: 1, y: 0, ownerId: 1 },
data: { id: 2, x: 1, y: 0, z: 0, ownerId: 1 },
});
expect(r).toEqual(expect.objectContaining({ x: 1, y: 0 }));
expect(r).toEqual(expect.objectContaining({ x: 1, y: 0, z: 0 }));

r = await db.model.findUnique({ where: { id: 2 } });
expect(r).toEqual(expect.objectContaining({ x: 1, y: 0 }));
expect(r).toEqual(expect.objectContaining({ x: 1, y: 0, z: 0 }));

r = await db.user.findUnique({ where: { id: 1 }, select: { models: { where: { id: 2 } } } });
expect(r.models[0]).toEqual(expect.objectContaining({ x: 1, y: 0 }));
expect(r.models[0]).toEqual(expect.objectContaining({ x: 1, y: 0, z: 0 }));

r = await db.user.findUnique({
where: { id: 1 },
select: { models: { where: { id: 2 }, select: { y: true } } },
select: { models: { where: { id: 2 }, select: { y: true, z: true } } },
});
expect(r.models[0]).toEqual(expect.objectContaining({ y: 0 }));
expect(r.models[0]).toEqual(expect.objectContaining({ y: 0, z: 0 }));

r = await db.user.findUnique({ where: { id: 1 } }).models({ where: { id: 2 } });
expect(r[0]).toEqual(expect.objectContaining({ x: 1, y: 0 }));
expect(r[0]).toEqual(expect.objectContaining({ x: 1, y: 0, z: 0 }));

r = await db.user.findUnique({ where: { id: 1 } }).models({ where: { id: 2 }, select: { y: true } });
expect(r[0]).toEqual(expect.objectContaining({ y: 0 }));

r = await db.model.findUnique({ select: { x: true }, where: { id: 2 } });
expect(r.x).toEqual(1);
expect(r.y).toBeUndefined();
expect(r.z).toBeUndefined();

r = await db.model.findUnique({ select: { y: true }, where: { id: 2 } });
expect(r.x).toBeUndefined();
expect(r.y).toEqual(0);
expect(r.z).toBeUndefined();

r = await db.model.findUnique({ select: { x: false, y: true }, where: { id: 2 } });
r = await db.model.findUnique({ select: { x: false, y: true, z: true }, where: { id: 2 } });
expect(r.x).toBeUndefined();
expect(r.y).toEqual(0);
expect(r.z).toEqual(0);

r = await db.model.findUnique({ select: { x: true, y: true }, where: { id: 2 } });
expect(r).toEqual(expect.objectContaining({ x: 1, y: 0 }));
r = await db.model.findUnique({ select: { x: true, y: true, z: true }, where: { id: 2 } });
expect(r).toEqual(expect.objectContaining({ x: 1, y: 0, z: 0 }));

r = await db.model.findUnique({ include: { owner: true }, where: { id: 2 } });
expect(r).toEqual(expect.objectContaining({ x: 1, y: 0 }));
expect(r).toEqual(expect.objectContaining({ x: 1, y: 0, z: 0 }));
expect(r.owner).toBeTruthy();
});

Expand Down

0 comments on commit 484b920

Please sign in to comment.