Skip to content

Commit

Permalink
chore(zql): test nested property access
Browse files Browse the repository at this point in the history
  • Loading branch information
tantaman committed Jan 3, 2025
1 parent d267ff4 commit 23d1378
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 0 deletions.
47 changes: 47 additions & 0 deletions packages/zero-cache/src/auth/read-authorizer.query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ const schema = {
type AuthData = {
sub: string;
role: string;
properties?: {
role: string;
};
};

// eslint-disable-next-line arrow-body-style
Expand Down Expand Up @@ -301,6 +304,7 @@ const permissions = must(
isMemberOfProject(authData, eb),
isIssueOwner(authData, eb),
isIssueCreator(authData, eb),
isAdminThroughNestedData(authData, eb),
);

const canSeeComment = (
Expand All @@ -312,6 +316,13 @@ const permissions = must(
authData: AuthData,
{cmpLit}: ExpressionBuilder<TableSchema>,
) => cmpLit(authData.role, '=', 'admin');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type TODO = any;
const isAdminThroughNestedData = (
authData: AuthData,
{cmpLit}: ExpressionBuilder<TableSchema>,
// TODO: proxy should return parameter references instead....
) => cmpLit(authData.properties?.role as TODO, 'IS', 'admin');

const isMemberOfProject = (
authData: AuthData,
Expand Down Expand Up @@ -1446,6 +1457,42 @@ describe('read permissions against nested paths', () => {
});
});

describe('read permissions against nested paths', () => {
beforeEach(() => {
addUser({id: 'owner-creator', name: 'Alice', role: 'user'});
addUser({id: 'project-member', name: 'Bob', role: 'user'});
addUser({id: 'not-project-member', name: 'Charlie', role: 'user'});

addIssue({
id: '001',
title: 'Issue 1',
description: 'This is the first issue',
closed: false,
ownerId: 'owner-creator',
creatorId: 'owner-creator',
projectId: '001',
});
});

test('nested property access', () => {
let actual = runReadQueryWithPermissions(
{sub: 'dne', role: '', properties: {role: 'admin'}},
newQuery(queryDelegate, schema.tables.issue),
);
expect(toIdsOnly(actual)).toEqual([
{
id: '001',
},
]);

actual = runReadQueryWithPermissions(
{sub: 'dne', role: ''},
newQuery(queryDelegate, schema.tables.issue),
);
expect(toIdsOnly(actual)).toEqual([]);
});
});

// maps over nodes, drops all information from `row` except the id
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function toIdsOnly(nodes: Node[]): any[] {
Expand Down
126 changes: 126 additions & 0 deletions packages/zero-schema/src/permissions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,129 @@ test('permission rules create query ASTs', async () => {
}
`);
});

test('nested parameters', async () => {
type AuthData = {
sub: string;
attributes: {role: 'admin' | 'user'};
};
const config = await definePermissions<AuthData, typeof schema>(
schema,
() => {
const allowIfAdmin = (
authData: AuthData,
{cmpLit}: ExpressionBuilder<TableSchema>,
) => cmpLit(authData.attributes.role, '=', 'admin');

return {
user: {
row: {
insert: [allowIfAdmin],
update: {
preMutation: [allowIfAdmin],
},
delete: [allowIfAdmin],
select: [allowIfAdmin],
},
},
};
},
);

expect(config).toMatchInlineSnapshot(`
{
"user": {
"cell": undefined,
"row": {
"delete": [
[
"allow",
{
"left": {
"anchor": "authData",
"field": [
"attributes",
"role",
],
"type": "static",
},
"op": "=",
"right": {
"type": "literal",
"value": "admin",
},
"type": "simple",
},
],
],
"insert": [
[
"allow",
{
"left": {
"anchor": "authData",
"field": [
"attributes",
"role",
],
"type": "static",
},
"op": "=",
"right": {
"type": "literal",
"value": "admin",
},
"type": "simple",
},
],
],
"select": [
[
"allow",
{
"left": {
"anchor": "authData",
"field": [
"attributes",
"role",
],
"type": "static",
},
"op": "=",
"right": {
"type": "literal",
"value": "admin",
},
"type": "simple",
},
],
],
"update": {
"postMutation": undefined,
"preMutation": [
[
"allow",
{
"left": {
"anchor": "authData",
"field": [
"attributes",
"role",
],
"type": "static",
},
"op": "=",
"right": {
"type": "literal",
"value": "admin",
},
"type": "simple",
},
],
],
},
},
},
}
`);
});
15 changes: 15 additions & 0 deletions packages/zql/src/query/query-impl.query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {AdvancedQuery} from './query-internal.js';
import type {DefaultQueryResultRow} from './query.js';
import {QueryDelegateImpl} from './test/query-delegate.js';
import {issueSchema, userSchema} from './test/testSchemas.js';
import {toStaticParam} from '../../../zero-protocol/src/ast.js';

/**
* Some basic manual tests to get us started.
Expand Down Expand Up @@ -1144,3 +1145,17 @@ test('result type initially complete', () => {
expect(rows).toEqual([]);
expect(resultType).toEqual('complete');
});

test('nested parameter access', () => {
const queryDelegate = new QueryDelegateImpl();
const issueQuery = newQuery(queryDelegate, issueSchema);
issueQuery.where('ownerId', '=', {
[toStaticParam]() {
return {
anchor: 'authData',
field: ['attributes', 'userId'],
type: 'static',
};
},
});
});

0 comments on commit 23d1378

Please sign in to comment.