@webda/core 4.0.0-beta.4 → 4.0.0-beta.6

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.
Files changed (69) hide show
  1. package/lib/application/application.d.ts.map +1 -1
  2. package/lib/application/application.js +12 -1
  3. package/lib/application/application.js.map +1 -1
  4. package/lib/bin/cli.d.ts +11 -0
  5. package/lib/bin/cli.d.ts.map +1 -1
  6. package/lib/bin/cli.js +26 -6
  7. package/lib/bin/cli.js.map +1 -1
  8. package/lib/core/core.d.ts +3 -2
  9. package/lib/core/core.d.ts.map +1 -1
  10. package/lib/core/core.js +15 -17
  11. package/lib/core/core.js.map +1 -1
  12. package/lib/core/operations.d.ts.map +1 -1
  13. package/lib/core/operations.js +13 -11
  14. package/lib/core/operations.js.map +1 -1
  15. package/lib/deployers/packager.d.ts +38 -0
  16. package/lib/deployers/packager.d.ts.map +1 -1
  17. package/lib/deployers/packager.js +70 -18
  18. package/lib/deployers/packager.js.map +1 -1
  19. package/lib/deployers/packager.service.d.ts +33 -0
  20. package/lib/deployers/packager.service.d.ts.map +1 -0
  21. package/lib/deployers/packager.service.js +112 -0
  22. package/lib/deployers/packager.service.js.map +1 -0
  23. package/lib/index.d.ts +4 -2
  24. package/lib/index.d.ts.map +1 -1
  25. package/lib/index.js +3 -1
  26. package/lib/index.js.map +1 -1
  27. package/lib/models/aclmodel.d.ts +32 -5
  28. package/lib/models/aclmodel.d.ts.map +1 -1
  29. package/lib/models/aclmodel.js +34 -9
  30. package/lib/models/aclmodel.js.map +1 -1
  31. package/lib/models/ownermodel.model.d.ts +25 -3
  32. package/lib/models/ownermodel.model.d.ts.map +1 -1
  33. package/lib/models/ownermodel.model.js +44 -9
  34. package/lib/models/ownermodel.model.js.map +1 -1
  35. package/lib/models/permissions.d.ts +247 -0
  36. package/lib/models/permissions.d.ts.map +1 -0
  37. package/lib/models/permissions.js +522 -0
  38. package/lib/models/permissions.js.map +1 -0
  39. package/lib/models/rolemodel.model.d.ts.map +1 -1
  40. package/lib/models/rolemodel.model.js +1 -2
  41. package/lib/models/rolemodel.model.js.map +1 -1
  42. package/lib/models/user.model.d.ts +12 -0
  43. package/lib/models/user.model.d.ts.map +1 -1
  44. package/lib/models/user.model.js +16 -0
  45. package/lib/models/user.model.js.map +1 -1
  46. package/lib/rest/restoperationstransport.service.d.ts.map +1 -1
  47. package/lib/rest/restoperationstransport.service.js +7 -5
  48. package/lib/rest/restoperationstransport.service.js.map +1 -1
  49. package/lib/services/audit.model.d.ts +2 -2
  50. package/lib/services/audit.model.d.ts.map +1 -1
  51. package/lib/services/audit.model.js +20 -7
  52. package/lib/services/audit.model.js.map +1 -1
  53. package/lib/services/binary.service.d.ts +42 -4
  54. package/lib/services/binary.service.d.ts.map +1 -1
  55. package/lib/services/binary.service.js +33 -13
  56. package/lib/services/binary.service.js.map +1 -1
  57. package/lib/services/domainservice.service.d.ts +74 -3
  58. package/lib/services/domainservice.service.d.ts.map +1 -1
  59. package/lib/services/domainservice.service.js +234 -57
  60. package/lib/services/domainservice.service.js.map +1 -1
  61. package/lib/services/mailer.service.d.ts.map +1 -1
  62. package/lib/services/mailer.service.js +1 -0
  63. package/lib/services/mailer.service.js.map +1 -1
  64. package/lib/stores/store.d.ts +20 -1
  65. package/lib/stores/store.d.ts.map +1 -1
  66. package/lib/stores/store.js +29 -1
  67. package/lib/stores/store.js.map +1 -1
  68. package/package.json +20 -17
  69. package/webda.module.json +92 -24
@@ -1,14 +1,37 @@
1
+ import type { IOperationContext } from "../contexts/icontext.js";
2
+ /**
3
+ * Access control entry
4
+ */
1
5
  export type Ace = {
6
+ /**
7
+ * Action the entry applies to ("get", "update", "delete", an action name...)
8
+ */
2
9
  action: string;
10
+ /**
11
+ * Kind of principal: a user id or a group name
12
+ */
3
13
  type: "GROUP" | "USER";
14
+ /**
15
+ * The user id (type USER) or group name (type GROUP) the entry applies to.
16
+ * An entry without principal matches nobody.
17
+ */
18
+ principal?: string;
19
+ /**
20
+ * Allow or deny: a matching deny wins over any allow
21
+ */
4
22
  allow: boolean;
5
23
  };
6
24
  /**
7
25
  * Allow to define ACLs for the object
8
26
  *
9
27
  * It is used as an attribute in the model so
10
- * you can add it later on to existing models
28
+ * you can add it later on to existing models; the model delegates its permissions to it:
11
29
  *
30
+ * ```ts
31
+ * async canAct(context: IOperationContext, action: string) {
32
+ * return ResourceAcl.from(this.acl ?? []).canAct(context, action);
33
+ * }
34
+ * ```
12
35
  */
13
36
  export declare class ResourceAcl extends Array<Ace> {
14
37
  /**
@@ -22,11 +45,15 @@ export declare class ResourceAcl extends Array<Ace> {
22
45
  */
23
46
  fromDto(dto: Ace[]): void;
24
47
  /**
25
- * ACLs for the object
48
+ * Check the ACL for the current caller, with the same signature as a model `canAct`
49
+ *
50
+ * An entry matches when its action is `action` and its principal is the caller id (USER) or one of the caller
51
+ * groups (GROUP). A matching deny wins, then a matching allow; with no matching entry the action is refused.
52
+ * Anonymous callers match no entry.
53
+ * @param context - the caller context
26
54
  * @param action - the action to check
27
- * @param user - the user object
28
- * @returns the result
55
+ * @returns true or the refusal reason
29
56
  */
30
- canAct(action: string, user?: any): Promise<string | boolean>;
57
+ canAct(context: IOperationContext, action: string): Promise<string | boolean>;
31
58
  }
32
59
  //# sourceMappingURL=aclmodel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"aclmodel.d.ts","sourceRoot":"","sources":["../../src/models/aclmodel.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,GAAG;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF;;;;;;GAMG;AACH,qBAAa,WAAY,SAAQ,KAAK,CAAC,GAAG,CAAC;IACzC;;;OAGG;IACH,KAAK,IAAI,GAAG,EAAE,CAEb;IAED;;;OAGG;IACH,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAKxB;IAED;;;;;OAKG;IACG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CAYlE;CACF"}
1
+ {"version":3,"file":"aclmodel.d.ts","sourceRoot":"","sources":["../../src/models/aclmodel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,GAAG,GAAG;IAChB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,qBAAa,WAAY,SAAQ,KAAK,CAAC,GAAG,CAAC;IACzC;;;OAGG;IACH,KAAK,IAAI,GAAG,EAAE,CAEb;IAED;;;OAGG;IACH,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAKxB;IAED;;;;;;;;;OASG;IACG,MAAM,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CA4BlF;CACF"}
@@ -2,8 +2,13 @@
2
2
  * Allow to define ACLs for the object
3
3
  *
4
4
  * It is used as an attribute in the model so
5
- * you can add it later on to existing models
5
+ * you can add it later on to existing models; the model delegates its permissions to it:
6
6
  *
7
+ * ```ts
8
+ * async canAct(context: IOperationContext, action: string) {
9
+ * return ResourceAcl.from(this.acl ?? []).canAct(context, action);
10
+ * }
11
+ * ```
7
12
  */
8
13
  export class ResourceAcl extends Array {
9
14
  /**
@@ -24,19 +29,39 @@ export class ResourceAcl extends Array {
24
29
  }
25
30
  }
26
31
  /**
27
- * ACLs for the object
32
+ * Check the ACL for the current caller, with the same signature as a model `canAct`
33
+ *
34
+ * An entry matches when its action is `action` and its principal is the caller id (USER) or one of the caller
35
+ * groups (GROUP). A matching deny wins, then a matching allow; with no matching entry the action is refused.
36
+ * Anonymous callers match no entry.
37
+ * @param context - the caller context
28
38
  * @param action - the action to check
29
- * @param user - the user object
30
- * @returns the result
39
+ * @returns true or the refusal reason
31
40
  */
32
- async canAct(action, user) {
33
- for (const ace of this.filter(a => !a.allow)) {
34
- if (ace.action === action) {
41
+ async canAct(context, action) {
42
+ const userId = context?.getCurrentUserId();
43
+ if (!userId) {
44
+ return "no matching ACE in resource ACL";
45
+ }
46
+ const entries = this.filter(a => a.action === action && a.principal);
47
+ let groups;
48
+ const matches = async (ace) => {
49
+ if (ace.type === "USER") {
50
+ return ace.principal === userId;
51
+ }
52
+ if (ace.type === "GROUP") {
53
+ groups ??= (await context.getCurrentUser())?.getGroups?.() ?? [];
54
+ return groups.includes(ace.principal);
55
+ }
56
+ return false;
57
+ };
58
+ for (const ace of entries.filter(a => !a.allow)) {
59
+ if (await matches(ace)) {
35
60
  return "explicitly denied by resource ACL";
36
61
  }
37
62
  }
38
- for (const ace of this.filter(a => a.allow)) {
39
- if (ace.action === action) {
63
+ for (const ace of entries.filter(a => a.allow)) {
64
+ if (await matches(ace)) {
40
65
  return true;
41
66
  }
42
67
  }
@@ -1 +1 @@
1
- {"version":3,"file":"aclmodel.js","sourceRoot":"","sources":["../../src/models/aclmodel.ts"],"names":[],"mappings":"AAMA;;;;;;GAMG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAU;IACzC;;;OAGG;IACH,KAAK;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,GAAU;QAChB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CAAC,MAAc,EAAE,IAAU;QACrC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7C,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC1B,OAAO,mCAAmC,CAAC;YAC7C,CAAC;QACH,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC1B,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,iCAAiC,CAAC;IAC3C,CAAC;CACF"}
1
+ {"version":3,"file":"aclmodel.js","sourceRoot":"","sources":["../../src/models/aclmodel.ts"],"names":[],"mappings":"AAyBA;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAU;IACzC;;;OAGG;IACH,KAAK;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,GAAU;QAChB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,GAAG,IAAI,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,MAAM,CAAC,OAA0B,EAAE,MAAc;QACrD,MAAM,MAAM,GAAG,OAAO,EAAE,gBAAgB,EAAE,CAAC;QAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,iCAAiC,CAAC;QAC3C,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC;QACrE,IAAI,MAA4B,CAAC;QACjC,MAAM,OAAO,GAAG,KAAK,EAAE,GAAQ,EAAoB,EAAE;YACnD,IAAI,GAAG,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACxB,OAAO,GAAG,CAAC,SAAS,KAAK,MAAM,CAAC;YAClC,CAAC;YACD,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBACzB,MAAM,KAAK,CAAC,MAAM,OAAO,CAAC,cAAc,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC;gBACjE,OAAO,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACxC,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;QACF,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAChD,IAAI,MAAM,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvB,OAAO,mCAAmC,CAAC;YAC7C,CAAC;QACH,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/C,IAAI,MAAM,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvB,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,iCAAiC,CAAC;IAC3C,CAAC;CACF"}
@@ -4,8 +4,8 @@ import { IOperationContext } from "../contexts/icontext.js";
4
4
  /**
5
5
  * Abstract class to define an object with an owner
6
6
  *
7
- * The owner is the user that created the object
8
- * The owner can be changed by the owner
7
+ * The owner is the user that created the object, set from the caller on create.
8
+ * The owner is never taken from client input.
9
9
  */
10
10
  export declare abstract class AbstractOwnerModel<T extends User> extends UuidModel {
11
11
  /**
@@ -23,6 +23,17 @@ export declare abstract class AbstractOwnerModel<T extends User> extends UuidMod
23
23
  * @returns
24
24
  */
25
25
  abstract getOwnerModel(): ModelClass<T>;
26
+ /**
27
+ * Attributes never taken from client input by the DomainService (REST, gRPC, MCP) or GraphQL
28
+ * @returns the attribute names
29
+ */
30
+ static getProtectedAttributes(): string[];
31
+ /**
32
+ * Called by the DomainService (and GraphQL) on a new object built from client input, before the
33
+ * `canAct(context, "create")` check and the save: the caller becomes the owner
34
+ * @param context - the caller context
35
+ */
36
+ prepareCreate(context: IOperationContext): void;
26
37
  /**
27
38
  * Set object owner
28
39
  * @param uuid - the unique identifier
@@ -43,7 +54,18 @@ export declare abstract class AbstractOwnerModel<T extends User> extends UuidMod
43
54
  */
44
55
  canAct(context: IOperationContext, action: string): Promise<string | boolean>;
45
56
  /**
46
- * Return a query to filter OwnerModel
57
+ * Whether an action is a binary read (a behavior action of Binary/Binaries): `avatar.download`, `photos.get`...
58
+ * @param action - the action name
59
+ * @returns true for the read actions of binary attributes
60
+ */
61
+ static isBinaryRead(action: string): boolean;
62
+ /**
63
+ * Return a query to filter OwnerModel: the objects of the caller and the public ones
64
+ *
65
+ * The user id is bound as an escaped WebdaQL value, it can never change the query structure.
66
+ *
67
+ * Subclasses inherit this filter even when they override `canAct` (it fails closed: it can only hide rows). A
68
+ * subclass whose `canAct` is more permissive overrides this method (returning `null` disables the store filter).
47
69
  *
48
70
  * @param context - the execution context
49
71
  * @returns the result
@@ -1 +1 @@
1
- {"version":3,"file":"ownermodel.model.d.ts","sourceRoot":"","sources":["../../src/models/ownermodel.model.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D;;;;;GAKG;AACH,8BAAsB,kBAAkB,CAAC,CAAC,SAAS,IAAI,CAAE,SAAQ,SAAS;IACxE;;OAEG;IACH,IAAI,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,CAExB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,EAU7D;IACD;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,QAAQ,CAAC,aAAa,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAExC;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI,CAGtC;IAED;;;;;OAKG;IACH,QAAQ,IAAI,SAAS,CAAC,CAAC,CAAC,CAEvB;IAED;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CAalF;IAED;;;;;OAKG;IACH,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAQjG;IAED,gFAAgF;IAChF,MAAM,IAAI,GAAG,CAQZ;CACF;AAED;;GAEG;AACH,qBAAa,UAAW,SAAQ,kBAAkB,CAAC,IAAI,CAAC;IACtD;;;OAGG;IACH,aAAa,IAAI,UAAU,CAAC,IAAI,CAAC,CAEhC;CACF"}
1
+ {"version":3,"file":"ownermodel.model.d.ts","sourceRoot":"","sources":["../../src/models/ownermodel.model.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D;;;;;GAKG;AACH,8BAAsB,kBAAkB,CAAC,CAAC,SAAS,IAAI,CAAE,SAAQ,SAAS;IACxE;;OAEG;IACH,IAAI,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,CAExB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,EAU7D;IACD;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,QAAQ,CAAC,aAAa,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;IAExC;;;OAGG;IACH,MAAM,CAAC,sBAAsB,IAAI,MAAM,EAAE,CAExC;IAED;;;;OAIG;IACH,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAQ9C;IAED;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,IAAI,CAGtC;IAED;;;;;OAKG;IACH,QAAQ,IAAI,SAAS,CAAC,CAAC,CAAC,CAEvB;IAED;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CAWlF;IAED;;;;OAIG;IACH,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE3C;IAED;;;;;;;;;;OAUG;IACH,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CASjG;IAED,gFAAgF;IAChF,MAAM,IAAI,GAAG,CAQZ;CACF;AAED;;GAEG;AACH,qBAAa,UAAW,SAAQ,kBAAkB,CAAC,IAAI,CAAC;IACtD;;;OAGG;IACH,aAAa,IAAI,UAAU,CAAC,IAAI,CAAC,CAEhC;CACF"}
@@ -2,11 +2,12 @@ import { WEBDA_STORAGE } from "@webda/models";
2
2
  //import { UuidModel } from "./uuid.js";
3
3
  import { ModelLink, UuidModel } from "@webda/models";
4
4
  import { User } from "./user.model.js";
5
+ import { bind } from "@webda/ql";
5
6
  /**
6
7
  * Abstract class to define an object with an owner
7
8
  *
8
- * The owner is the user that created the object
9
- * The owner can be changed by the owner
9
+ * The owner is the user that created the object, set from the caller on create.
10
+ * The owner is never taken from client input.
10
11
  */
11
12
  export class AbstractOwnerModel extends UuidModel {
12
13
  /**
@@ -33,6 +34,28 @@ export class AbstractOwnerModel extends UuidModel {
33
34
  * @default false
34
35
  */
35
36
  public;
37
+ /**
38
+ * Attributes never taken from client input by the DomainService (REST, gRPC, MCP) or GraphQL
39
+ * @returns the attribute names
40
+ */
41
+ static getProtectedAttributes() {
42
+ return ["_user"];
43
+ }
44
+ /**
45
+ * Called by the DomainService (and GraphQL) on a new object built from client input, before the
46
+ * `canAct(context, "create")` check and the save: the caller becomes the owner
47
+ * @param context - the caller context
48
+ */
49
+ prepareCreate(context) {
50
+ const userId = context?.getCurrentUserId();
51
+ if (userId) {
52
+ this.setOwner(userId);
53
+ }
54
+ else {
55
+ // Never keep an owner the caller did not prove
56
+ this._user = undefined;
57
+ }
58
+ }
36
59
  /**
37
60
  * Set object owner
38
61
  * @param uuid - the unique identifier
@@ -57,8 +80,8 @@ export class AbstractOwnerModel extends UuidModel {
57
80
  * @returns the result
58
81
  */
59
82
  async canAct(context, action) {
60
- // Object is public
61
- if (this.public && (action === "get" || action === "get_binary")) {
83
+ // Object is public: readable, binaries included (`<attribute>.download`, `.downloadUrl`, `.get`, `.getUrl`)
84
+ if (this.public && (action === "get" || AbstractOwnerModel.isBinaryRead(action))) {
62
85
  return true;
63
86
  }
64
87
  else if (!context.getCurrentUserId()) {
@@ -67,13 +90,24 @@ export class AbstractOwnerModel extends UuidModel {
67
90
  else if (!this.getOwner() && action !== "create") {
68
91
  return "Object does not have an owner";
69
92
  }
70
- if (action === "create") {
71
- //this.setOwner(Uuid.parse(ctx.getCurrentUserId(), this.getOwnerModel()));
72
- }
93
+ // On create, the owner was set from the caller by prepareCreate: only the owner may create its objects
73
94
  return context.getCurrentUserId() === this.getOwner()?.toString();
74
95
  }
75
96
  /**
76
- * Return a query to filter OwnerModel
97
+ * Whether an action is a binary read (a behavior action of Binary/Binaries): `avatar.download`, `photos.get`...
98
+ * @param action - the action name
99
+ * @returns true for the read actions of binary attributes
100
+ */
101
+ static isBinaryRead(action) {
102
+ return /^[^.]+\.(download|downloadUrl|get|getUrl)$/.test(action);
103
+ }
104
+ /**
105
+ * Return a query to filter OwnerModel: the objects of the caller and the public ones
106
+ *
107
+ * The user id is bound as an escaped WebdaQL value, it can never change the query structure.
108
+ *
109
+ * Subclasses inherit this filter even when they override `canAct` (it fails closed: it can only hide rows). A
110
+ * subclass whose `canAct` is more permissive overrides this method (returning `null` disables the store filter).
77
111
  *
78
112
  * @param context - the execution context
79
113
  * @returns the result
@@ -82,8 +116,9 @@ export class AbstractOwnerModel extends UuidModel {
82
116
  if (!context) {
83
117
  return null;
84
118
  }
119
+ const userId = context.getCurrentUserId();
85
120
  return {
86
- query: `_user = '${context.getCurrentUserId()}' OR public = TRUE`,
121
+ query: userId ? bind("_user = ? OR public = TRUE", [userId]) : "public = TRUE",
87
122
  partial: false
88
123
  };
89
124
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ownermodel.model.js","sourceRoot":"","sources":["../../src/models/ownermodel.model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ownermodel.model.js","sourceRoot":"","sources":["../../src/models/ownermodel.model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,wCAAwC;AACxC,OAAO,EAAmB,SAAS,EAAuB,SAAS,EAAE,MAAM,eAAe,CAAC;AAC3F,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,OAAgB,kBAAmC,SAAQ,SAAS;IACxE;;OAEG;IACH,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,KAAK,CAAC,KAAoD;QAC5D,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;QACvC,CAAC;aAAM,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;YACzB,MAAM,IAAI,GAAiB,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;YAC/E,IAAI,CAAC,GAAG,CAAC,KAAY,CAAC,CAAC;YACvB,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;QACvC,CAAC;IACH,CAAC;IACD;;;OAGG;IACH,MAAM,CAAW;IAQjB;;;OAGG;IACH,MAAM,CAAC,sBAAsB;QAC3B,OAAO,CAAC,OAAO,CAAC,CAAC;IACnB,CAAC;IAED;;;;OAIG;IACH,aAAa,CAAC,OAA0B;QACtC,MAAM,MAAM,GAAG,OAAO,EAAE,gBAAgB,EAAE,CAAC;QAC3C,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,QAAQ,CAAC,MAAa,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,+CAA+C;YAC/C,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;QACzB,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,QAAQ,CAAC,IAAuB;QAC9B,IAAI,CAAC,KAAK,KAAK,IAAI,SAAS,CAAI,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED;;;;;OAKG;IACH,QAAQ;QACN,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,MAAM,CAAC,OAA0B,EAAE,MAAc;QACrD,4GAA4G;QAC5G,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,KAAK,KAAK,IAAI,kBAAkB,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YACjF,OAAO,IAAI,CAAC;QACd,CAAC;aAAM,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;YACvC,OAAO,gDAAgD,CAAC;QAC1D,CAAC;aAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YACnD,OAAO,+BAA+B,CAAC;QACzC,CAAC;QACD,uGAAuG;QACvG,OAAO,OAAO,CAAC,gBAAgB,EAAE,KAAK,IAAI,CAAC,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC;IACpE,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,YAAY,CAAC,MAAc;QAChC,OAAO,4CAA4C,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnE,CAAC;IAED;;;;;;;;;;OAUG;IACH,MAAM,CAAC,kBAAkB,CAAC,OAA2B;QACnD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;QAC1C,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,4BAA4B,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe;YAC9E,OAAO,EAAE,KAAK;SACf,CAAC;IACJ,CAAC;IAED,gFAAgF;IAChF,MAAM;QACJ,MAAM,MAAM,GAAS,KAAK,CAAC,MAAkB,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpE,MAAM,MAAM,GAAQ,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;QAClD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,CAAC,GAAG,CAAC,GAAI,IAAY,CAAC,GAAG,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,MAAM,EAAG,IAAY,CAAC,aAAa,CAAC,CAAC,CAAC;QACpD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,kBAAwB;IACtD;;;OAGG;IACH,aAAa;QACX,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -0,0 +1,247 @@
1
+ import { QueryValidator } from "@webda/ql";
2
+ import type { IOperationContext } from "../contexts/icontext.js";
3
+ /**
4
+ * Result of a model's static `getPermissionQuery(context)`
5
+ */
6
+ export type PermissionQuery = {
7
+ /**
8
+ * WebdaQL filter ANDed with the client query (filter only: no ORDER BY, LIMIT or OFFSET)
9
+ */
10
+ query: string;
11
+ /**
12
+ * The filter only narrows the results: `canAct(context, "get")` decides for each of them
13
+ */
14
+ partial: boolean;
15
+ };
16
+ /**
17
+ * Whether a model class defines a static `canAct` of its own (not the base one)
18
+ * @param model - the model class
19
+ * @returns true when the static form is overridden
20
+ */
21
+ export declare function hasStaticPermissionCheck(model: any): boolean;
22
+ /**
23
+ * Whether a model class defines a permission check at all: a static `canAct` of its own (not the base one), or an
24
+ * instance `canAct`. A model with neither is refused on every transport. Subclasses with their own check do not
25
+ * open a class that has none: an object reached through a class must be allowed by that class too
26
+ * @param model - the model class
27
+ * @returns true when the model defines one of the two forms
28
+ */
29
+ export declare function hasModelPermissionCheck(model: any): boolean;
30
+ /**
31
+ * Ask the model whether the current caller may perform `action` on an object
32
+ *
33
+ * The question goes through the static `canAct(context, action, object)` of the model class (see
34
+ * {@link Model.canAct}): the base implementation delegates to the object's instance `canAct`, and denies when there
35
+ * is none. `true` allows; anything else (`false`, a refusal reason string, `undefined`...) refuses, and so does a
36
+ * `canAct` that throws.
37
+ *
38
+ * @param object - the model instance (the new, unsaved object on create)
39
+ * @param context - the caller context
40
+ * @param action - the action name ("get", "create", "update", "delete", an action name or "attribute.action")
41
+ * @param model - the model class (defaults to the object's class)
42
+ * @returns true when allowed, otherwise the refusal (a reason string or false)
43
+ */
44
+ export declare function getModelPermission(object: any, context: IOperationContext, action: string, model?: any): Promise<any>;
45
+ /**
46
+ * Whether the current caller may perform `action` on a model instance (see {@link checkModelPermission}); it never
47
+ * throws
48
+ * @param object - the model instance
49
+ * @param context - the caller context
50
+ * @param action - the action name
51
+ * @param model - the model class (defaults to the object's class)
52
+ * @returns true when allowed
53
+ */
54
+ export declare function isModelActionAllowed(object: any, context: IOperationContext, action: string, model?: any): Promise<boolean>;
55
+ /**
56
+ * Message of the NotFound error for a missing object, also used for an object the caller may not read
57
+ */
58
+ export declare const NOT_FOUND_MESSAGE = "Object not found";
59
+ /**
60
+ * Load an object for an action as the caller: a missing object and an object the caller may not read throw the same
61
+ * `NotFound("Object not found")`; a readable object whose action is refused throws `Forbidden`
62
+ * @param model - the model class
63
+ * @param key - the primary key
64
+ * @param context - the caller context
65
+ * @param action - the action ("get" to only read)
66
+ * @returns the object
67
+ */
68
+ export declare function loadModelForAction<T = any>(model: any, key: any, context: IOperationContext, action: string): Promise<T>;
69
+ /**
70
+ * The parent relation (`ModelParent`) of a model, if any
71
+ * @param model - the model class
72
+ * @returns the parent attribute and model identifier
73
+ */
74
+ export declare function getParentRelation(model: any): {
75
+ attribute: string;
76
+ model: string;
77
+ } | undefined;
78
+ /**
79
+ * A child can only be attached to a parent the caller may read: a missing parent and an unreadable one throw the same
80
+ * `NotFound("Object not found")`
81
+ * @param model - the child model class
82
+ * @param parentId - the parent primary key
83
+ * @param context - the caller context
84
+ */
85
+ export declare function checkModelParent(model: any, parentId: any, context: IOperationContext): Promise<void>;
86
+ /**
87
+ * When an update moves an object to another parent, the caller must be able to read the new parent
88
+ * @param model - the model class
89
+ * @param object - the stored object
90
+ * @param input - the sanitized update input
91
+ * @param context - the caller context
92
+ */
93
+ export declare function checkModelReparent(model: any, object: any, input: any, context: IOperationContext): Promise<void>;
94
+ /**
95
+ * Enforce a model's permission for an action on an object: the single check used by every client path (REST, gRPC,
96
+ * MCP, GraphQL and any transport dispatching operations)
97
+ *
98
+ * A caller who may not read the object (`canAct(ctx, "get", object)` refused) gets exactly the error of a missing
99
+ * object (`NotFound("Object not found")`), whatever the action, so a refusal never reveals that the key exists. A
100
+ * caller who may read the object but not perform the action gets a `Forbidden`. On `"create"` the object does not
101
+ * exist yet: a refusal is always a `Forbidden`.
102
+ *
103
+ * The refusal reason returned by `canAct` is logged, never sent to the client.
104
+ *
105
+ * @param object - the model instance
106
+ * @param context - the caller context
107
+ * @param action - the action name
108
+ * @param model - the model class (defaults to the object's class)
109
+ * @throws WebdaError.NotFound when the caller may not read the object
110
+ * @throws WebdaError.Forbidden when the caller may read the object but not perform the action
111
+ */
112
+ export declare function checkModelPermission(object: any, context: IOperationContext, action: string, model?: any): Promise<void>;
113
+ /**
114
+ * Enforce a model's permission for a static (class-level) action: `canAct(context, action)` without object
115
+ *
116
+ * There is no object to hide, so a refusal is always a `Forbidden`.
117
+ * @param model - the model class
118
+ * @param context - the caller context
119
+ * @param action - the action name
120
+ * @throws WebdaError.Forbidden when refused
121
+ */
122
+ export declare function checkStaticModelPermission(model: any, context: IOperationContext, action: string): Promise<void>;
123
+ /**
124
+ * Persist a new object with the repository `create`, which refuses an existing key (atomically in every bundled
125
+ * repository), instead of `save()`, which upserts
126
+ * @param object - the new model instance
127
+ * @returns the object
128
+ * @throws WebdaError.Conflict when an object with the same primary key exists
129
+ */
130
+ export declare function createModel<T = any>(object: T): Promise<T>;
131
+ /**
132
+ * AND the model's permission query into a client query
133
+ *
134
+ * Both sides are parsed and combined on the expression tree, so a client `OR` keeps its precedence. The client
135
+ * ORDER BY, LIMIT and OFFSET are kept.
136
+ *
137
+ * @param query - the client query
138
+ * @param permission - the permission query, if any
139
+ * @returns the query to run
140
+ * @throws SyntaxError if the client query is invalid
141
+ */
142
+ export declare function mergePermissionQuery(query: string, permission?: PermissionQuery | null): string;
143
+ /**
144
+ * Highest LIMIT a client query can ask for: larger LIMITs are lowered to it
145
+ */
146
+ export declare const MAX_QUERY_LIMIT = 1000;
147
+ /**
148
+ * Rows a permission-filtered query scans to fill one page: ten times the page LIMIT, at least
149
+ * {@link MIN_SCANNED_ROWS}, at most {@link MAX_SCANNED_ROWS}
150
+ */
151
+ export declare const SCAN_FACTOR = 10;
152
+ /**
153
+ * Lower bound of the scan budget of a permission-filtered page
154
+ */
155
+ export declare const MIN_SCANNED_ROWS = 100;
156
+ /**
157
+ * Absolute bound of the scan budget of a permission-filtered page
158
+ */
159
+ export declare const MAX_SCANNED_ROWS = 10000;
160
+ /**
161
+ * Most store pages read to fill one page, whatever their size
162
+ */
163
+ export declare const MAX_REFILL_PAGES = 100;
164
+ /**
165
+ * Lifetime of a continuation token (one hour): paging has to resume within it
166
+ */
167
+ export declare const CONTINUATION_TOKEN_TTL_MS: number;
168
+ /**
169
+ * Refuse a client query that reads private (`__`-prefixed) fields, at any depth, in its filter or ORDER BY: a filter
170
+ * on a private field (a password hash...) would reveal its value through which rows match. The field list of a
171
+ * SELECT and the targets of an UPDATE SET are checked too.
172
+ * @param query - the parsed client query
173
+ * @throws WebdaError.BadRequest when a field path has a `__`-prefixed segment
174
+ */
175
+ export declare function assertNoPrivateFields(query: QueryValidator): void;
176
+ /**
177
+ * Refuse what the Query operations do not run: DELETE and UPDATE statements, and SELECT field lists
178
+ *
179
+ * Statements are parsed by WebdaQL and run by the repositories (`deleteMany`, `updateMany`), never through the
180
+ * Query operations exposed to clients (REST, gRPC, MCP, GraphQL): those take a filter query only.
181
+ * @param query - the parsed client query
182
+ * @throws WebdaError.BadRequest for a DELETE or UPDATE statement, or a SELECT field list
183
+ */
184
+ export declare function assertFilterOnly(query: QueryValidator): void;
185
+ /**
186
+ * What a continuation token is bound to: it is only accepted back for the same model, the same query (filter and
187
+ * ORDER BY) and the same caller
188
+ */
189
+ export type ContinuationTokenBinding = {
190
+ /**
191
+ * Model identifier
192
+ */
193
+ model: string;
194
+ /**
195
+ * The query the token continues: filter and ORDER BY, without LIMIT and OFFSET
196
+ */
197
+ query: string;
198
+ /**
199
+ * Caller id, "anonymous" when not logged in
200
+ */
201
+ user: string;
202
+ };
203
+ /**
204
+ * Seal a store continuation token: encrypted with the CryptoService (random IV, padded to 64 bytes) so its value
205
+ * cannot be read, compared or forged by the client; bound to the model, the query and the caller; and expiring
206
+ * after `ttl` ({@link CONTINUATION_TOKEN_TTL_MS})
207
+ * @param token - the store token
208
+ * @param binding - what the token is bound to
209
+ * @param ttl - lifetime in milliseconds
210
+ * @returns the opaque token
211
+ */
212
+ export declare function sealContinuationToken(token: string, binding: ContinuationTokenBinding, ttl?: number): Promise<string>;
213
+ /**
214
+ * Open a token produced by {@link sealContinuationToken}
215
+ * @param token - the opaque token
216
+ * @param binding - what the token must be bound to
217
+ * @returns the store token
218
+ * @throws WebdaError.BadRequest when the token was not produced by this application for this model, query and
219
+ * caller, or has expired
220
+ */
221
+ export declare function unsealContinuationToken(token: string, binding: ContinuationTokenBinding): Promise<string>;
222
+ /**
223
+ * Query a model as the caller
224
+ *
225
+ * - the client query may not read private (`__`) fields (400), and its LIMIT is lowered to {@link MAX_QUERY_LIMIT};
226
+ * - the client query is a filter: DELETE, UPDATE and SELECT field lists are refused (400, {@link assertFilterOnly});
227
+ * - a model defining neither `canAct` form refuses every row: the store is not asked;
228
+ * - the model's static `getPermissionQuery(context)` is ANDed into the query;
229
+ * - every result is checked with the static `canAct(context, "get", row)` and refused ones are dropped. Refused rows
230
+ * are replaced by continuing the scan (next store pages, asking only for the missing rows), within a budget of
231
+ * `SCAN_FACTOR` x LIMIT rows (between `MIN_SCANNED_ROWS` and `MAX_SCANNED_ROWS`) and {@link MAX_REFILL_PAGES}
232
+ * store pages. When the budget is spent, an empty page carries no token;
233
+ * - the continuation token is sealed ({@link sealContinuationToken}): a store token counting or naming rows
234
+ * (Postgres/Firestore offsets, Dynamo keys, memory offsets) would otherwise reveal hidden matches. It is bound to
235
+ * the model, the query and the caller, expires after {@link CONTINUATION_TOKEN_TTL_MS}, and is sent back as is in
236
+ * `OFFSET`; any other value is a 400.
237
+ *
238
+ * @param model - the model class
239
+ * @param query - the client query
240
+ * @param context - the caller context
241
+ * @returns the query results the caller may read
242
+ */
243
+ export declare function queryModelWithPermissions<T = any>(model: any, query: string, context: IOperationContext): Promise<{
244
+ results: T[];
245
+ continuationToken?: string;
246
+ }>;
247
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/models/permissions.ts"],"names":[],"mappings":"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"}