Skip to content

Commit c390de1

Browse files
jasonmacdonaldJason MacDonaldymc9
authored
feat: add @@auth option for declaring auth model (#787)
Co-authored-by: Jason MacDonald <jason.macdonald@veeva.com> Co-authored-by: ymc9 <104139426+ymc9@users.noreply.github.com>
1 parent 42d654f commit c390de1

File tree

5 files changed

+428
-115
lines changed

5 files changed

+428
-115
lines changed

packages/schema/src/language-server/zmodel-linker.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {
3636
isReferenceExpr,
3737
isStringLiteral,
3838
} from '@zenstackhq/language/ast';
39-
import { getContainingModel, isFromStdlib } from '@zenstackhq/sdk';
39+
import { getContainingModel, hasAttribute, isFromStdlib } from '@zenstackhq/sdk';
4040
import {
4141
AstNode,
4242
AstNodeDescription,
@@ -278,9 +278,15 @@ export class ZModelLinker extends DefaultLinker {
278278
const model = getContainingModel(node);
279279

280280
if (model) {
281-
const userModel = getAllDeclarationsFromImports(this.langiumDocuments(), model).find(
282-
(d) => isDataModel(d) && d.name === 'User'
283-
);
281+
let userModel;
282+
userModel = getAllDeclarationsFromImports(this.langiumDocuments(), model).find((d) => {
283+
return isDataModel(d) && hasAttribute(d, '@@auth');
284+
});
285+
if (!userModel) {
286+
userModel = getAllDeclarationsFromImports(this.langiumDocuments(), model).find((d) => {
287+
return isDataModel(d) && d.name === 'User';
288+
});
289+
}
284290
if (userModel) {
285291
node.$resolvedType = { decl: userModel, nullable: true };
286292
}

packages/schema/src/res/stdlib.zmodel

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,11 @@ attribute @@deny(_ operation: String, _ condition: Boolean)
376376
*/
377377
attribute @deny(_ operation: String, _ condition: Boolean)
378378

379+
/**
380+
* Defines the model to use when when checking access policies.
381+
*/
382+
attribute @@auth()
383+
379384
/**
380385
* Indicates that the field is a password field and needs to be hashed before persistence.
381386
*

packages/schema/tests/generator/expression-writer.test.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,40 @@ describe('Expression Writer Tests', () => {
883883
);
884884
});
885885

886+
it('auth using different model check', async () => {
887+
await check(
888+
`
889+
model Membership {
890+
id String @id
891+
t Test?
892+
@@auth()
893+
}
894+
895+
model Test {
896+
id String @id
897+
owner Membership @relation(fields: [ownerId], references: [id])
898+
ownerId String @unique @allow('all', auth().id == owner.id)
899+
value Int
900+
@@allow('all', auth().id == owner.id)
901+
902+
}
903+
`,
904+
(model) => {
905+
const args = model.attributes[0].args[1];
906+
return args.value;
907+
},
908+
`((user?.id??null)==null)?{
909+
OR:[]
910+
}:{
911+
owner:{
912+
id:{
913+
equals:(user?.id??null)
914+
}
915+
}
916+
}`
917+
);
918+
});
919+
886920
it('relation field null check', async () => {
887921
await check(
888922
`
@@ -903,8 +937,7 @@ describe('Expression Writer Tests', () => {
903937
`
904938
{
905939
OR: [{ m: { is: null } }, { m: { s: { equals: null } } }]
906-
}
907-
`
940+
}`
908941
);
909942

910943
await check(

packages/sdk/src/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export function hasAttribute(decl: DataModel | DataModelField | Enum | EnumField
111111

112112
export function getAttribute(decl: DataModel | DataModelField | Enum | EnumField, name: string) {
113113
return (decl.attributes as (DataModelAttribute | DataModelFieldAttribute)[]).find(
114-
(attr) => resolved(attr.decl).name === name
114+
(attr) => attr.decl.$refText === name
115115
);
116116
}
117117

0 commit comments

Comments
 (0)