@memberjunction/core-entities 6.2.0-edge.4 → 6.2.0-edge.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 (60) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/custom/ConversationWriteAccess.d.ts +5 -0
  3. package/dist/custom/ConversationWriteAccess.d.ts.map +1 -1
  4. package/dist/custom/ConversationWriteAccess.js +6 -6
  5. package/dist/custom/ConversationWriteAccess.js.map +1 -1
  6. package/dist/custom/MJConversationDetailEntityExtended.d.ts +70 -4
  7. package/dist/custom/MJConversationDetailEntityExtended.d.ts.map +1 -1
  8. package/dist/custom/MJConversationDetailEntityExtended.js +268 -28
  9. package/dist/custom/MJConversationDetailEntityExtended.js.map +1 -1
  10. package/dist/custom/MJQueryEntityExtended.d.ts +37 -1
  11. package/dist/custom/MJQueryEntityExtended.d.ts.map +1 -1
  12. package/dist/custom/MJQueryEntityExtended.js +92 -5
  13. package/dist/custom/MJQueryEntityExtended.js.map +1 -1
  14. package/dist/custom/MJThemeEntityExtended.d.ts +13 -0
  15. package/dist/custom/MJThemeEntityExtended.d.ts.map +1 -0
  16. package/dist/custom/MJThemeEntityExtended.js +37 -0
  17. package/dist/custom/MJThemeEntityExtended.js.map +1 -0
  18. package/dist/custom/MJUserViewEntityExtended.d.ts +39 -4
  19. package/dist/custom/MJUserViewEntityExtended.d.ts.map +1 -1
  20. package/dist/custom/MJUserViewEntityExtended.js +111 -18
  21. package/dist/custom/MJUserViewEntityExtended.js.map +1 -1
  22. package/dist/custom/approvals.d.ts +55 -0
  23. package/dist/custom/approvals.d.ts.map +1 -0
  24. package/dist/custom/approvals.js +120 -0
  25. package/dist/custom/approvals.js.map +1 -0
  26. package/dist/engines/InstanceConfigEngine.d.ts.map +1 -1
  27. package/dist/engines/InstanceConfigEngine.js +13 -3
  28. package/dist/engines/InstanceConfigEngine.js.map +1 -1
  29. package/dist/engines/PermissionEngine.d.ts +5 -4
  30. package/dist/engines/PermissionEngine.d.ts.map +1 -1
  31. package/dist/engines/PermissionEngine.js +7 -5
  32. package/dist/engines/PermissionEngine.js.map +1 -1
  33. package/dist/engines/QueryEngine.d.ts +81 -6
  34. package/dist/engines/QueryEngine.d.ts.map +1 -1
  35. package/dist/engines/QueryEngine.js +283 -4
  36. package/dist/engines/QueryEngine.js.map +1 -1
  37. package/dist/engines/UserInfoEngine.d.ts +48 -2
  38. package/dist/engines/UserInfoEngine.d.ts.map +1 -1
  39. package/dist/engines/UserInfoEngine.js +65 -8
  40. package/dist/engines/UserInfoEngine.js.map +1 -1
  41. package/dist/engines/conversations.d.ts +19 -1
  42. package/dist/engines/conversations.d.ts.map +1 -1
  43. package/dist/engines/conversations.js +125 -22
  44. package/dist/engines/conversations.js.map +1 -1
  45. package/dist/engines/knowledgeHubMetadata.d.ts +8 -3
  46. package/dist/engines/knowledgeHubMetadata.d.ts.map +1 -1
  47. package/dist/engines/knowledgeHubMetadata.js +19 -2
  48. package/dist/engines/knowledgeHubMetadata.js.map +1 -1
  49. package/dist/generated/entities/__mj.d.ts +4142 -111
  50. package/dist/generated/entities/__mj.d.ts.map +1 -1
  51. package/dist/generated/entities/__mj.js +5895 -262
  52. package/dist/generated/entities/__mj.js.map +1 -1
  53. package/dist/generated/remote_operations.d.ts +17 -5
  54. package/dist/generated/remote_operations.d.ts.map +1 -1
  55. package/dist/generated/remote_operations.js.map +1 -1
  56. package/dist/index.d.ts +2 -0
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +2 -0
  59. package/dist/index.js.map +1 -1
  60. package/package.json +6 -5
@@ -6,6 +6,11 @@ export interface ConversationWriteDecision {
6
6
  Reason: string | null;
7
7
  /** True only when the conversation loaded and the user is its owner. */
8
8
  IsOwner: boolean;
9
+ /**
10
+ * True when the conversation loaded for the user. A conversation that did not load is `Allowed` whether it is
11
+ * missing or hidden from the user by a row filter, so a caller that must tell those apart checks this.
12
+ */
13
+ Loaded: boolean;
9
14
  }
10
15
  /**
11
16
  * Whether a user may write rows that belong to a conversation: the owner, or a user with
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationWriteAccess.d.ts","sourceRoot":"","sources":["../../src/custom/ConversationWriteAccess.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAKnE,4EAA4E;AAC5E,eAAO,MAAM,8BAA8B,yCAAyC,CAAC;AAErF,MAAM,WAAW,yBAAyB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC1C,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EACjC,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,yBAAyB,CAAC,CAuBpC"}
1
+ {"version":3,"file":"ConversationWriteAccess.d.ts","sourceRoot":"","sources":["../../src/custom/ConversationWriteAccess.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAKnE,4EAA4E;AAC5E,eAAO,MAAM,8BAA8B,yCAAyC,CAAC;AAErF,MAAM,WAAW,yBAAyB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,MAAM,EAAE,OAAO,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,wBAAwB,CAC1C,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,EACjC,cAAc,EAAE,MAAM,GACvB,OAAO,CAAC,yBAAyB,CAAC,CAuBpC"}
@@ -9,14 +9,14 @@ export const CONVERSATIONS_RESOURCE_TYPE_ID = '81D4BC3D-9FEB-EF11-B01A-286B35C04
9
9
  */
10
10
  export async function UserMayWriteConversation(provider, user, conversationId) {
11
11
  if (provider?.ProviderType !== 'Database' || !user) {
12
- return { Allowed: true, Reason: null, IsOwner: false };
12
+ return { Allowed: true, Reason: null, IsOwner: false, Loaded: false };
13
13
  }
14
14
  const conversation = await provider.GetEntityObject('MJ: Conversations', user);
15
15
  if (!(await conversation.Load(conversationId))) {
16
- return { Allowed: true, Reason: null, IsOwner: false };
16
+ return { Allowed: true, Reason: null, IsOwner: false, Loaded: false };
17
17
  }
18
18
  if (conversation.UserID && UUIDsEqual(conversation.UserID, user.ID)) {
19
- return { Allowed: true, Reason: null, IsOwner: true };
19
+ return { Allowed: true, Reason: null, IsOwner: true, Loaded: true };
20
20
  }
21
21
  const engine = ResourcePermissionEngine.GetProviderInstance(provider, ResourcePermissionEngine);
22
22
  await engine.Config(false, user);
@@ -24,11 +24,11 @@ export async function UserMayWriteConversation(provider, user, conversationId) {
24
24
  .GetUserAvailableResources(user, CONVERSATIONS_RESOURCE_TYPE_ID)
25
25
  .find((p) => UUIDsEqual(p.ResourceRecordID, conversationId));
26
26
  if (!grant) {
27
- return { Allowed: false, Reason: 'You do not have access to this conversation.', IsOwner: false };
27
+ return { Allowed: false, Reason: 'You do not have access to this conversation.', IsOwner: false, Loaded: true };
28
28
  }
29
29
  if (grant.PermissionLevel === 'Edit' || grant.PermissionLevel === 'Owner') {
30
- return { Allowed: true, Reason: null, IsOwner: false };
30
+ return { Allowed: true, Reason: null, IsOwner: false, Loaded: true };
31
31
  }
32
- return { Allowed: false, Reason: 'You have view-only access to this conversation.', IsOwner: false };
32
+ return { Allowed: false, Reason: 'You have view-only access to this conversation.', IsOwner: false, Loaded: true };
33
33
  }
34
34
  //# sourceMappingURL=ConversationWriteAccess.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationWriteAccess.js","sourceRoot":"","sources":["../../src/custom/ConversationWriteAccess.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,gDAAgD,CAAC;AAE1F,4EAA4E;AAC5E,MAAM,CAAC,MAAM,8BAA8B,GAAG,sCAAsC,CAAC;AASrF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC1C,QAA2B,EAC3B,IAAiC,EACjC,cAAsB;IAEtB,IAAI,QAAQ,EAAE,YAAY,KAAK,UAAU,IAAI,CAAC,IAAI,EAAE,CAAC;QACjD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC3D,CAAC;IACD,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,eAAe,CAAuB,mBAAmB,EAAE,IAAI,CAAC,CAAC;IACrG,IAAI,CAAC,CAAC,MAAM,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC;QAC7C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC3D,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,IAAI,UAAU,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QAClE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC1D,CAAC;IACD,MAAM,MAAM,GAAG,wBAAwB,CAAC,mBAAmB,CAA2B,QAAQ,EAAE,wBAAwB,CAA6B,CAAC;IACtJ,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,MAAM;SACf,yBAAyB,CAAC,IAAI,EAAE,8BAA8B,CAAC;SAC/D,IAAI,CAAC,CAAC,CAA6B,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAC,CAAC;IAC7F,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,8CAA8C,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACtG,CAAC;IACD,IAAI,KAAK,CAAC,eAAe,KAAK,MAAM,IAAI,KAAK,CAAC,eAAe,KAAK,OAAO,EAAE,CAAC;QACxE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,iDAAiD,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACzG,CAAC"}
1
+ {"version":3,"file":"ConversationWriteAccess.js","sourceRoot":"","sources":["../../src/custom/ConversationWriteAccess.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,gDAAgD,CAAC;AAE1F,4EAA4E;AAC5E,MAAM,CAAC,MAAM,8BAA8B,GAAG,sCAAsC,CAAC;AAcrF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC1C,QAA2B,EAC3B,IAAiC,EACjC,cAAsB;IAEtB,IAAI,QAAQ,EAAE,YAAY,KAAK,UAAU,IAAI,CAAC,IAAI,EAAE,CAAC;QACjD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC1E,CAAC;IACD,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,eAAe,CAAuB,mBAAmB,EAAE,IAAI,CAAC,CAAC;IACrG,IAAI,CAAC,CAAC,MAAM,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC;QAC7C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC1E,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,IAAI,UAAU,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;QAClE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACxE,CAAC;IACD,MAAM,MAAM,GAAG,wBAAwB,CAAC,mBAAmB,CAA2B,QAAQ,EAAE,wBAAwB,CAA6B,CAAC;IACtJ,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,MAAM;SACf,yBAAyB,CAAC,IAAI,EAAE,8BAA8B,CAAC;SAC/D,IAAI,CAAC,CAAC,CAA6B,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAC,CAAC;IAC7F,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,8CAA8C,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACpH,CAAC;IACD,IAAI,KAAK,CAAC,eAAe,KAAK,MAAM,IAAI,KAAK,CAAC,eAAe,KAAK,OAAO,EAAE,CAAC;QACxE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,iDAAiD,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AACvH,CAAC"}
@@ -15,6 +15,28 @@ import { MJConversationDetailEntity } from '../generated/entity_subclasses.js';
15
15
  * owner-only — even users with `Edit`-level grants on the conversation are
16
16
  * blocked from changing them, because the storage is a single value per
17
17
  * message (it represents the owner's evaluation, not per-grantee opinions).
18
+ *
19
+ * **Who a message is from** (A19, the rules for people's messages). For a `Role='User'` message, the owner and
20
+ * grantees alike:
21
+ * - post only as themselves: a new message naming another person's `UserID` is refused, and a grantee's message
22
+ * with no `UserID` gets the grantee's (an empty one reads as the conversation's owner). The owner's stays empty,
23
+ * since it already means them;
24
+ * - change who wrote it and what it says ({@link AUTHORED_FIELDS}) only when they wrote it, and never its role;
25
+ * - never move it to another conversation or fork ({@link PLACEMENT_FIELDS});
26
+ * - delete it only when they wrote it, or own the conversation.
27
+ *
28
+ * The system user is exempt from these authorship rules, since server code writes on people's behalf, but it still
29
+ * needs to own the conversation or hold a grant on it, as before: some server paths elevate an anonymous visitor to
30
+ * the system user (MJ#4791), and this gate keeps those paths out of other people's conversations, so no transaction
31
+ * variable may set the row or conversation it writes ({@link ACCESS_GATED_FIELDS}) for it either.
32
+ *
33
+ * **Only the server writes an agent's reply** (A19, second half). A reply (`Role` `AI` or `Error`) is created, and
34
+ * what it says ({@link REPLY_FIELDS}) changed, only on an instance server code marked with
35
+ * {@link MarkServerAuthoredReply}. The marker is not a field: no GraphQL input, `OldValues___` or transaction
36
+ * variable reaches it, so a person's save of a reply is refused whatever its user, and so is an unmarked save as the
37
+ * system user. A marked write still needs its context user (the person the reply is for, or whoever runs a
38
+ * host-driven turn) to own the conversation or hold a grant on it, so being the system user grants nothing new.
39
+ * Pinning, hiding, the owner's rating and other bookkeeping keep the conversation-level rules.
18
40
  * MJ's permissions engine has no native primitive for per-field row-conditional
19
41
  * write rules, so this gate lives here rather than in `MJ: Entity Permissions`.
20
42
  *
@@ -24,15 +46,59 @@ import { MJConversationDetailEntity } from '../generated/entity_subclasses.js';
24
46
  * round trips for permission lookups.
25
47
  */
26
48
  export declare class MJConversationDetailEntityExtended extends MJConversationDetailEntity {
49
+ private serverAuthoredReply;
50
+ /**
51
+ * Marks this instance as server code writing an agent's reply, so it may create a reply (`Role` `AI` or `Error`)
52
+ * and change what one says. Call it before `Save()`; it stays on the instance for every later save, like
53
+ * `SetCloneContext()`. The context user still needs to own the conversation or hold a grant on it.
54
+ *
55
+ * It is not a field: no GraphQL input, `OldValues___` or transaction variable can set it, and the browser's copy of
56
+ * this class never sends it. It has no effect on a person's message.
57
+ */
58
+ MarkServerAuthoredReply(): void;
59
+ /** Whether {@link MarkServerAuthoredReply} was called on this instance. */
60
+ get IsServerAuthoredReply(): boolean;
27
61
  Save(options?: EntitySaveOptions): Promise<boolean>;
28
62
  Delete(options?: EntityDeleteOptions): Promise<boolean>;
29
63
  private currentUserMayWrite;
30
64
  /**
31
- * Names of the rating/feedback fields that are dirty on this save.
32
- * Used by `currentUserMayWrite` to detect attempts by non-owners to
33
- * modify the owner-only fields and surface a specific denial message.
65
+ * Why the user may not write to this message's conversation at all, or `null` when they may. The owner may. Anyone
66
+ * else may not change the rating fields, which are the owner's alone, and needs an `Edit` or `Owner` grant. A
67
+ * conversation that does not load for the user is hidden from them when it exists (a row filter), and they may not
68
+ * write to it; when no one can find it, it is missing or being created in this same transaction, and the foreign
69
+ * key and the base save decide, as before.
70
+ */
71
+ private accessRefusal;
72
+ /**
73
+ * Why this write would put words in an agent's mouth, or `null` when it does not: an unmarked save that creates a
74
+ * reply, turns a message into one, or changes what one says ({@link REPLY_FIELDS}). Applies to every user, the
75
+ * system user included. Deleting a reply keeps the conversation-level rules.
76
+ */
77
+ private replyRefusal;
78
+ /**
79
+ * Why this write breaks the rules for people's messages (see the class doc), or `null` when it does not. On a new
80
+ * message from a grantee with no `UserID`, fills in the grantee's.
81
+ */
82
+ private authorshipRefusal;
83
+ /** A new person's message names its author as the poster, or names no one (filled in for a grantee). */
84
+ private postRefusal;
85
+ /**
86
+ * An existing person's message: only its author changes who wrote it and what it says, no one its role (to or
87
+ * from `User`), and no one moves it to another conversation or fork. A message with no `UserID` is the owner's.
88
+ */
89
+ private changeRefusal;
90
+ /** A person's message is deleted by its author or by the conversation's owner (whose it is with no `UserID`). */
91
+ private deleteRefusal;
92
+ /** Whether the conversation exists, read as the system user; false when there is no system user to ask. */
93
+ private conversationExists;
94
+ /**
95
+ * Whether a transaction variable of this message's group sets one of `fields` on it. The field is resolved the way
96
+ * `Set` resolves it at submit (trimmed, any case).
34
97
  */
35
- private dirtyRatingFieldNames;
98
+ private fieldSetByTransactionVariable;
99
+ private isFieldDirty;
100
+ /** A field's value as loaded, before this save's changes; `null` when it was empty or is not text. */
101
+ private originalFieldText;
36
102
  /**
37
103
  * Records WHY the write was refused as a NEW `ResultHistory` entry, so callers that read
38
104
  * `LatestResult.CompleteMessage` see the reason (issue #4791).
@@ -1 +1 @@
1
- {"version":3,"file":"MJConversationDetailEntityExtended.d.ts","sourceRoot":"","sources":["../../src/custom/MJConversationDetailEntityExtended.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,mBAAmB,EACnB,iBAAiB,EAGpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAW5E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBACa,kCAAmC,SAAQ,0BAA0B;IAC/D,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAOnD,MAAM,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;YAOxD,mBAAmB;IA2DjC;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAS7B;;;;;;;;OAQG;IACH,OAAO,CAAC,YAAY;CASvB;AAED,6FAA6F;AAC7F,wBAAgB,sCAAsC,IAAI,IAAI,CAE7D"}
1
+ {"version":3,"file":"MJConversationDetailEntityExtended.d.ts","sourceRoot":"","sources":["../../src/custom/MJConversationDetailEntityExtended.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,mBAAmB,EAEnB,iBAAiB,EAKpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,0BAA0B,EAAwB,MAAM,gCAAgC,CAAC;AAuFlG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,qBACa,kCAAmC,SAAQ,0BAA0B;IAC9E,OAAO,CAAC,mBAAmB,CAAS;IAEpC;;;;;;;OAOG;IACI,uBAAuB,IAAI,IAAI;IAItC,2EAA2E;IAC3E,IAAW,qBAAqB,IAAI,OAAO,CAE1C;IAEc,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAYnD,MAAM,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;YAOxD,mBAAmB;IA2DjC;;;;;;OAMG;YACW,aAAa;IAoB3B;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAWpB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAOzB,wGAAwG;IACxG,OAAO,CAAC,WAAW;IAgBnB;;;OAGG;IACH,OAAO,CAAC,aAAa;IA2BrB,iHAAiH;IACjH,OAAO,CAAC,aAAa;IASrB,2GAA2G;YAC7F,kBAAkB;IAahC;;;OAGG;IACH,OAAO,CAAC,6BAA6B;IAOrC,OAAO,CAAC,YAAY;IAIpB,sGAAsG;IACtG,OAAO,CAAC,iBAAiB;IAKzB;;;;;;;;OAQG;IACH,OAAO,CAAC,YAAY;CASvB;AAED,6FAA6F;AAC7F,wBAAgB,sCAAsC,IAAI,IAAI,CAE7D"}
@@ -4,8 +4,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { BaseEntity, BaseEntityResult, LogError } from '@memberjunction/core';
8
- import { RegisterClass } from '@memberjunction/global';
7
+ import { BaseEntity, BaseEntityResult, EntityFieldInfo, LogError, WellKnownUserSource } from '@memberjunction/core';
8
+ import { RegisterClass, UUIDsEqual } from '@memberjunction/global';
9
9
  import { MJConversationDetailEntity } from '../generated/entity_subclasses.js';
10
10
  import { UserMayWriteConversation } from './ConversationWriteAccess.js';
11
11
  /**
@@ -15,6 +15,74 @@ import { UserMayWriteConversation } from './ConversationWriteAccess.js';
15
15
  * the parent conversation cannot retroactively rewrite the owner's rating.
16
16
  */
17
17
  const OWNER_ONLY_RATING_FIELDS = ['UserRating', 'UserFeedback'];
18
+ /**
19
+ * The fields of a person's (`Role='User'`) message that say who wrote it and what it says: its author, its role, its
20
+ * text, its attachment, its media type and where it sits in a thread. Only its author may change them, except
21
+ * `Role`, which no one may change. Other fields (pinning, hiding, bookkeeping the server writes) keep the
22
+ * conversation-level rules.
23
+ */
24
+ const AUTHORED_FIELDS = ['UserID', 'Role', 'Message', 'ArtifactID', 'ArtifactVersionID', 'MediaType', 'ParentID'];
25
+ /**
26
+ * Where a message sits: its conversation and its fork (`BranchID`, null is Main). No one moves a person's message,
27
+ * its author included: moving it takes it out of the thread it was written in.
28
+ */
29
+ const PLACEMENT_FIELDS = ['ConversationID', 'BranchID'];
30
+ /**
31
+ * The fields the access and reply-authorship rules, which hold for everyone (including the system user unless
32
+ * explicitly marked), decide on: which row is written (`ID`; an update's SQL is rebuilt with the variable's value at
33
+ * submit, so it would write another row), its conversation, its role (`Role` — a transaction variable setting `AI` or
34
+ * `Error` at submit would bypass the unmarked reply check), and the owner's rating. A transaction variable sets its
35
+ * field at submit, after the gate has run, so none of these may take its value from one.
36
+ */
37
+ const ACCESS_GATED_FIELDS = ['ID', 'ConversationID', 'Role', ...OWNER_ONLY_RATING_FIELDS];
38
+ /**
39
+ * {@link ACCESS_GATED_FIELDS} plus `BranchID` (placement on a fork) and the remaining {@link AUTHORED_FIELDS}
40
+ * (`Role` is already in {@link ACCESS_GATED_FIELDS}), which the authorship rules decide on and the system user skips.
41
+ */
42
+ const GATED_FIELDS = [
43
+ ...ACCESS_GATED_FIELDS,
44
+ 'BranchID',
45
+ ...AUTHORED_FIELDS.filter(field => field !== 'Role')
46
+ ];
47
+ /**
48
+ * The fields of an agent's reply (`Role` `AI` or `Error`) that say what the agent said, who said it, and where it sits:
49
+ * the authored fields ({@link AUTHORED_FIELDS}: `UserID`, `Role`, `Message`, `ArtifactID`, `ArtifactVersionID`,
50
+ * `MediaType`, `ParentID`), plus its conversation (`ConversationID`) and fork (`BranchID`), its agent (`AgentID`),
51
+ * its status (`Status`) and error (`Error`), the form (`ResponseForm`), commands (`ActionableCommands`,
52
+ * `AutomaticCommands`), suggested replies (`SuggestedResponses`) and reflection (`ReflectionInsights`) it offers, and
53
+ * the turn timing and external reference (`CompletionTime`, `ExternalID`). Only a server write marked with
54
+ * {@link MJConversationDetailEntityExtended.MarkServerAuthoredReply} changes them. Pinning, hiding, the owner's rating
55
+ * and the other bookkeeping keep the conversation-level rules.
56
+ */
57
+ const REPLY_FIELDS = [
58
+ ...AUTHORED_FIELDS,
59
+ 'ConversationID',
60
+ 'BranchID',
61
+ 'AgentID',
62
+ 'Status',
63
+ 'Error',
64
+ 'ResponseForm',
65
+ 'ActionableCommands',
66
+ 'AutomaticCommands',
67
+ 'SuggestedResponses',
68
+ 'ReflectionInsights',
69
+ 'CompletionTime',
70
+ 'ExternalID'
71
+ ];
72
+ /** The refusal for a write that would put words in an agent's mouth. */
73
+ const REPLY_REFUSAL = "Only the server writes an agent's reply.";
74
+ /**
75
+ * Whether a `Role` value is a person's message. Compared the way validation and the database's CHECK constraint
76
+ * compare it (trimmed, any case), so `'user'` or `'User '`, which both store, cannot slip past these rules.
77
+ */
78
+ function isPersonRole(role) {
79
+ return role != null && EntityFieldInfo.NormalizeValueListValue(role) === 'user';
80
+ }
81
+ /** Whether a `Role` value is an agent's reply (`AI` or `Error`), compared as {@link isPersonRole} compares. */
82
+ function isReplyRole(role) {
83
+ const normalized = role == null ? null : EntityFieldInfo.NormalizeValueListValue(role);
84
+ return normalized === 'ai' || normalized === 'error';
85
+ }
18
86
  /**
19
87
  * Server-side defense-in-depth for conversation sharing. The Angular chat UI
20
88
  * disables the message input when the current user only holds `View` access,
@@ -30,6 +98,28 @@ const OWNER_ONLY_RATING_FIELDS = ['UserRating', 'UserFeedback'];
30
98
  * owner-only — even users with `Edit`-level grants on the conversation are
31
99
  * blocked from changing them, because the storage is a single value per
32
100
  * message (it represents the owner's evaluation, not per-grantee opinions).
101
+ *
102
+ * **Who a message is from** (A19, the rules for people's messages). For a `Role='User'` message, the owner and
103
+ * grantees alike:
104
+ * - post only as themselves: a new message naming another person's `UserID` is refused, and a grantee's message
105
+ * with no `UserID` gets the grantee's (an empty one reads as the conversation's owner). The owner's stays empty,
106
+ * since it already means them;
107
+ * - change who wrote it and what it says ({@link AUTHORED_FIELDS}) only when they wrote it, and never its role;
108
+ * - never move it to another conversation or fork ({@link PLACEMENT_FIELDS});
109
+ * - delete it only when they wrote it, or own the conversation.
110
+ *
111
+ * The system user is exempt from these authorship rules, since server code writes on people's behalf, but it still
112
+ * needs to own the conversation or hold a grant on it, as before: some server paths elevate an anonymous visitor to
113
+ * the system user (MJ#4791), and this gate keeps those paths out of other people's conversations, so no transaction
114
+ * variable may set the row or conversation it writes ({@link ACCESS_GATED_FIELDS}) for it either.
115
+ *
116
+ * **Only the server writes an agent's reply** (A19, second half). A reply (`Role` `AI` or `Error`) is created, and
117
+ * what it says ({@link REPLY_FIELDS}) changed, only on an instance server code marked with
118
+ * {@link MarkServerAuthoredReply}. The marker is not a field: no GraphQL input, `OldValues___` or transaction
119
+ * variable reaches it, so a person's save of a reply is refused whatever its user, and so is an unmarked save as the
120
+ * system user. A marked write still needs its context user (the person the reply is for, or whoever runs a
121
+ * host-driven turn) to own the conversation or hold a grant on it, so being the system user grants nothing new.
122
+ * Pinning, hiding, the owner's rating and other bookkeeping keep the conversation-level rules.
33
123
  * MJ's permissions engine has no native primitive for per-field row-conditional
34
124
  * write rules, so this gate lives here rather than in `MJ: Entity Permissions`.
35
125
  *
@@ -39,11 +129,35 @@ const OWNER_ONLY_RATING_FIELDS = ['UserRating', 'UserFeedback'];
39
129
  * round trips for permission lookups.
40
130
  */
41
131
  let MJConversationDetailEntityExtended = class MJConversationDetailEntityExtended extends MJConversationDetailEntity {
132
+ constructor() {
133
+ super(...arguments);
134
+ this.serverAuthoredReply = false;
135
+ }
136
+ /**
137
+ * Marks this instance as server code writing an agent's reply, so it may create a reply (`Role` `AI` or `Error`)
138
+ * and change what one says. Call it before `Save()`; it stays on the instance for every later save, like
139
+ * `SetCloneContext()`. The context user still needs to own the conversation or hold a grant on it.
140
+ *
141
+ * It is not a field: no GraphQL input, `OldValues___` or transaction variable can set it, and the browser's copy of
142
+ * this class never sends it. It has no effect on a person's message.
143
+ */
144
+ MarkServerAuthoredReply() {
145
+ this.serverAuthoredReply = true;
146
+ }
147
+ /** Whether {@link MarkServerAuthoredReply} was called on this instance. */
148
+ get IsServerAuthoredReply() {
149
+ return this.serverAuthoredReply;
150
+ }
42
151
  async Save(options) {
152
+ const callerUserID = this.UserID; // the gate may fill in a grantee's; a save that fails keeps the caller's
43
153
  if (!(await this.currentUserMayWrite('save'))) {
44
154
  return false;
45
155
  }
46
- return super.Save(options);
156
+ const saved = await super.Save(options);
157
+ if (!saved) {
158
+ this.UserID = callerUserID;
159
+ }
160
+ return saved;
47
161
  }
48
162
  async Delete(options) {
49
163
  if (!(await this.currentUserMayWrite('delete'))) {
@@ -63,6 +177,19 @@ let MJConversationDetailEntityExtended = class MJConversationDetailEntityExtende
63
177
  // No user context on a server save = system operation; allow.
64
178
  return true;
65
179
  }
180
+ // The system user skips the authorship rules (server code writes on people's behalf) but, like anyone, must own
181
+ // the conversation or hold a grant on it: some server paths elevate an anonymous visitor to the system user
182
+ // (MJ#4791), and this gate is what keeps those paths out of other people's conversations.
183
+ const isSystemUser = WellKnownUserSource.Instance.IsSystemUser(user);
184
+ const variableGated = isSystemUser ? ACCESS_GATED_FIELDS : GATED_FIELDS;
185
+ const touchesReply = isReplyRole(this.Role) || isReplyRole(this.originalFieldText('Role'));
186
+ if (operation === 'save' && this.fieldSetByTransactionVariable(touchesReply && !this.serverAuthoredReply ? [...variableGated, ...REPLY_FIELDS] : variableGated)) {
187
+ // A transaction variable sets its field after this check has passed, at submit, to any value an earlier
188
+ // item in the group produced: a message would be checked as one row, author or conversation and saved as
189
+ // another, and an unmarked save could rewrite what an agent's reply says.
190
+ this.recordDenied('A conversation message cannot take its values from a transaction variable.', resultType);
191
+ return false;
192
+ }
66
193
  // A message created by the record-cloning engine, as part of copying a conversation into a
67
194
  // new user's account. The engine has already authorized that copy (Owner-only, the clone
68
195
  // authorization, and conversations are copyable only from a template user), and the
@@ -72,20 +199,12 @@ let MJConversationDetailEntityExtended = class MJConversationDetailEntityExtende
72
199
  return true;
73
200
  }
74
201
  try {
75
- const decision = await UserMayWriteConversation(provider, user, this.ConversationID);
76
- if (decision.IsOwner) {
77
- return true;
78
- }
79
- // Non-owners are *never* allowed to touch the rating fields, even
80
- // when they hold Edit/Owner grants on the conversation. UserRating
81
- // and UserFeedback represent the owner's evaluation of an AI
82
- // message and there is no per-user storage to overwrite.
83
- if (this.dirtyRatingFieldNames().length > 0) {
84
- this.recordDenied('Only the conversation owner can set or change the rating and feedback on this message.', resultType);
85
- return false;
86
- }
87
- if (!decision.Allowed) {
88
- this.recordDenied(decision.Reason ?? 'Permission denied.', resultType);
202
+ const access = await UserMayWriteConversation(provider, user, this.ConversationID);
203
+ const refusal = (await this.accessRefusal(provider, access))
204
+ ?? (isSystemUser ? null : this.authorshipRefusal(operation, user, access.IsOwner))
205
+ ?? this.replyRefusal(operation);
206
+ if (refusal) {
207
+ this.recordDenied(refusal, resultType);
89
208
  return false;
90
209
  }
91
210
  return true;
@@ -100,18 +219,139 @@ let MJConversationDetailEntityExtended = class MJConversationDetailEntityExtende
100
219
  }
101
220
  }
102
221
  /**
103
- * Names of the rating/feedback fields that are dirty on this save.
104
- * Used by `currentUserMayWrite` to detect attempts by non-owners to
105
- * modify the owner-only fields and surface a specific denial message.
222
+ * Why the user may not write to this message's conversation at all, or `null` when they may. The owner may. Anyone
223
+ * else may not change the rating fields, which are the owner's alone, and needs an `Edit` or `Owner` grant. A
224
+ * conversation that does not load for the user is hidden from them when it exists (a row filter), and they may not
225
+ * write to it; when no one can find it, it is missing or being created in this same transaction, and the foreign
226
+ * key and the base save decide, as before.
106
227
  */
107
- dirtyRatingFieldNames() {
108
- const matches = [];
109
- for (const name of OWNER_ONLY_RATING_FIELDS) {
110
- const field = this.Fields.find((f) => f.Name === name);
111
- if (field?.Dirty)
112
- matches.push(name);
113
- }
114
- return matches;
228
+ async accessRefusal(provider, access) {
229
+ if (access.IsOwner) {
230
+ return null;
231
+ }
232
+ // Non-owners are *never* allowed to touch the rating fields, even
233
+ // when they hold Edit/Owner grants on the conversation. UserRating
234
+ // and UserFeedback represent the owner's evaluation of an AI
235
+ // message and there is no per-user storage to overwrite.
236
+ if (OWNER_ONLY_RATING_FIELDS.some((name) => this.isFieldDirty(name))) {
237
+ return 'Only the conversation owner can set or change the rating and feedback on this message.';
238
+ }
239
+ if (!access.Allowed) {
240
+ return access.Reason ?? 'Permission denied.';
241
+ }
242
+ if (!access.Loaded && (await this.conversationExists(provider))) {
243
+ return 'You do not have access to this conversation.';
244
+ }
245
+ return null;
246
+ }
247
+ /**
248
+ * Why this write would put words in an agent's mouth, or `null` when it does not: an unmarked save that creates a
249
+ * reply, turns a message into one, or changes what one says ({@link REPLY_FIELDS}). Applies to every user, the
250
+ * system user included. Deleting a reply keeps the conversation-level rules.
251
+ */
252
+ replyRefusal(operation) {
253
+ if (operation === 'delete' || this.serverAuthoredReply) {
254
+ return null;
255
+ }
256
+ if (!this.IsSaved) {
257
+ return isReplyRole(this.Role) ? REPLY_REFUSAL : null;
258
+ }
259
+ const touchesReply = isReplyRole(this.originalFieldText('Role')) || isReplyRole(this.Role);
260
+ return touchesReply && REPLY_FIELDS.some((name) => this.isFieldDirty(name)) ? REPLY_REFUSAL : null;
261
+ }
262
+ /**
263
+ * Why this write breaks the rules for people's messages (see the class doc), or `null` when it does not. On a new
264
+ * message from a grantee with no `UserID`, fills in the grantee's.
265
+ */
266
+ authorshipRefusal(operation, user, isOwner) {
267
+ if (operation === 'delete') {
268
+ return this.deleteRefusal(user, isOwner);
269
+ }
270
+ return this.IsSaved ? this.changeRefusal(user, isOwner) : this.postRefusal(user, isOwner);
271
+ }
272
+ /** A new person's message names its author as the poster, or names no one (filled in for a grantee). */
273
+ postRefusal(user, isOwner) {
274
+ if (!isPersonRole(this.Role)) {
275
+ return null;
276
+ }
277
+ if (!this.UserID) {
278
+ // An empty UserID reads as the conversation's owner. A grantee's message gets the grantee's ID; the
279
+ // owner's stays empty, so server code writing as the owner (a room transcript of what other people said)
280
+ // is not stamped with the owner's ID.
281
+ if (!isOwner) {
282
+ this.UserID = user.ID;
283
+ }
284
+ return null;
285
+ }
286
+ return UUIDsEqual(this.UserID, user.ID) ? null : 'You can post a message only as yourself.';
287
+ }
288
+ /**
289
+ * An existing person's message: only its author changes who wrote it and what it says, no one its role (to or
290
+ * from `User`), and no one moves it to another conversation or fork. A message with no `UserID` is the owner's.
291
+ */
292
+ changeRefusal(user, isOwner) {
293
+ const wasPerson = isPersonRole(this.originalFieldText('Role'));
294
+ if (this.isFieldDirty('Role') && (wasPerson || isPersonRole(this.Role))) {
295
+ return "A message's role cannot be changed.";
296
+ }
297
+ if (!wasPerson) {
298
+ return null;
299
+ }
300
+ if (PLACEMENT_FIELDS.some((name) => this.isFieldDirty(name))) {
301
+ // Moving it would take it out of the thread it was written in (a delete) and put it in one its author never
302
+ // wrote in, and, with no UserID, make it read as another conversation's owner's.
303
+ return 'A message cannot be moved to another conversation or fork.';
304
+ }
305
+ const changed = AUTHORED_FIELDS.filter((name) => this.isFieldDirty(name));
306
+ if (changed.length === 0) {
307
+ return null;
308
+ }
309
+ const author = this.originalFieldText('UserID') ?? (isOwner ? user.ID : null);
310
+ if (!author || !UUIDsEqual(author, user.ID)) {
311
+ return 'Only the person who wrote this message can change it.';
312
+ }
313
+ if (changed.includes('UserID') && !(this.UserID && UUIDsEqual(this.UserID, user.ID))) {
314
+ return 'You can post a message only as yourself.';
315
+ }
316
+ return null;
317
+ }
318
+ /** A person's message is deleted by its author or by the conversation's owner (whose it is with no `UserID`). */
319
+ deleteRefusal(user, isOwner) {
320
+ if (!isPersonRole(this.Role) || isOwner) {
321
+ return null;
322
+ }
323
+ return this.UserID && UUIDsEqual(this.UserID, user.ID)
324
+ ? null
325
+ : "Only the message's author or the conversation's owner can delete it.";
326
+ }
327
+ /** Whether the conversation exists, read as the system user; false when there is no system user to ask. */
328
+ async conversationExists(provider) {
329
+ const systemUser = await WellKnownUserSource.Instance.GetSystemUser(provider);
330
+ if (!systemUser) {
331
+ LogError(`MJConversationDetailEntityExtended: no system user to check whether conversation ${this.ConversationID} ` +
332
+ 'exists; treating it as missing.');
333
+ return false;
334
+ }
335
+ const conversation = await provider.GetEntityObject('MJ: Conversations', systemUser);
336
+ return conversation.Load(this.ConversationID);
337
+ }
338
+ /**
339
+ * Whether a transaction variable of this message's group sets one of `fields` on it. The field is resolved the way
340
+ * `Set` resolves it at submit (trimmed, any case).
341
+ */
342
+ fieldSetByTransactionVariable(fields) {
343
+ return !!this.TransactionGroup?.Variables.some((v) => {
344
+ const name = v.EntityObject === this && v.Type === 'Use' ? this.GetFieldByName(v.FieldName)?.Name : null;
345
+ return !!name && fields.includes(name);
346
+ });
347
+ }
348
+ isFieldDirty(name) {
349
+ return !!this.GetFieldByName(name)?.Dirty;
350
+ }
351
+ /** A field's value as loaded, before this save's changes; `null` when it was empty or is not text. */
352
+ originalFieldText(name) {
353
+ const original = this.GetFieldByName(name)?.OldValue;
354
+ return typeof original === 'string' && original.length > 0 ? original : null;
115
355
  }
116
356
  /**
117
357
  * Records WHY the write was refused as a NEW `ResultHistory` entry, so callers that read
@@ -1 +1 @@
1
- {"version":3,"file":"MJConversationDetailEntityExtended.js","sourceRoot":"","sources":["../../src/custom/MJConversationDetailEntityExtended.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MJConversationDetailEntityExtended.js","sourceRoot":"","sources":["../../src/custom/MJConversationDetailEntityExtended.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- import { UserInfo, QueryCacheConfig } from "@memberjunction/core";
1
+ import { UserInfo, QueryCacheConfig, EntityInfo, FieldSecurityViolation } from "@memberjunction/core";
2
+ import { SQLScreeningDialect } from "@memberjunction/global";
2
3
  import { MJQueryEntity, MJQueryFieldEntity, MJQueryParameterEntity, MJQueryEntityEntity, MJQueryPermissionEntity, MJQueryDependencyEntity, MJQueryCategoryEntity } from "../generated/entity_subclasses.js";
3
4
  import type { DatabasePlatform } from "@memberjunction/core";
4
5
  /**
@@ -61,6 +62,41 @@ export declare class MJQueryEntityExtended extends MJQueryEntity {
61
62
  canRun: boolean;
62
63
  deniedEntities: string[];
63
64
  };
65
+ /**
66
+ * SECURITY — the run-time half of {@link UserCanRun}: the checks that need the SQL a run will
67
+ * actually execute, after composition and template rendering. Call it with that SQL in addition
68
+ * to {@link UserCanRun}, never instead of it.
69
+ *
70
+ * 1. A query with `MJ: Query Permissions` rows is authorized by its role list alone: a user who
71
+ * holds one of those roles may run it whatever it reads (stored-procedure semantics), and one
72
+ * who holds none may not.
73
+ * 2. Otherwise every entity whose base view or base table the SQL reads as a relation must be
74
+ * readable by the user ({@link FieldSecuritySQLGate.RelationEntities}). These come from the
75
+ * SQL, so a query whose `QueryEntities` rows are missing or stale is still checked. SQL that
76
+ * cannot be read is refused for a user who cannot read every entity in the catalog.
77
+ * 3. The SQL must not reference a field the user is denied anywhere it could leak one: the
78
+ * select list, WHERE, JOIN, GROUP BY, ORDER BY or HAVING, inside expressions and aggregates
79
+ * included. A whole-row construct (`SELECT *`, `alias.*`) over an entity the user is denied a
80
+ * field on, and SQL that cannot be read, are refused too ({@link FieldSecuritySQLGate.FindViolation}).
81
+ * So is a relation that is not a CTE or a catalog entity's base view or base table (a custom
82
+ * view, synonym, table-valued function, temp table or cross-database name), whose reads
83
+ * cannot be checked. A user denied no field on any entity is not scanned.
84
+ *
85
+ * The result never names a denied field, so a refusal says only that the query cannot be run.
86
+ * `QueryFields` metadata plays no part: it is documentation, not a security source.
87
+ *
88
+ * @param user The user the query runs for
89
+ * @param renderedSQL The SQL the run will execute
90
+ * @param catalog Every entity the executing provider knows
91
+ * @param dialect The database dialect the SQL runs under
92
+ */
93
+ UserCanRunRenderedSQL(user: UserInfo, renderedSQL: string, catalog: readonly EntityInfo[], dialect: SQLScreeningDialect): {
94
+ canRun: boolean;
95
+ deniedEntities: string[];
96
+ fieldSecurityViolation?: FieldSecurityViolation;
97
+ };
98
+ /** The catalog entities this query's `QueryEntities` rows name; stale rows are left out. */
99
+ private declaredEntities;
64
100
  /** Whether this query has been formally approved for production use */
65
101
  get IsApproved(): boolean;
66
102
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"MJQueryEntityExtended.d.ts","sourceRoot":"","sources":["../../src/custom/MJQueryEntityExtended.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,QAAQ,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAEjG,OAAO,EACH,aAAa,EACb,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACxB,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;;;;GAOG;AACH,qBACa,qBAAsB,SAAQ,aAAa;IAIpD,2CAA2C;IAC3C,IAAW,WAAW,IAAI,kBAAkB,EAAE,CAE7C;IAED,+CAA+C;IAC/C,IAAW,eAAe,IAAI,sBAAsB,EAAE,CAErD;IAED,6CAA6C;IAC7C,IAAW,aAAa,IAAI,mBAAmB,EAAE,CAEhD;IAED,4CAA4C;IAC5C,IAAW,gBAAgB,IAAI,uBAAuB,EAAE,CAEvD;IAED,oGAAoG;IACpG,IAAW,iBAAiB,IAAI,uBAAuB,EAAE,CAExD;IAED,+EAA+E;IAC/E,IAAW,eAAe,IAAI,uBAAuB,EAAE,CAEtD;IAID,6EAA6E;IAC7E,IAAW,cAAc,IAAI,qBAAqB,GAAG,SAAS,CAG7D;IAED,OAAO,CAAC,aAAa,CAAuB;IAE5C;;;OAGG;IACH,IAAW,YAAY,IAAI,MAAM,CAKhC;IAED;;OAEG;IACI,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAmBpD;;;;;OAKG;IACI,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO;IAkBrD;;;;;;;;;;;;;;;OAeG;IACI,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,MAAM,EAAE,CAAA;KAAE;IAyDxG,uEAAuE;IACvE,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED;;;OAGG;IACH,IAAW,YAAY,IAAI,OAAO,CAEjC;IAID;;;;;OAKG;IACI,cAAc,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM;IAgBzD,OAAO,CAAC,YAAY,CAAiC;IAErD;;;;OAIG;IACH,IAAW,WAAW,IAAI,gBAAgB,CAuBzC;IAED,OAAO,CAAC,uBAAuB;CAqBlC"}
1
+ {"version":3,"file":"MJQueryEntityExtended.d.ts","sourceRoot":"","sources":["../../src/custom/MJQueryEntityExtended.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,QAAQ,EAAE,gBAAgB,EAAqB,UAAU,EAAwB,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC3J,OAAO,EAA0C,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AACrG,OAAO,EACH,aAAa,EACb,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACxB,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;;;;;;GAOG;AACH,qBACa,qBAAsB,SAAQ,aAAa;IAIpD,2CAA2C;IAC3C,IAAW,WAAW,IAAI,kBAAkB,EAAE,CAE7C;IAED,+CAA+C;IAC/C,IAAW,eAAe,IAAI,sBAAsB,EAAE,CAErD;IAED,6CAA6C;IAC7C,IAAW,aAAa,IAAI,mBAAmB,EAAE,CAEhD;IAED,4CAA4C;IAC5C,IAAW,gBAAgB,IAAI,uBAAuB,EAAE,CAEvD;IAED,oGAAoG;IACpG,IAAW,iBAAiB,IAAI,uBAAuB,EAAE,CAExD;IAED,+EAA+E;IAC/E,IAAW,eAAe,IAAI,uBAAuB,EAAE,CAEtD;IAID,6EAA6E;IAC7E,IAAW,cAAc,IAAI,qBAAqB,GAAG,SAAS,CAG7D;IAED,OAAO,CAAC,aAAa,CAAuB;IAE5C;;;OAGG;IACH,IAAW,YAAY,IAAI,MAAM,CAKhC;IAED;;OAEG;IACI,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAmBpD;;;;;OAKG;IACI,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO;IAkBrD;;;;;;;;;;;;;;;OAeG;IACI,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,MAAM,EAAE,CAAA;KAAE;IA2ExG;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACI,qBAAqB,CACxB,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,OAAO,EAAE,mBAAmB,GAC7B;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,cAAc,EAAE,MAAM,EAAE,CAAC;QAAC,sBAAsB,CAAC,EAAE,sBAAsB,CAAA;KAAE;IA0BjG,4FAA4F;IAC5F,OAAO,CAAC,gBAAgB;IAcxB,uEAAuE;IACvE,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED;;;OAGG;IACH,IAAW,YAAY,IAAI,OAAO,CAEjC;IAID;;;;;OAKG;IACI,cAAc,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM;IAgBzD,OAAO,CAAC,YAAY,CAAiC;IAErD;;;;OAIG;IACH,IAAW,WAAW,IAAI,gBAAgB,CAuBzC;IAED,OAAO,CAAC,uBAAuB;CAqBlC"}