@memberjunction/core-entities-server 6.2.0-edge.7 → 6.2.0-edge.8
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/dist/custom/AnchoredConversationReadSecurityHook.d.ts +22 -0
- package/dist/custom/AnchoredConversationReadSecurityHook.d.ts.map +1 -0
- package/dist/custom/AnchoredConversationReadSecurityHook.js +293 -0
- package/dist/custom/AnchoredConversationReadSecurityHook.js.map +1 -0
- package/dist/custom/MJConversationDetailEntityServer.server.d.ts +20 -30
- package/dist/custom/MJConversationDetailEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJConversationDetailEntityServer.server.js +89 -35
- package/dist/custom/MJConversationDetailEntityServer.server.js.map +1 -1
- package/dist/custom/MJConversationEntityServer.server.d.ts +19 -4
- package/dist/custom/MJConversationEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJConversationEntityServer.server.js +169 -11
- package/dist/custom/MJConversationEntityServer.server.js.map +1 -1
- package/dist/custom/MJProjectEntityServer.server.d.ts +21 -0
- package/dist/custom/MJProjectEntityServer.server.d.ts.map +1 -0
- package/dist/custom/MJProjectEntityServer.server.js +106 -0
- package/dist/custom/MJProjectEntityServer.server.js.map +1 -0
- package/dist/custom/MJQueryEntityServer.server.d.ts.map +1 -1
- package/dist/custom/MJQueryEntityServer.server.js +14 -2
- package/dist/custom/MJQueryEntityServer.server.js.map +1 -1
- package/dist/custom/RestrictedFieldCommentClassifier.d.ts +73 -0
- package/dist/custom/RestrictedFieldCommentClassifier.d.ts.map +1 -0
- package/dist/custom/RestrictedFieldCommentClassifier.js +240 -0
- package/dist/custom/RestrictedFieldCommentClassifier.js.map +1 -0
- package/dist/engines/QueryEngineServer.d.ts +4 -4
- package/dist/engines/QueryEngineServer.d.ts.map +1 -1
- package/dist/engines/QueryEngineServer.js +6 -16
- package/dist/engines/QueryEngineServer.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +30 -30
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PostRunViewHook } from '@memberjunction/core';
|
|
2
|
+
/**
|
|
3
|
+
* Server-side `PostRunView` data hook enforcing Row-Level Security (RLS) and Field-Level Security (FLS)
|
|
4
|
+
* on anchored conversations (`MJ: Conversations` where `AnchorType IS NOT NULL`) and their comment
|
|
5
|
+
* messages (`MJ: Conversation Details`).
|
|
6
|
+
*
|
|
7
|
+
* Guarantees that even a raw `RunView` call over GraphQL, REST, or server code cannot return:
|
|
8
|
+
* - Anchored conversation threads (`MJ: Conversations`) attached to a linked record that `contextUser`
|
|
9
|
+
* cannot read (entity permissions + RLS), or anchored/stamped with restricted fields (`AnchorKey` /
|
|
10
|
+
* `AnchorMetadata.restrictedFields`) that `contextUser` is denied Read access to (FLS).
|
|
11
|
+
* - Comment messages (`MJ: Conversation Details`) belonging to any such anchored conversation thread.
|
|
12
|
+
*
|
|
13
|
+
* Never mutates `results.Results` in place (safe with frozen cache entries); returns a replacement
|
|
14
|
+
* `RunViewResult` whenever rows are filtered out.
|
|
15
|
+
*/
|
|
16
|
+
export declare const AnchoredConversationPostRunViewHook: PostRunViewHook;
|
|
17
|
+
/**
|
|
18
|
+
* Ensures {@link AnchoredConversationPostRunViewHook} is registered in the global `PostRunView`
|
|
19
|
+
* data hooks store. Safe to call multiple times (idempotent).
|
|
20
|
+
*/
|
|
21
|
+
export declare function EnsureAnchoredConversationReadHookRegistered(): void;
|
|
22
|
+
//# sourceMappingURL=AnchoredConversationReadSecurityHook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnchoredConversationReadSecurityHook.d.ts","sourceRoot":"","sources":["../../src/custom/AnchoredConversationReadSecurityHook.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,eAAe,EAOlB,MAAM,sBAAsB,CAAC;AAmE9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mCAAmC,EAAE,eA0CjD,CAAC;AA4SF;;;GAGG;AACH,wBAAgB,4CAA4C,IAAI,IAAI,CAKnE"}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { BaseEntity, GetDataHooks, LogError, Metadata, RegisterDataHook, RunView, WellKnownUserSource } from '@memberjunction/core';
|
|
2
|
+
import { EscapeSQLString, NormalizeUUID } from '@memberjunction/global';
|
|
3
|
+
import { FilterAnchoredConversationsForUser } from '@memberjunction/core-entities';
|
|
4
|
+
const INTERNAL_SECURITY_QUERY_PARAMS = new WeakSet();
|
|
5
|
+
/**
|
|
6
|
+
* Resolves the entity name targeted by `params` without additional I/O.
|
|
7
|
+
*/
|
|
8
|
+
function resolveTargetEntityName(params) {
|
|
9
|
+
if (typeof params.EntityName === 'string' && params.EntityName.trim().length > 0) {
|
|
10
|
+
return params.EntityName.trim();
|
|
11
|
+
}
|
|
12
|
+
const viewEntityName = params.ViewEntity?.Get('Entity');
|
|
13
|
+
if (typeof viewEntityName === 'string' && viewEntityName.trim().length > 0) {
|
|
14
|
+
return viewEntityName.trim();
|
|
15
|
+
}
|
|
16
|
+
return '';
|
|
17
|
+
}
|
|
18
|
+
function resolveProviderForUser(contextUser) {
|
|
19
|
+
return (contextUser.OwningProvider ??
|
|
20
|
+
Metadata.Provider ?? // global-provider-ok: fallback when UserInfo was constructed without an OwningProvider
|
|
21
|
+
null);
|
|
22
|
+
}
|
|
23
|
+
function readRowField(row, fieldName) {
|
|
24
|
+
if (!row || typeof row !== 'object') {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
if (row instanceof BaseEntity) {
|
|
28
|
+
return row.Get(fieldName);
|
|
29
|
+
}
|
|
30
|
+
return row[fieldName];
|
|
31
|
+
}
|
|
32
|
+
function hasOwnOrEntityField(row, fieldName) {
|
|
33
|
+
if (!row || typeof row !== 'object') {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (row instanceof BaseEntity) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return Object.prototype.hasOwnProperty.call(row, fieldName);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Server-side `PostRunView` data hook enforcing Row-Level Security (RLS) and Field-Level Security (FLS)
|
|
43
|
+
* on anchored conversations (`MJ: Conversations` where `AnchorType IS NOT NULL`) and their comment
|
|
44
|
+
* messages (`MJ: Conversation Details`).
|
|
45
|
+
*
|
|
46
|
+
* Guarantees that even a raw `RunView` call over GraphQL, REST, or server code cannot return:
|
|
47
|
+
* - Anchored conversation threads (`MJ: Conversations`) attached to a linked record that `contextUser`
|
|
48
|
+
* cannot read (entity permissions + RLS), or anchored/stamped with restricted fields (`AnchorKey` /
|
|
49
|
+
* `AnchorMetadata.restrictedFields`) that `contextUser` is denied Read access to (FLS).
|
|
50
|
+
* - Comment messages (`MJ: Conversation Details`) belonging to any such anchored conversation thread.
|
|
51
|
+
*
|
|
52
|
+
* Never mutates `results.Results` in place (safe with frozen cache entries); returns a replacement
|
|
53
|
+
* `RunViewResult` whenever rows are filtered out.
|
|
54
|
+
*/
|
|
55
|
+
export const AnchoredConversationPostRunViewHook = async (params, results, contextUser) => {
|
|
56
|
+
if (!results?.Success ||
|
|
57
|
+
!Array.isArray(results.Results) ||
|
|
58
|
+
results.Results.length === 0 ||
|
|
59
|
+
!contextUser ||
|
|
60
|
+
INTERNAL_SECURITY_QUERY_PARAMS.has(params)) {
|
|
61
|
+
return results;
|
|
62
|
+
}
|
|
63
|
+
const entityName = resolveTargetEntityName(params);
|
|
64
|
+
if (entityName !== 'MJ: Conversations' && entityName !== 'MJ: Conversation Details') {
|
|
65
|
+
return results;
|
|
66
|
+
}
|
|
67
|
+
const provider = resolveProviderForUser(contextUser);
|
|
68
|
+
if (!provider || provider.ProviderType !== 'Database') {
|
|
69
|
+
return results;
|
|
70
|
+
}
|
|
71
|
+
if (WellKnownUserSource.Instance.IsSystemUser(contextUser)) {
|
|
72
|
+
return results;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
if (entityName === 'MJ: Conversations') {
|
|
76
|
+
return await filterConversationsRunViewResult(provider, contextUser, results);
|
|
77
|
+
}
|
|
78
|
+
return await filterConversationDetailsRunViewResult(provider, contextUser, results);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
LogError(`AnchoredConversationPostRunViewHook failed on ${entityName}: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
+
return results;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
async function filterConversationsRunViewResult(provider, contextUser, results) {
|
|
86
|
+
const rows = results.Results;
|
|
87
|
+
// Check if any row is missing security columns due to a narrow `Fields` projection
|
|
88
|
+
const missingMetadataIds = [];
|
|
89
|
+
for (const row of rows) {
|
|
90
|
+
const hasSecurityCols = hasOwnOrEntityField(row, 'AnchorType') &&
|
|
91
|
+
hasOwnOrEntityField(row, 'LinkedEntityID') &&
|
|
92
|
+
hasOwnOrEntityField(row, 'LinkedRecordID') &&
|
|
93
|
+
hasOwnOrEntityField(row, 'AnchorKey') &&
|
|
94
|
+
hasOwnOrEntityField(row, 'AnchorMetadata');
|
|
95
|
+
if (!hasSecurityCols) {
|
|
96
|
+
const id = String(readRowField(row, 'ID') ?? '').trim();
|
|
97
|
+
if (id) {
|
|
98
|
+
missingMetadataIds.push(id);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const hydratedById = new Map();
|
|
103
|
+
if (missingMetadataIds.length > 0) {
|
|
104
|
+
const fetched = await fetchConversationsSecurityMetadataByIds(provider, contextUser, missingMetadataIds, false);
|
|
105
|
+
for (const item of fetched) {
|
|
106
|
+
if (item.ID) {
|
|
107
|
+
hydratedById.set(NormalizeUUID(item.ID), item);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Collect anchored candidates
|
|
112
|
+
const anchoredCandidates = [];
|
|
113
|
+
for (let i = 0; i < rows.length; i++) {
|
|
114
|
+
const row = rows[i];
|
|
115
|
+
const rowId = String(readRowField(row, 'ID') ?? '').trim();
|
|
116
|
+
const hydrated = rowId ? hydratedById.get(NormalizeUUID(rowId)) : undefined;
|
|
117
|
+
const anchorType = (hydrated ? hydrated.AnchorType : readRowField(row, 'AnchorType'));
|
|
118
|
+
const linkedEntityId = (hydrated ? hydrated.LinkedEntityID : readRowField(row, 'LinkedEntityID'));
|
|
119
|
+
const linkedRecordId = (hydrated ? hydrated.LinkedRecordID : readRowField(row, 'LinkedRecordID'));
|
|
120
|
+
if (!anchorType && (!linkedEntityId || !linkedRecordId)) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
// Only filter anchored comment threads (or conversations linked to a record that carry an anchor)
|
|
124
|
+
if (!anchorType) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const anchorKey = (hydrated ? hydrated.AnchorKey : readRowField(row, 'AnchorKey'));
|
|
128
|
+
const anchorMetadata = (hydrated ? hydrated.AnchorMetadata : readRowField(row, 'AnchorMetadata'));
|
|
129
|
+
const anchorMetadataObject = (hydrated ? hydrated.AnchorMetadataObject : readRowField(row, 'AnchorMetadataObject'));
|
|
130
|
+
anchoredCandidates.push({
|
|
131
|
+
__rowIndex: i,
|
|
132
|
+
ID: rowId || undefined,
|
|
133
|
+
LinkedEntityID: linkedEntityId ?? null,
|
|
134
|
+
LinkedRecordID: linkedRecordId ?? null,
|
|
135
|
+
AnchorType: anchorType,
|
|
136
|
+
AnchorKey: anchorKey ?? null,
|
|
137
|
+
AnchorMetadata: anchorMetadata ?? null,
|
|
138
|
+
AnchorMetadataObject: anchorMetadataObject ?? null
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
if (anchoredCandidates.length === 0) {
|
|
142
|
+
return results;
|
|
143
|
+
}
|
|
144
|
+
const allowedAnchored = await FilterAnchoredConversationsForUser(provider, contextUser, anchoredCandidates);
|
|
145
|
+
if (allowedAnchored.length === anchoredCandidates.length) {
|
|
146
|
+
return results;
|
|
147
|
+
}
|
|
148
|
+
const allowedRowIndexes = new Set(allowedAnchored.map((c) => c.__rowIndex));
|
|
149
|
+
const anchoredRowIndexes = new Set(anchoredCandidates.map((c) => c.__rowIndex));
|
|
150
|
+
const filteredRows = rows.filter((_row, index) => !anchoredRowIndexes.has(index) || allowedRowIndexes.has(index));
|
|
151
|
+
const removedCount = rows.length - filteredRows.length;
|
|
152
|
+
return {
|
|
153
|
+
...results,
|
|
154
|
+
Results: filteredRows,
|
|
155
|
+
RowCount: filteredRows.length,
|
|
156
|
+
TotalRowCount: Math.max(filteredRows.length, (results.TotalRowCount ?? results.RowCount ?? rows.length) - removedCount)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
async function filterConversationDetailsRunViewResult(provider, contextUser, results) {
|
|
160
|
+
const rows = results.Results;
|
|
161
|
+
const missingConvIdDetailIds = [];
|
|
162
|
+
for (const row of rows) {
|
|
163
|
+
if (!hasOwnOrEntityField(row, 'ConversationID')) {
|
|
164
|
+
const detailId = String(readRowField(row, 'ID') ?? '').trim();
|
|
165
|
+
if (detailId) {
|
|
166
|
+
missingConvIdDetailIds.push(detailId);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const convIdByDetailId = new Map();
|
|
171
|
+
if (missingConvIdDetailIds.length > 0) {
|
|
172
|
+
const fetchedDetails = await fetchDetailConversationIdsByIds(provider, contextUser, missingConvIdDetailIds);
|
|
173
|
+
for (const d of fetchedDetails) {
|
|
174
|
+
if (d.ID && d.ConversationID) {
|
|
175
|
+
convIdByDetailId.set(NormalizeUUID(d.ID), d.ConversationID);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const conversationIdsByNorm = new Map();
|
|
180
|
+
const rowConvNormByIndex = new Map();
|
|
181
|
+
for (let i = 0; i < rows.length; i++) {
|
|
182
|
+
const row = rows[i];
|
|
183
|
+
let convId = String(readRowField(row, 'ConversationID') ?? '').trim();
|
|
184
|
+
if (!convId) {
|
|
185
|
+
const detailId = String(readRowField(row, 'ID') ?? '').trim();
|
|
186
|
+
if (detailId) {
|
|
187
|
+
convId = convIdByDetailId.get(NormalizeUUID(detailId)) ?? '';
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (convId) {
|
|
191
|
+
const norm = NormalizeUUID(convId);
|
|
192
|
+
conversationIdsByNorm.set(norm, convId);
|
|
193
|
+
rowConvNormByIndex.set(i, norm);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (conversationIdsByNorm.size === 0) {
|
|
197
|
+
return results;
|
|
198
|
+
}
|
|
199
|
+
// Fetch only parent conversations that are anchored (`AnchorType IS NOT NULL`)
|
|
200
|
+
const anchoredParents = await fetchConversationsSecurityMetadataByIds(provider, contextUser, [...conversationIdsByNorm.values()], true);
|
|
201
|
+
if (anchoredParents.length === 0) {
|
|
202
|
+
return results;
|
|
203
|
+
}
|
|
204
|
+
const allowedParents = await FilterAnchoredConversationsForUser(provider, contextUser, anchoredParents);
|
|
205
|
+
if (allowedParents.length === anchoredParents.length) {
|
|
206
|
+
return results;
|
|
207
|
+
}
|
|
208
|
+
const allowedNormIds = new Set(allowedParents.map((c) => NormalizeUUID(String(c.ID ?? ''))));
|
|
209
|
+
const deniedNormIds = new Set();
|
|
210
|
+
for (const parent of anchoredParents) {
|
|
211
|
+
const norm = NormalizeUUID(String(parent.ID ?? ''));
|
|
212
|
+
if (norm && !allowedNormIds.has(norm)) {
|
|
213
|
+
deniedNormIds.add(norm);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (deniedNormIds.size === 0) {
|
|
217
|
+
return results;
|
|
218
|
+
}
|
|
219
|
+
const filteredRows = rows.filter((_row, index) => {
|
|
220
|
+
const convNorm = rowConvNormByIndex.get(index);
|
|
221
|
+
return !convNorm || !deniedNormIds.has(convNorm);
|
|
222
|
+
});
|
|
223
|
+
const removedCount = rows.length - filteredRows.length;
|
|
224
|
+
return {
|
|
225
|
+
...results,
|
|
226
|
+
Results: filteredRows,
|
|
227
|
+
RowCount: filteredRows.length,
|
|
228
|
+
TotalRowCount: Math.max(filteredRows.length, (results.TotalRowCount ?? results.RowCount ?? rows.length) - removedCount)
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
async function fetchConversationsSecurityMetadataByIds(provider, contextUser, conversationIds, anchoredOnly) {
|
|
232
|
+
const uniqueEscaped = Array.from(new Set(conversationIds
|
|
233
|
+
.map((id) => id.trim())
|
|
234
|
+
.filter((id) => id.length > 0)
|
|
235
|
+
.map((id) => `'${EscapeSQLString(id)}'`)));
|
|
236
|
+
if (uniqueEscaped.length === 0) {
|
|
237
|
+
return [];
|
|
238
|
+
}
|
|
239
|
+
const systemUser = (await WellKnownUserSource.Instance.GetSystemUser(provider)) ?? contextUser;
|
|
240
|
+
const rv = RunView.FromMetadataProvider(provider);
|
|
241
|
+
const filterClauses = [`ID IN (${uniqueEscaped.join(',')})`];
|
|
242
|
+
if (anchoredOnly) {
|
|
243
|
+
filterClauses.push(`AnchorType IS NOT NULL`);
|
|
244
|
+
}
|
|
245
|
+
const params = {
|
|
246
|
+
EntityName: 'MJ: Conversations',
|
|
247
|
+
ExtraFilter: filterClauses.join(' AND '),
|
|
248
|
+
Fields: ['ID', 'LinkedEntityID', 'LinkedRecordID', 'AnchorType', 'AnchorKey', 'AnchorMetadata'],
|
|
249
|
+
ResultType: 'simple'
|
|
250
|
+
};
|
|
251
|
+
INTERNAL_SECURITY_QUERY_PARAMS.add(params);
|
|
252
|
+
const res = await rv.RunView(params, systemUser);
|
|
253
|
+
if (!res?.Success || !Array.isArray(res.Results)) {
|
|
254
|
+
return [];
|
|
255
|
+
}
|
|
256
|
+
return res.Results;
|
|
257
|
+
}
|
|
258
|
+
async function fetchDetailConversationIdsByIds(provider, contextUser, detailIds) {
|
|
259
|
+
const uniqueEscaped = Array.from(new Set(detailIds
|
|
260
|
+
.map((id) => id.trim())
|
|
261
|
+
.filter((id) => id.length > 0)
|
|
262
|
+
.map((id) => `'${EscapeSQLString(id)}'`)));
|
|
263
|
+
if (uniqueEscaped.length === 0) {
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
const systemUser = (await WellKnownUserSource.Instance.GetSystemUser(provider)) ?? contextUser;
|
|
267
|
+
const rv = RunView.FromMetadataProvider(provider);
|
|
268
|
+
const params = {
|
|
269
|
+
EntityName: 'MJ: Conversation Details',
|
|
270
|
+
ExtraFilter: `ID IN (${uniqueEscaped.join(',')})`,
|
|
271
|
+
Fields: ['ID', 'ConversationID'],
|
|
272
|
+
ResultType: 'simple'
|
|
273
|
+
};
|
|
274
|
+
INTERNAL_SECURITY_QUERY_PARAMS.add(params);
|
|
275
|
+
const res = await rv.RunView(params, systemUser);
|
|
276
|
+
if (!res?.Success || !Array.isArray(res.Results)) {
|
|
277
|
+
return [];
|
|
278
|
+
}
|
|
279
|
+
return res.Results;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Ensures {@link AnchoredConversationPostRunViewHook} is registered in the global `PostRunView`
|
|
283
|
+
* data hooks store. Safe to call multiple times (idempotent).
|
|
284
|
+
*/
|
|
285
|
+
export function EnsureAnchoredConversationReadHookRegistered() {
|
|
286
|
+
const existing = GetDataHooks('PostRunView');
|
|
287
|
+
if (!existing.includes(AnchoredConversationPostRunViewHook)) {
|
|
288
|
+
RegisterDataHook('PostRunView', AnchoredConversationPostRunViewHook);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// Auto-register when @memberjunction/core-entities-server is loaded
|
|
292
|
+
EnsureAnchoredConversationReadHookRegistered();
|
|
293
|
+
//# sourceMappingURL=AnchoredConversationReadSecurityHook.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnchoredConversationReadSecurityHook.js","sourceRoot":"","sources":["../../src/custom/AnchoredConversationReadSecurityHook.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,48 +1,38 @@
|
|
|
1
1
|
import { EntitySaveOptions } from "@memberjunction/core";
|
|
2
2
|
import { MJConversationDetailEntityExtended } from "@memberjunction/core-entities";
|
|
3
3
|
/**
|
|
4
|
-
* Server-side subclass of MJConversationDetailEntity that
|
|
5
|
-
* when the original message content has been modified
|
|
6
|
-
* each new message.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* summary is explicitly lossy; agents page in exact rows via the retrieval tools).
|
|
16
|
-
*
|
|
17
|
-
* Extends {@link MJConversationDetailEntityExtended} rather than the generated entity
|
|
18
|
-
* directly. Both classes register for `BaseEntity` under the key
|
|
19
|
-
* `'MJ: Conversation Details'`, and `@RegisterClass` passes `priority = 0`, which routes
|
|
20
|
-
* to the auto-increment branch — so whichever registers LAST wins outright. On the server
|
|
21
|
-
* this package loads after `@memberjunction/core-entities`, which meant this class replaced
|
|
22
|
-
* the Extended one and its `Save`/`Delete` permission gate never ran. That gate is the check
|
|
23
|
-
* that only a conversation's owner may set `UserRating` / `UserFeedback`, and that a
|
|
24
|
-
* non-owner without a resource grant cannot write at all — and it is explicitly designed to
|
|
25
|
-
* run server-side (`ProviderType === 'Database'`), which is exactly where it was being
|
|
26
|
-
* shadowed out. Inheriting composes the two behaviors instead: this class flags the edit,
|
|
27
|
-
* then delegates to the permission gate via `super.Save`.
|
|
28
|
-
*
|
|
29
|
-
* A new row keeps the `BranchID` its writer set (null is Main). On create, a non-null `BranchID`
|
|
30
|
-
* must name a fork of the same conversation; otherwise `Save` returns `false` with a result
|
|
31
|
-
* entry and nothing is written.
|
|
4
|
+
* Server-side subclass of MJConversationDetailEntity that:
|
|
5
|
+
* 1. Automatically tracks when the original message content has been modified (`OriginalMessageChanged`).
|
|
6
|
+
* 2. Checks the fork (`BranchID`) of each new message on create.
|
|
7
|
+
* 3. Inspects comments posted on anchored conversations (`LinkedEntityID` + `LinkedRecordID`)
|
|
8
|
+
* when the target entity has `FieldLevelSecurityApplies === true`:
|
|
9
|
+
* - Runs two-stage restricted-field detection (deterministic field/label/value matching + semantic LLM classifier).
|
|
10
|
+
* - Refuses the save if the authoring user is denied Read access on a restricted field referenced by their comment.
|
|
11
|
+
* - Stamps any detected restricted field names into the parent conversation's
|
|
12
|
+
* `AnchorMetadataObject.restrictedFields` so all subsequent reads filter deterministically in 0ms.
|
|
13
|
+
* 4. Delegates to {@link MJConversationDetailEntityExtended} for write-access, authorship,
|
|
14
|
+
* server-authored reply validation, and `@mention` notification dispatch.
|
|
32
15
|
*/
|
|
33
16
|
export declare class MJConversationDetailEntityServer extends MJConversationDetailEntityExtended {
|
|
34
17
|
/**
|
|
35
18
|
* Sets the OriginalMessageChanged flag on a genuine message edit, checks a new row's fork,
|
|
36
|
-
* then calls super.Save().
|
|
19
|
+
* runs FLS restricted-field inspection for anchored conversations, then calls super.Save().
|
|
37
20
|
*/
|
|
38
21
|
Save(options?: EntitySaveOptions): Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* When saving a message in an anchored conversation (`LinkedEntityID` + `LinkedRecordID`) whose
|
|
24
|
+
* target entity has `FieldLevelSecurityApplies === true`, runs {@link InspectAndStampRestrictedFieldsForComment}
|
|
25
|
+
* to block restricted-field probing by unauthorized authors and stamp `restrictedFields` onto the
|
|
26
|
+
* parent conversation's `AnchorMetadataObject`.
|
|
27
|
+
*/
|
|
28
|
+
CheckAndStampAnchoredRestrictedFields(): Promise<string | null>;
|
|
39
29
|
/**
|
|
40
30
|
* On create, a non-null BranchID must name a fork of the same conversation. Returns why the save
|
|
41
31
|
* must fail, or null when it may go on. An existing row, a Main row (null BranchID), a row with no
|
|
42
32
|
* ConversationID and a save with no context user are not checked.
|
|
43
33
|
*/
|
|
44
34
|
CheckBranchOnCreate(): Promise<string | null>;
|
|
45
|
-
/** Records a failed create in the result history, so callers see why nothing was written. */
|
|
35
|
+
/** Records a failed create/save in the result history, so callers see why nothing was written. */
|
|
46
36
|
private recordCreateFailure;
|
|
47
37
|
/**
|
|
48
38
|
* True when this save is a genuine Message EDIT on an existing record — not one of
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MJConversationDetailEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJConversationDetailEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"MJConversationDetailEntityServer.server.d.ts","sourceRoot":"","sources":["../../src/custom/MJConversationDetailEntityServer.server.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,iBAAiB,EAKpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,kCAAkC,EAAwB,MAAM,+BAA+B,CAAC;AAGzG;;;;;;;;;;;;GAYG;AACH,qBACa,gCAAiC,SAAQ,kCAAkC;IACpF;;;OAGG;IACY,IAAI,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBlE;;;;;OAKG;IACU,qCAAqC,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAsE5E;;;;OAIG;IACU,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAgC1D,kGAAkG;IAClG,OAAO,CAAC,mBAAmB;IAU3B;;;;;;;;;;;OAWG;IACI,gCAAgC,IAAI,OAAO;CAiBrD"}
|
|
@@ -4,43 +4,28 @@ 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
|
-
|
|
7
|
+
var MJConversationDetailEntityServer_1;
|
|
8
|
+
import { BaseEntity, BaseEntityResult, LogError, RunView, WellKnownUserSource } from "@memberjunction/core";
|
|
8
9
|
import { EscapeSQLString, RegisterClass, UUIDsEqual } from "@memberjunction/global";
|
|
9
10
|
import { MJConversationDetailEntityExtended } from "@memberjunction/core-entities";
|
|
11
|
+
import { InspectAndStampRestrictedFieldsForComment } from "./RestrictedFieldCommentClassifier.js";
|
|
10
12
|
/**
|
|
11
|
-
* Server-side subclass of MJConversationDetailEntity that
|
|
12
|
-
* when the original message content has been modified
|
|
13
|
-
* each new message.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* summary is explicitly lossy; agents page in exact rows via the retrieval tools).
|
|
23
|
-
*
|
|
24
|
-
* Extends {@link MJConversationDetailEntityExtended} rather than the generated entity
|
|
25
|
-
* directly. Both classes register for `BaseEntity` under the key
|
|
26
|
-
* `'MJ: Conversation Details'`, and `@RegisterClass` passes `priority = 0`, which routes
|
|
27
|
-
* to the auto-increment branch — so whichever registers LAST wins outright. On the server
|
|
28
|
-
* this package loads after `@memberjunction/core-entities`, which meant this class replaced
|
|
29
|
-
* the Extended one and its `Save`/`Delete` permission gate never ran. That gate is the check
|
|
30
|
-
* that only a conversation's owner may set `UserRating` / `UserFeedback`, and that a
|
|
31
|
-
* non-owner without a resource grant cannot write at all — and it is explicitly designed to
|
|
32
|
-
* run server-side (`ProviderType === 'Database'`), which is exactly where it was being
|
|
33
|
-
* shadowed out. Inheriting composes the two behaviors instead: this class flags the edit,
|
|
34
|
-
* then delegates to the permission gate via `super.Save`.
|
|
35
|
-
*
|
|
36
|
-
* A new row keeps the `BranchID` its writer set (null is Main). On create, a non-null `BranchID`
|
|
37
|
-
* must name a fork of the same conversation; otherwise `Save` returns `false` with a result
|
|
38
|
-
* entry and nothing is written.
|
|
13
|
+
* Server-side subclass of MJConversationDetailEntity that:
|
|
14
|
+
* 1. Automatically tracks when the original message content has been modified (`OriginalMessageChanged`).
|
|
15
|
+
* 2. Checks the fork (`BranchID`) of each new message on create.
|
|
16
|
+
* 3. Inspects comments posted on anchored conversations (`LinkedEntityID` + `LinkedRecordID`)
|
|
17
|
+
* when the target entity has `FieldLevelSecurityApplies === true`:
|
|
18
|
+
* - Runs two-stage restricted-field detection (deterministic field/label/value matching + semantic LLM classifier).
|
|
19
|
+
* - Refuses the save if the authoring user is denied Read access on a restricted field referenced by their comment.
|
|
20
|
+
* - Stamps any detected restricted field names into the parent conversation's
|
|
21
|
+
* `AnchorMetadataObject.restrictedFields` so all subsequent reads filter deterministically in 0ms.
|
|
22
|
+
* 4. Delegates to {@link MJConversationDetailEntityExtended} for write-access, authorship,
|
|
23
|
+
* server-authored reply validation, and `@mention` notification dispatch.
|
|
39
24
|
*/
|
|
40
|
-
let MJConversationDetailEntityServer = class MJConversationDetailEntityServer extends MJConversationDetailEntityExtended {
|
|
25
|
+
let MJConversationDetailEntityServer = MJConversationDetailEntityServer_1 = class MJConversationDetailEntityServer extends MJConversationDetailEntityExtended {
|
|
41
26
|
/**
|
|
42
27
|
* Sets the OriginalMessageChanged flag on a genuine message edit, checks a new row's fork,
|
|
43
|
-
* then calls super.Save().
|
|
28
|
+
* runs FLS restricted-field inspection for anchored conversations, then calls super.Save().
|
|
44
29
|
*/
|
|
45
30
|
async Save(options) {
|
|
46
31
|
if (this.ShouldFlagOriginalMessageChanged()) {
|
|
@@ -51,8 +36,77 @@ let MJConversationDetailEntityServer = class MJConversationDetailEntityServer ex
|
|
|
51
36
|
this.recordCreateFailure(problem);
|
|
52
37
|
return false;
|
|
53
38
|
}
|
|
39
|
+
const checkFls = this.CheckAndStampAnchoredRestrictedFields ??
|
|
40
|
+
MJConversationDetailEntityServer_1.prototype.CheckAndStampAnchoredRestrictedFields;
|
|
41
|
+
const flsRefusal = await checkFls.call(this);
|
|
42
|
+
if (flsRefusal) {
|
|
43
|
+
this.recordCreateFailure(flsRefusal, this.IsSaved ? 'update' : 'create');
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
54
46
|
return super.Save(options);
|
|
55
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* When saving a message in an anchored conversation (`LinkedEntityID` + `LinkedRecordID`) whose
|
|
50
|
+
* target entity has `FieldLevelSecurityApplies === true`, runs {@link InspectAndStampRestrictedFieldsForComment}
|
|
51
|
+
* to block restricted-field probing by unauthorized authors and stamp `restrictedFields` onto the
|
|
52
|
+
* parent conversation's `AnchorMetadataObject`.
|
|
53
|
+
*/
|
|
54
|
+
async CheckAndStampAnchoredRestrictedFields() {
|
|
55
|
+
const provider = this.ProviderToUse;
|
|
56
|
+
if (provider?.ProviderType !== 'Database') {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const user = this.ContextCurrentUser;
|
|
60
|
+
if (!user || !this.ConversationID) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
// Only inspect on new messages or when Message text is being modified
|
|
64
|
+
if (this.IsSaved && !this.GetFieldByName('Message')?.Dirty) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const systemUser = (await WellKnownUserSource.Instance.GetSystemUser(provider)) ?? user;
|
|
69
|
+
const conversation = await provider.GetEntityObject('MJ: Conversations', systemUser);
|
|
70
|
+
const loaded = await conversation.Load(this.ConversationID);
|
|
71
|
+
if (!loaded || !conversation.AnchorType || !conversation.LinkedEntityID || !conversation.LinkedRecordID) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
const entityInfo = (typeof provider.EntityByID === 'function'
|
|
75
|
+
? provider.EntityByID(conversation.LinkedEntityID)
|
|
76
|
+
: undefined) ??
|
|
77
|
+
provider.Entities?.find((e) => UUIDsEqual(e.ID, conversation.LinkedEntityID)) ??
|
|
78
|
+
null;
|
|
79
|
+
if (!entityInfo) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
if (!WellKnownUserSource.Instance.IsSystemUser(user)) {
|
|
83
|
+
if (entityInfo.AllowRecordComments === false) {
|
|
84
|
+
return `Record commenting is disabled for ${entityInfo.DisplayNameOrName}.`;
|
|
85
|
+
}
|
|
86
|
+
if (typeof entityInfo.CanUserComment === 'function' && !entityInfo.CanUserComment(user)) {
|
|
87
|
+
return `You do not have permission to post comments on ${entityInfo.DisplayNameOrName} records.`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!entityInfo.FieldLevelSecurityApplies) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const inspection = await InspectAndStampRestrictedFieldsForComment({
|
|
94
|
+
Provider: provider,
|
|
95
|
+
ContextUser: user,
|
|
96
|
+
Conversation: conversation,
|
|
97
|
+
EntityInfo: entityInfo,
|
|
98
|
+
MessageText: this.Message
|
|
99
|
+
});
|
|
100
|
+
if (!inspection.Allowed) {
|
|
101
|
+
return inspection.Reason ?? 'You do not have permission to comment on this restricted field.';
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
LogError(`MJConversationDetailEntityServer.CheckAndStampAnchoredRestrictedFields failed on conversation ${this.ConversationID}: ${error instanceof Error ? error.message : String(error)}`);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
56
110
|
/**
|
|
57
111
|
* On create, a non-null BranchID must name a fork of the same conversation. Returns why the save
|
|
58
112
|
* must fail, or null when it may go on. An existing row, a Main row (null BranchID), a row with no
|
|
@@ -90,11 +144,11 @@ let MJConversationDetailEntityServer = class MJConversationDetailEntityServer ex
|
|
|
90
144
|
return 'Unable to check the fork of this message.';
|
|
91
145
|
}
|
|
92
146
|
}
|
|
93
|
-
/** Records a failed create in the result history, so callers see why nothing was written. */
|
|
94
|
-
recordCreateFailure(message) {
|
|
147
|
+
/** Records a failed create/save in the result history, so callers see why nothing was written. */
|
|
148
|
+
recordCreateFailure(message, type = 'create') {
|
|
95
149
|
const result = new BaseEntityResult();
|
|
96
150
|
result.Success = false;
|
|
97
|
-
result.Type =
|
|
151
|
+
result.Type = type;
|
|
98
152
|
result.Message = message;
|
|
99
153
|
result.StartedAt = new Date();
|
|
100
154
|
result.EndedAt = new Date();
|
|
@@ -130,7 +184,7 @@ let MJConversationDetailEntityServer = class MJConversationDetailEntityServer ex
|
|
|
130
184
|
return true;
|
|
131
185
|
}
|
|
132
186
|
};
|
|
133
|
-
MJConversationDetailEntityServer = __decorate([
|
|
187
|
+
MJConversationDetailEntityServer = MJConversationDetailEntityServer_1 = __decorate([
|
|
134
188
|
RegisterClass(BaseEntity, "MJ: Conversation Details")
|
|
135
189
|
], MJConversationDetailEntityServer);
|
|
136
190
|
export { MJConversationDetailEntityServer };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MJConversationDetailEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJConversationDetailEntityServer.server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MJConversationDetailEntityServer.server.js","sourceRoot":"","sources":["../../src/custom/MJConversationDetailEntityServer.server.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,28 @@
|
|
|
1
|
-
import { ValidationResult } from '@memberjunction/core';
|
|
1
|
+
import { EntitySaveOptions, ValidationResult } from '@memberjunction/core';
|
|
2
2
|
import { MJConversationEntity } from '@memberjunction/core-entities';
|
|
3
3
|
/**
|
|
4
4
|
* Server-side `MJ: Conversations` entity.
|
|
5
5
|
*
|
|
6
|
-
* Keeps `ExternalID` — the scope identity an anonymous visitor's realtime session is owned through — out
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* 1. Keeps `ExternalID` — the scope identity an anonymous visitor's realtime session is owned through — out
|
|
7
|
+
* of an anonymous visitor's reach once the conversation exists. See {@link CheckAnonymousOwnershipKeyChange}.
|
|
8
|
+
* 2. Keeps who a conversation belongs to and who it is shared with in its owner's hands: see
|
|
9
|
+
* {@link checkOwnerOnlyChange}. Uses the synchronous `Validate()` so the rules run on every save, like
|
|
10
|
+
* `MJAIAgentSessionEntityServer`.
|
|
11
|
+
* 3. Saves `AdditionalData` as compact JSON when it holds an object, so the stored text always uses the spelling
|
|
12
|
+
* the conversation list's SQL filter matches (`INHERIT_FOLDER_ACCESS_SQL_CLAUSE`), and refuses a value the filter
|
|
13
|
+
* and the parser would still read differently: see {@link checkInheritanceReadingsAgree}.
|
|
14
|
+
* 4. Enforces Row-Level Security (RLS) and Field-Level Security (FLS) on anchored conversations
|
|
15
|
+
* (`LinkedEntityID` + `LinkedRecordID` / `AnchorType`):
|
|
16
|
+
* - Verifies the saving user can read the linked record (`UserCanReadLinkedRecord`).
|
|
17
|
+
* - Verifies the saving user is not denied Read access on the anchored field or `restrictedFields`
|
|
18
|
+
* (`IsConversationHiddenByFieldSecurity`).
|
|
19
|
+
* - When `AnchorType === 'Field'` targets an FLS-governed field (`HasFieldPermissions === true`),
|
|
20
|
+
* automatically stamps that canonical field name into `AnchorMetadataObject.restrictedFields`.
|
|
9
21
|
*/
|
|
10
22
|
export declare class MJConversationEntityServer extends MJConversationEntity {
|
|
11
23
|
Validate(): ValidationResult;
|
|
24
|
+
Save(options?: EntitySaveOptions): Promise<boolean>;
|
|
25
|
+
private checkAnchoredConversationAccessAndStampMetadata;
|
|
26
|
+
private recordSaveDenied;
|
|
12
27
|
}
|
|
13
28
|
//# sourceMappingURL=MJConversationEntityServer.server.d.ts.map
|