agentlang 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/api/http.js +27 -19
- package/out/api/http.js.map +1 -1
- package/out/language/generated/ast.d.ts +20 -3
- package/out/language/generated/ast.d.ts.map +1 -1
- package/out/language/generated/ast.js +26 -1
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.d.ts.map +1 -1
- package/out/language/generated/grammar.js +208 -106
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/main.cjs +228 -107
- package/out/language/main.cjs.map +2 -2
- package/out/runtime/loader.d.ts.map +1 -1
- package/out/runtime/loader.js +25 -9
- package/out/runtime/loader.js.map +1 -1
- package/out/runtime/module.d.ts +1 -0
- package/out/runtime/module.d.ts.map +1 -1
- package/out/runtime/module.js +14 -0
- package/out/runtime/module.js.map +1 -1
- package/out/runtime/modules/auth.d.ts.map +1 -1
- package/out/runtime/modules/auth.js +13 -2
- package/out/runtime/modules/auth.js.map +1 -1
- package/out/runtime/resolvers/sqldb/database.d.ts +4 -2
- package/out/runtime/resolvers/sqldb/database.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/database.js +43 -4
- package/out/runtime/resolvers/sqldb/database.js.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.d.ts.map +1 -1
- package/out/runtime/resolvers/sqldb/impl.js +7 -3
- package/out/runtime/resolvers/sqldb/impl.js.map +1 -1
- package/out/runtime/util.d.ts +1 -0
- package/out/runtime/util.d.ts.map +1 -1
- package/out/runtime/util.js +11 -0
- package/out/runtime/util.js.map +1 -1
- package/package.json +1 -1
- package/src/api/http.ts +26 -17
- package/src/language/agentlang.langium +5 -1
- package/src/language/generated/ast.ts +48 -4
- package/src/language/generated/grammar.ts +208 -106
- package/src/runtime/loader.ts +23 -10
- package/src/runtime/module.ts +14 -0
- package/src/runtime/modules/auth.ts +13 -2
- package/src/runtime/resolvers/sqldb/database.ts +55 -4
- package/src/runtime/resolvers/sqldb/impl.ts +8 -7
- package/src/runtime/util.ts +12 -0
package/src/runtime/loader.ts
CHANGED
|
@@ -19,11 +19,10 @@ import {
|
|
|
19
19
|
SchemaDefinition,
|
|
20
20
|
isAgentDefinition,
|
|
21
21
|
AgentDefinition,
|
|
22
|
-
SetAttribute,
|
|
23
|
-
isLiteral,
|
|
24
22
|
isResolverDefinition,
|
|
25
23
|
ResolverDefinition,
|
|
26
24
|
ResolverMethodSpec,
|
|
25
|
+
AgentPropertyDef,
|
|
27
26
|
} from '../language/generated/ast.js';
|
|
28
27
|
import {
|
|
29
28
|
addEntity,
|
|
@@ -417,27 +416,41 @@ async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
|
|
|
417
416
|
const attrsStrs = new Array<string>();
|
|
418
417
|
attrsStrs.push(`name "${name}"`);
|
|
419
418
|
const attrs = newInstanceAttributes();
|
|
420
|
-
def.body?.attributes.forEach((
|
|
419
|
+
def.body?.attributes.forEach((apdef: AgentPropertyDef) => {
|
|
421
420
|
let v: any = undefined;
|
|
422
|
-
if (
|
|
423
|
-
v =
|
|
421
|
+
if (apdef.value.array) {
|
|
422
|
+
v = apdef.value.array.vals
|
|
423
|
+
.map((stmt: Statement) => {
|
|
424
|
+
if (stmt.pattern.literal) {
|
|
425
|
+
const s = stmt.pattern.literal.str;
|
|
426
|
+
if (s == undefined) {
|
|
427
|
+
throw new Error(`Only arrays of string-literals are to be passed to agent ${name}`);
|
|
428
|
+
}
|
|
429
|
+
return s;
|
|
430
|
+
} else {
|
|
431
|
+
throw new Error(`Invalid value in array passed to agent ${name}`);
|
|
432
|
+
}
|
|
433
|
+
})
|
|
434
|
+
.join(',');
|
|
435
|
+
} else {
|
|
436
|
+
v = apdef.value.str || apdef.value.id || apdef.value.num;
|
|
424
437
|
if (v == undefined) {
|
|
425
|
-
v =
|
|
438
|
+
v = apdef.value.bool;
|
|
426
439
|
}
|
|
427
440
|
}
|
|
428
441
|
if (v == undefined) {
|
|
429
442
|
throw new Error(`Cannot initialize agent ${name}, only literals can be set for attributes`);
|
|
430
443
|
}
|
|
431
|
-
if (llmName == undefined &&
|
|
444
|
+
if (llmName == undefined && apdef.name == 'llm') {
|
|
432
445
|
llmName = v;
|
|
433
446
|
hasUserLlm = true;
|
|
434
447
|
}
|
|
435
448
|
const ov = v;
|
|
436
|
-
if (
|
|
449
|
+
if (apdef.value.str || apdef.value.id || apdef.value.array) {
|
|
437
450
|
v = `"${v}"`;
|
|
438
451
|
}
|
|
439
|
-
attrsStrs.push(`${
|
|
440
|
-
attrs.set(
|
|
452
|
+
attrsStrs.push(`${apdef.name} ${v}`);
|
|
453
|
+
attrs.set(apdef.name, ov);
|
|
441
454
|
});
|
|
442
455
|
if (!attrs.has('llm')) {
|
|
443
456
|
llmName = `${name}_llm`;
|
package/src/runtime/module.ts
CHANGED
|
@@ -2419,3 +2419,17 @@ export function instanceToObject<Type>(inst: Instance, obj: any): Type {
|
|
|
2419
2419
|
});
|
|
2420
2420
|
return obj as Type;
|
|
2421
2421
|
}
|
|
2422
|
+
|
|
2423
|
+
export function getEntityRbacRules(entityFqName: string): RbacSpecification[] | undefined {
|
|
2424
|
+
const p = splitFqName(entityFqName);
|
|
2425
|
+
const mn = p.getModuleName();
|
|
2426
|
+
const en = p.getEntryName();
|
|
2427
|
+
const m = isModule(mn) && fetchModule(mn);
|
|
2428
|
+
if (m && m.isEntity(en)) {
|
|
2429
|
+
const entity = getEntity(en, mn);
|
|
2430
|
+
return entity.getRbacSpecifications()?.filter((spec: RbacSpecification) => {
|
|
2431
|
+
return spec.expression != undefined;
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
return undefined;
|
|
2435
|
+
}
|
|
@@ -35,7 +35,13 @@ entity User {
|
|
|
35
35
|
id UUID @id @default(uuid()),
|
|
36
36
|
email Email @unique @indexed,
|
|
37
37
|
firstName String,
|
|
38
|
-
lastName String
|
|
38
|
+
lastName String,
|
|
39
|
+
@rbac [(allow: [read, delete, update, create], where: auth.user = this.id)],
|
|
40
|
+
@after {delete AfterDeleteUser}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
workflow AfterDeleteUser {
|
|
44
|
+
{RemoveUserSession {id AfterDeleteUser.User.id}}
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
workflow CreateUser {
|
|
@@ -124,7 +130,8 @@ entity Session {
|
|
|
124
130
|
id UUID @id,
|
|
125
131
|
userId UUID @indexed,
|
|
126
132
|
authToken String @optional,
|
|
127
|
-
isActive Boolean
|
|
133
|
+
isActive Boolean,
|
|
134
|
+
@rbac [(allow: [read, delete, update, create], where: auth.user = this.userId)]
|
|
128
135
|
}
|
|
129
136
|
|
|
130
137
|
|
|
@@ -147,6 +154,10 @@ workflow RemoveSession {
|
|
|
147
154
|
purge {Session {id? RemoveSession.id}}
|
|
148
155
|
}
|
|
149
156
|
|
|
157
|
+
workflow RemoveUserSession {
|
|
158
|
+
{Session {userId? RemoveUserSession.id}} as [session];
|
|
159
|
+
purge {Session {id? session.id}}
|
|
160
|
+
}
|
|
150
161
|
|
|
151
162
|
workflow signup {
|
|
152
163
|
await Auth.signUpUser(signup.email, signup.password, signup.userData)
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
InstanceAttributes,
|
|
15
15
|
newInstanceAttributes,
|
|
16
16
|
RbacPermissionFlag,
|
|
17
|
+
RbacSpecification,
|
|
17
18
|
Relationship,
|
|
18
19
|
} from '../../module.js';
|
|
19
20
|
import pgvector from 'pgvector';
|
|
@@ -29,13 +30,15 @@ export class DbContext {
|
|
|
29
30
|
resourceFqName: string;
|
|
30
31
|
activeEnv: Environment;
|
|
31
32
|
private needAuthCheckFlag: boolean = true;
|
|
33
|
+
rbacRules: RbacSpecification[] | undefined;
|
|
32
34
|
|
|
33
35
|
constructor(
|
|
34
36
|
resourceFqName: string,
|
|
35
37
|
authInfo: ResolverAuthInfo,
|
|
36
38
|
activeEnv: Environment,
|
|
37
39
|
txnId?: string,
|
|
38
|
-
inKernelMode?: boolean
|
|
40
|
+
inKernelMode?: boolean,
|
|
41
|
+
rbacRules?: RbacSpecification[]
|
|
39
42
|
) {
|
|
40
43
|
this.resourceFqName = resourceFqName;
|
|
41
44
|
this.authInfo = authInfo;
|
|
@@ -44,6 +47,7 @@ export class DbContext {
|
|
|
44
47
|
if (inKernelMode != undefined) {
|
|
45
48
|
this.inKernelMode = inKernelMode;
|
|
46
49
|
}
|
|
50
|
+
this.rbacRules = rbacRules;
|
|
47
51
|
}
|
|
48
52
|
private static GlobalDbContext: DbContext | undefined;
|
|
49
53
|
|
|
@@ -93,6 +97,12 @@ export class DbContext {
|
|
|
93
97
|
return this;
|
|
94
98
|
}
|
|
95
99
|
|
|
100
|
+
switchAuthCheck(flag: boolean): boolean {
|
|
101
|
+
const old = this.needAuthCheckFlag;
|
|
102
|
+
this.needAuthCheckFlag = flag;
|
|
103
|
+
return old;
|
|
104
|
+
}
|
|
105
|
+
|
|
96
106
|
isPermitted(): boolean {
|
|
97
107
|
return this.inKernelMode || !this.needAuthCheckFlag;
|
|
98
108
|
}
|
|
@@ -379,8 +389,29 @@ export async function insertRows(
|
|
|
379
389
|
}
|
|
380
390
|
if (hasPerm) {
|
|
381
391
|
await insertRowsHelper(tableName, rows, ctx, doUpsert);
|
|
382
|
-
if (!
|
|
383
|
-
|
|
392
|
+
if (!doUpsert) {
|
|
393
|
+
if (!ctx.isInKernelMode()) {
|
|
394
|
+
await createOwnership(tableName, rows, ctx);
|
|
395
|
+
}
|
|
396
|
+
if (ctx.rbacRules) {
|
|
397
|
+
for (let i = 0; i < ctx.rbacRules.length; ++i) {
|
|
398
|
+
const rbacRule = ctx.rbacRules[i];
|
|
399
|
+
const e = rbacRule.expression;
|
|
400
|
+
if (e) {
|
|
401
|
+
const [selfRef, userRef] = e.lhs.startsWith('this.') ? [e.lhs, e.rhs] : [e.rhs, e.lhs];
|
|
402
|
+
if (userRef == 'auth.user') {
|
|
403
|
+
const attr = selfRef.split('.')[1];
|
|
404
|
+
for (let j = 0; j < rows.length; ++j) {
|
|
405
|
+
const r: any = rows[j];
|
|
406
|
+
const userId = r[attr];
|
|
407
|
+
if (userId) {
|
|
408
|
+
await createLimitedOwnership(tableName, [r], userId, rbacRule.permissions, ctx);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
384
415
|
}
|
|
385
416
|
} else {
|
|
386
417
|
throw new UnauthorisedError({ opr: 'insert', entity: tableName });
|
|
@@ -430,13 +461,33 @@ export async function insertBetweenRow(
|
|
|
430
461
|
|
|
431
462
|
const PathKey = PathAttributeName as keyof object;
|
|
432
463
|
|
|
464
|
+
const AllPerms = new Set<RbacPermissionFlag>()
|
|
465
|
+
.add(RbacPermissionFlag.CREATE)
|
|
466
|
+
.add(RbacPermissionFlag.READ)
|
|
467
|
+
.add(RbacPermissionFlag.UPDATE)
|
|
468
|
+
.add(RbacPermissionFlag.DELETE);
|
|
469
|
+
|
|
433
470
|
async function createOwnership(tableName: string, rows: object[], ctx: DbContext): Promise<void> {
|
|
471
|
+
await createLimitedOwnership(tableName, rows, ctx.authInfo.userId, AllPerms, ctx);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async function createLimitedOwnership(
|
|
475
|
+
tableName: string,
|
|
476
|
+
rows: object[],
|
|
477
|
+
userId: string,
|
|
478
|
+
perms: Set<RbacPermissionFlag>,
|
|
479
|
+
ctx: DbContext
|
|
480
|
+
): Promise<void> {
|
|
434
481
|
const ownerRows: object[] = [];
|
|
435
482
|
rows.forEach((r: object) => {
|
|
436
483
|
ownerRows.push({
|
|
437
484
|
id: crypto.randomUUID(),
|
|
438
485
|
path: r[PathKey],
|
|
439
|
-
user_id:
|
|
486
|
+
user_id: userId,
|
|
487
|
+
c: perms.has(RbacPermissionFlag.CREATE),
|
|
488
|
+
r: perms.has(RbacPermissionFlag.READ),
|
|
489
|
+
d: perms.has(RbacPermissionFlag.DELETE),
|
|
490
|
+
u: perms.has(RbacPermissionFlag.UPDATE),
|
|
440
491
|
});
|
|
441
492
|
});
|
|
442
493
|
const tname = ownersTable(tableName);
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getAllBetweenRelationships,
|
|
7
7
|
getAllOneToOneRelationshipsForEntity,
|
|
8
8
|
getBetweenInstanceNodeValues,
|
|
9
|
+
getEntityRbacRules,
|
|
9
10
|
Instance,
|
|
10
11
|
InstanceAttributes,
|
|
11
12
|
isBetweenRelationship,
|
|
@@ -72,7 +73,8 @@ export class SqlDbResolver extends Resolver {
|
|
|
72
73
|
this.authInfo,
|
|
73
74
|
activeEnv,
|
|
74
75
|
this.txnId,
|
|
75
|
-
activeEnv.isInKernelMode()
|
|
76
|
+
activeEnv.isInKernelMode(),
|
|
77
|
+
getEntityRbacRules(resourceFqName)
|
|
76
78
|
);
|
|
77
79
|
}
|
|
78
80
|
|
|
@@ -151,12 +153,11 @@ export class SqlDbResolver extends Resolver {
|
|
|
151
153
|
let result = SqlDbResolver.EmptyResultSet;
|
|
152
154
|
|
|
153
155
|
const tableName = asTableName(inst.moduleName, inst.name);
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
);
|
|
156
|
+
const fqName = inst.getFqName();
|
|
157
|
+
const ctx = this.getDbContext(fqName);
|
|
158
|
+
const qattrs: any = queryAll ? undefined : inst.queryAttributesAsObject();
|
|
159
|
+
const qvals: any = queryAll ? undefined : inst.queryAttributeValuesAsObject();
|
|
160
|
+
const rslt: any = await getMany(tableName, qattrs, qvals, ctx);
|
|
160
161
|
if (rslt instanceof Array) {
|
|
161
162
|
result = new Array<Instance>();
|
|
162
163
|
rslt.forEach((r: object) => {
|
package/src/runtime/util.ts
CHANGED
|
@@ -528,3 +528,15 @@ export function walkDownInstancePath(path: string): [string, string, string | un
|
|
|
528
528
|
}
|
|
529
529
|
return [moduleName, entryName, undefined, parts];
|
|
530
530
|
}
|
|
531
|
+
|
|
532
|
+
export function areSetsEqual<T>(set1: Set<T>, set2: Set<T>): boolean {
|
|
533
|
+
if (set1.size !== set2.size) {
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
for (const item of set1) {
|
|
537
|
+
if (!set2.has(item)) {
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return true;
|
|
542
|
+
}
|