@memberjunction/core 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.
- package/dist/generic/BaseEntitySaveQueue.d.ts +22 -0
- package/dist/generic/BaseEntitySaveQueue.d.ts.map +1 -1
- package/dist/generic/BaseEntitySaveQueue.js +43 -4
- package/dist/generic/BaseEntitySaveQueue.js.map +1 -1
- package/dist/generic/JSONType-interfaces/IEntityConfiguration.d.ts +18 -0
- package/dist/generic/JSONType-interfaces/IEntityConfiguration.d.ts.map +1 -1
- package/dist/generic/alternateBaseViews.d.ts +119 -0
- package/dist/generic/alternateBaseViews.d.ts.map +1 -0
- package/dist/generic/alternateBaseViews.js +223 -0
- package/dist/generic/alternateBaseViews.js.map +1 -0
- package/dist/generic/baseEngine.d.ts +82 -14
- package/dist/generic/baseEngine.d.ts.map +1 -1
- package/dist/generic/baseEngine.js +311 -46
- package/dist/generic/baseEngine.js.map +1 -1
- package/dist/generic/baseEngineRegistry.d.ts.map +1 -1
- package/dist/generic/baseEngineRegistry.js +2 -1
- package/dist/generic/baseEngineRegistry.js.map +1 -1
- package/dist/generic/baseEntity.d.ts +194 -14
- package/dist/generic/baseEntity.d.ts.map +1 -1
- package/dist/generic/baseEntity.js +638 -218
- package/dist/generic/baseEntity.js.map +1 -1
- package/dist/generic/baseInfo.d.ts +31 -3
- package/dist/generic/baseInfo.d.ts.map +1 -1
- package/dist/generic/baseInfo.js +138 -40
- package/dist/generic/baseInfo.js.map +1 -1
- package/dist/generic/contextTokens.d.ts +136 -0
- package/dist/generic/contextTokens.d.ts.map +1 -0
- package/dist/generic/contextTokens.js +319 -0
- package/dist/generic/contextTokens.js.map +1 -0
- package/dist/generic/databaseProviderBase.d.ts +103 -13
- package/dist/generic/databaseProviderBase.d.ts.map +1 -1
- package/dist/generic/databaseProviderBase.js +321 -77
- package/dist/generic/databaseProviderBase.js.map +1 -1
- package/dist/generic/embeddedRecord.d.ts +6 -0
- package/dist/generic/embeddedRecord.d.ts.map +1 -1
- package/dist/generic/embeddedRecord.js +15 -1
- package/dist/generic/embeddedRecord.js.map +1 -1
- package/dist/generic/entityCompanion.d.ts +7 -0
- package/dist/generic/entityCompanion.d.ts.map +1 -1
- package/dist/generic/entityCompanion.js +8 -0
- package/dist/generic/entityCompanion.js.map +1 -1
- package/dist/generic/entityConfiguration.d.ts +8 -2
- package/dist/generic/entityConfiguration.d.ts.map +1 -1
- package/dist/generic/entityConfiguration.js +9 -0
- package/dist/generic/entityConfiguration.js.map +1 -1
- package/dist/generic/entityInfo.d.ts +306 -12
- package/dist/generic/entityInfo.d.ts.map +1 -1
- package/dist/generic/entityInfo.js +894 -92
- package/dist/generic/entityInfo.js.map +1 -1
- package/dist/generic/entityValueFieldSecurity.d.ts +139 -0
- package/dist/generic/entityValueFieldSecurity.d.ts.map +1 -0
- package/dist/generic/entityValueFieldSecurity.js +250 -0
- package/dist/generic/entityValueFieldSecurity.js.map +1 -0
- package/dist/generic/fieldSecuritySQLGate.d.ts +350 -0
- package/dist/generic/fieldSecuritySQLGate.d.ts.map +1 -0
- package/dist/generic/fieldSecuritySQLGate.js +898 -0
- package/dist/generic/fieldSecuritySQLGate.js.map +1 -0
- package/dist/generic/indexRestrictedFields.d.ts +47 -0
- package/dist/generic/indexRestrictedFields.d.ts.map +1 -0
- package/dist/generic/indexRestrictedFields.js +129 -0
- package/dist/generic/indexRestrictedFields.js.map +1 -0
- package/dist/generic/infoProviderResolution.d.ts +16 -0
- package/dist/generic/infoProviderResolution.d.ts.map +1 -0
- package/dist/generic/infoProviderResolution.js +17 -0
- package/dist/generic/infoProviderResolution.js.map +1 -0
- package/dist/generic/interfaces.d.ts +39 -2
- package/dist/generic/interfaces.d.ts.map +1 -1
- package/dist/generic/interfaces.js.map +1 -1
- package/dist/generic/localCacheManager.d.ts +318 -51
- package/dist/generic/localCacheManager.d.ts.map +1 -1
- package/dist/generic/localCacheManager.js +675 -109
- package/dist/generic/localCacheManager.js.map +1 -1
- package/dist/generic/logging.d.ts.map +1 -1
- package/dist/generic/logging.js +7 -1
- package/dist/generic/logging.js.map +1 -1
- package/dist/generic/metadata.d.ts +3 -2
- package/dist/generic/metadata.d.ts.map +1 -1
- package/dist/generic/metadata.js +5 -2
- package/dist/generic/metadata.js.map +1 -1
- package/dist/generic/mjapiUrl.d.ts +21 -0
- package/dist/generic/mjapiUrl.d.ts.map +1 -0
- package/dist/generic/mjapiUrl.js +25 -0
- package/dist/generic/mjapiUrl.js.map +1 -0
- package/dist/generic/platformSQL.d.ts +54 -0
- package/dist/generic/platformSQL.d.ts.map +1 -1
- package/dist/generic/platformSQL.js +55 -0
- package/dist/generic/platformSQL.js.map +1 -1
- package/dist/generic/providerBase.d.ts +370 -57
- package/dist/generic/providerBase.d.ts.map +1 -1
- package/dist/generic/providerBase.js +1085 -284
- package/dist/generic/providerBase.js.map +1 -1
- package/dist/generic/queryInfo.d.ts +23 -9
- package/dist/generic/queryInfo.d.ts.map +1 -1
- package/dist/generic/queryInfo.js +73 -33
- package/dist/generic/queryInfo.js.map +1 -1
- package/dist/generic/queryInfoInterfaces.d.ts +8 -0
- package/dist/generic/queryInfoInterfaces.d.ts.map +1 -1
- package/dist/generic/queryTemplateContext.d.ts +49 -0
- package/dist/generic/queryTemplateContext.d.ts.map +1 -0
- package/dist/generic/queryTemplateContext.js +113 -0
- package/dist/generic/queryTemplateContext.js.map +1 -0
- package/dist/generic/recordChangeFieldSecurity.d.ts.map +1 -1
- package/dist/generic/recordChangeFieldSecurity.js +13 -2
- package/dist/generic/recordChangeFieldSecurity.js.map +1 -1
- package/dist/generic/relatedRecordCollection.d.ts +13 -0
- package/dist/generic/relatedRecordCollection.d.ts.map +1 -1
- package/dist/generic/relatedRecordCollection.js +44 -5
- package/dist/generic/relatedRecordCollection.js.map +1 -1
- package/dist/generic/runQuery.d.ts +42 -0
- package/dist/generic/runQuery.d.ts.map +1 -1
- package/dist/generic/runQuery.js +14 -0
- package/dist/generic/runQuery.js.map +1 -1
- package/dist/generic/runQuerySQLFilterImplementations.d.ts.map +1 -1
- package/dist/generic/runQuerySQLFilterImplementations.js +38 -2
- package/dist/generic/runQuerySQLFilterImplementations.js.map +1 -1
- package/dist/generic/saveEntityGraphOperation.d.ts.map +1 -1
- package/dist/generic/saveEntityGraphOperation.js +3 -0
- package/dist/generic/saveEntityGraphOperation.js.map +1 -1
- package/dist/generic/scoring/ReciprocalRankFusion.d.ts.map +1 -1
- package/dist/generic/scoring/ReciprocalRankFusion.js +10 -3
- package/dist/generic/scoring/ReciprocalRankFusion.js.map +1 -1
- package/dist/generic/securityInfo.d.ts +17 -6
- package/dist/generic/securityInfo.d.ts.map +1 -1
- package/dist/generic/securityInfo.js +25 -10
- package/dist/generic/securityInfo.js.map +1 -1
- package/dist/generic/telemetryManager.d.ts +9 -0
- package/dist/generic/telemetryManager.d.ts.map +1 -1
- package/dist/generic/telemetryManager.js +5 -1
- package/dist/generic/telemetryManager.js.map +1 -1
- package/dist/generic/util.d.ts.map +1 -1
- package/dist/generic/util.js +3 -0
- package/dist/generic/util.js.map +1 -1
- package/dist/generic/wellKnownUserSource.d.ts +3 -2
- package/dist/generic/wellKnownUserSource.d.ts.map +1 -1
- package/dist/generic/wellKnownUserSource.js +18 -9
- package/dist/generic/wellKnownUserSource.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/views/runView.d.ts +89 -0
- package/dist/views/runView.d.ts.map +1 -1
- package/dist/views/runView.js +36 -0
- package/dist/views/runView.js.map +1 -1
- package/dist/views/viewProperties.d.ts +55 -0
- package/dist/views/viewProperties.d.ts.map +1 -0
- package/dist/views/viewProperties.js +258 -0
- package/dist/views/viewProperties.js.map +1 -0
- package/package.json +3 -3
- package/readme.md +29 -4
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { CurrentFieldSecurityGeneration, FieldSecurityError, ReadableFieldsTransportKey } from './entityInfo.js';
|
|
2
|
+
import { RecordChangeFieldSecurityProjector } from './recordChangeFieldSecurity.js';
|
|
3
|
+
/**
|
|
4
|
+
* Returns `values` without the keys `denied` names, matched case- and whitespace-insensitively.
|
|
5
|
+
*
|
|
6
|
+
* `denied` is a denied-field set as {@link EntityInfo.GetDeniedReadFields} builds it: lowercased,
|
|
7
|
+
* holding both the `Name` and the `CodeName` of every denied field. Because the match is against
|
|
8
|
+
* the row's OWN keys, a row keyed by either spelling, in any case, is narrowed.
|
|
9
|
+
*
|
|
10
|
+
* Returns a shallow COPY when a key was removed and the original object by reference when none
|
|
11
|
+
* was, so a row that is already narrow costs nothing. The input is never modified, which is what
|
|
12
|
+
* makes this safe on frozen, shared cache rows.
|
|
13
|
+
*/
|
|
14
|
+
export function OmitDeniedFieldKeys(values, denied) {
|
|
15
|
+
if (!values || typeof values !== 'object' || !denied || denied.size === 0) {
|
|
16
|
+
return values;
|
|
17
|
+
}
|
|
18
|
+
const source = values;
|
|
19
|
+
let copy = null;
|
|
20
|
+
for (const key of Object.keys(source)) {
|
|
21
|
+
if (!denied.has(key.trim().toLowerCase())) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
copy ??= { ...source };
|
|
25
|
+
delete copy[key];
|
|
26
|
+
}
|
|
27
|
+
return (copy ?? values);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Returns `values` without the keys `entity`'s own user may not read, for a payload built from
|
|
31
|
+
* that entity and sent to another tier (a graph save, a companion's wire form).
|
|
32
|
+
*
|
|
33
|
+
* The object already holds no denied value (its hydration points and `NewRecord` leave such
|
|
34
|
+
* fields not loaded), so `GetAll()` normally omits them by itself. This is the backstop for a key
|
|
35
|
+
* put back on the object without the readability check, which `SetMany` permits: the receiving
|
|
36
|
+
* tier refuses a restricted caller's input that names a field they may not read, so sending one
|
|
37
|
+
* fails the whole save.
|
|
38
|
+
*
|
|
39
|
+
* Returns the original object when nothing was removed, and a shallow copy otherwise.
|
|
40
|
+
*/
|
|
41
|
+
export function OmitFieldsUnreadableByActiveUser(entity, values) {
|
|
42
|
+
if (!values || typeof values !== 'object') {
|
|
43
|
+
return values;
|
|
44
|
+
}
|
|
45
|
+
const source = values;
|
|
46
|
+
let copy = null;
|
|
47
|
+
for (const key of Object.keys(source)) {
|
|
48
|
+
if (entity.IsFieldReadableByActiveUser(key)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
copy ??= { ...source };
|
|
52
|
+
delete copy[key];
|
|
53
|
+
}
|
|
54
|
+
return (copy ?? values);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The denied-READ set for `user` on `entityInfo`, or null when field security does not apply:
|
|
58
|
+
* it applies to neither the entity nor an IS-A ancestor, there is no user, or the user is denied nothing.
|
|
59
|
+
*/
|
|
60
|
+
function deniedReadFieldsFor(entityInfo, user) {
|
|
61
|
+
if (!entityInfo.FieldLevelSecurityApplies) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const denied = entityInfo.GetDeniedReadFields(user);
|
|
65
|
+
return denied.size > 0 ? denied : null;
|
|
66
|
+
}
|
|
67
|
+
/** The names of the fields a caller with this denied set may read, for {@link ReadableFieldsTransportKey}. */
|
|
68
|
+
function readableFieldNames(entityInfo, denied) {
|
|
69
|
+
return entityInfo.Fields
|
|
70
|
+
.filter((f) => !denied.has(f.Name.trim().toLowerCase()))
|
|
71
|
+
.map((f) => f.Name);
|
|
72
|
+
}
|
|
73
|
+
/** Field-security narrowing for one row: omission plus the optional readable-fields marker. */
|
|
74
|
+
function narrowRow(row, denied, readable) {
|
|
75
|
+
const narrowed = OmitDeniedFieldKeys(row, denied);
|
|
76
|
+
if (!readable) {
|
|
77
|
+
return narrowed;
|
|
78
|
+
}
|
|
79
|
+
const withMarker = narrowed === row
|
|
80
|
+
? { ...row }
|
|
81
|
+
: narrowed;
|
|
82
|
+
withMarker[ReadableFieldsTransportKey] = readable;
|
|
83
|
+
return withMarker;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Projects a plain object of entity field values down to what `user` may READ.
|
|
87
|
+
*
|
|
88
|
+
* **The one output projection every transport uses**, so that no transport can apply only half
|
|
89
|
+
* of it. Three things are applied, matching what the GraphQL resolvers' field mapping does:
|
|
90
|
+
*
|
|
91
|
+
* 1. **Denied read fields are omitted**, under both the `Name` and `CodeName` spellings and in any
|
|
92
|
+
* case, because callers reach here with rows keyed either way. Omitted rather than nulled:
|
|
93
|
+
* a null is indistinguishable from a real value.
|
|
94
|
+
* 2. **{@link ReadableFieldsTransportKey}** is appended when `includeReadableFields` is set and
|
|
95
|
+
* the caller is denied something (see the option).
|
|
96
|
+
* 3. **The Record Changes payload projection**, which is keyed on the entity each ROW is about
|
|
97
|
+
* rather than on the entity being queried. `MJ: Record Changes` normally has field security
|
|
98
|
+
* off, so step 1 is a no-op for it, while its payload columns carry other entities' values.
|
|
99
|
+
*
|
|
100
|
+
* An entity object built for a restricted user does not hold denied values in the first place,
|
|
101
|
+
* so on that path this is defence in depth. It is the primary control for values that did not
|
|
102
|
+
* come from such an object: a system-identity load, a raw provider row, or a cached payload.
|
|
103
|
+
*
|
|
104
|
+
* Returns a COPY whenever anything changed, and the original object by reference when nothing
|
|
105
|
+
* did, so unrestricted callers pay nothing and frozen cache rows are never written to.
|
|
106
|
+
*
|
|
107
|
+
* @param provider Metadata source, used to resolve the per-row target entity for Record Changes.
|
|
108
|
+
* @param entityInfo The entity the values belong to. When absent the values are returned unchanged.
|
|
109
|
+
* @param values The field values. Never modified.
|
|
110
|
+
* **Fails open on a missing user.** With no `user` the values are returned unchanged, full width.
|
|
111
|
+
* That is deliberate: a browser or other single-user client has no per-call user to pass, and
|
|
112
|
+
* server-internal code running without one is trusted with full values. It also means a
|
|
113
|
+
* server-side transport that forgets to pass the caller sends everything, so every request-path
|
|
114
|
+
* caller must pass the requesting user.
|
|
115
|
+
*
|
|
116
|
+
* @param user The caller. When absent the values are returned unchanged (see above).
|
|
117
|
+
* @param options See {@link ProjectEntityValuesOptions}.
|
|
118
|
+
*/
|
|
119
|
+
export function ProjectEntityValuesForUser(provider, entityInfo, values, user, options) {
|
|
120
|
+
if (!values || typeof values !== 'object' || !entityInfo || !user) {
|
|
121
|
+
return values;
|
|
122
|
+
}
|
|
123
|
+
const projected = ProjectEntityValueRowsForUser(provider, entityInfo, [values], user, options);
|
|
124
|
+
return projected?.[0] ?? values;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Array form of {@link ProjectEntityValuesForUser}. The denied set, the readable-fields list and
|
|
128
|
+
* the Record Changes projector are each built ONCE for the whole set: the projector memoizes its
|
|
129
|
+
* per-entity lookups for its own lifetime, and a denied set walks every field of the entity, so
|
|
130
|
+
* building either per row turns a per-request cost into a per-row one.
|
|
131
|
+
*
|
|
132
|
+
* Returns the ORIGINAL array when no row needed changing. Fails open on a missing user, as
|
|
133
|
+
* {@link ProjectEntityValuesForUser} does.
|
|
134
|
+
*/
|
|
135
|
+
export function ProjectEntityValueRowsForUser(provider, entityInfo, rows, user, options) {
|
|
136
|
+
if (!rows?.length || !entityInfo || !user) {
|
|
137
|
+
return rows;
|
|
138
|
+
}
|
|
139
|
+
const denied = deniedReadFieldsFor(entityInfo, user);
|
|
140
|
+
const isRecordChanges = RecordChangeFieldSecurityProjector.IsRecordChangesEntity(entityInfo);
|
|
141
|
+
if (!denied && !isRecordChanges) {
|
|
142
|
+
return rows;
|
|
143
|
+
}
|
|
144
|
+
let projected = rows;
|
|
145
|
+
if (denied) {
|
|
146
|
+
const readable = options?.includeReadableFields ? readableFieldNames(entityInfo, denied) : null;
|
|
147
|
+
projected = rows.map((row) => narrowRow(row, denied, readable));
|
|
148
|
+
}
|
|
149
|
+
if (isRecordChanges) {
|
|
150
|
+
projected = new RecordChangeFieldSecurityProjector(provider, user).ProjectRows(projected);
|
|
151
|
+
}
|
|
152
|
+
return projected.some((row, i) => row !== rows[i]) ? projected : rows;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Keys a transport adds to client input that are not entity fields, and that
|
|
156
|
+
* {@link AssertClientInputWritableForUser} therefore must not treat as unknown.
|
|
157
|
+
*/
|
|
158
|
+
export const ClientInputTransportKeys = [
|
|
159
|
+
'OldValues___',
|
|
160
|
+
ReadableFieldsTransportKey,
|
|
161
|
+
'RestoreContext___',
|
|
162
|
+
'CloneContext___',
|
|
163
|
+
// The companion payload key, spelled out rather than imported: the module that defines it
|
|
164
|
+
// imports BaseEntity, which imports this module, and this array is built at load time.
|
|
165
|
+
'Companions___',
|
|
166
|
+
'__typename',
|
|
167
|
+
];
|
|
168
|
+
/**
|
|
169
|
+
* The wording for refusing a key in client input. Deliberately the SAME for a key that names a
|
|
170
|
+
* field the caller cannot read and a key that names no field at all, so the refusal cannot be
|
|
171
|
+
* used to learn which columns exist.
|
|
172
|
+
*/
|
|
173
|
+
export function FieldSecurityClientInputMessage(key, entityName) {
|
|
174
|
+
return `Field '${key}' cannot be written on entity '${entityName}'.`;
|
|
175
|
+
}
|
|
176
|
+
/** {@link ClientInputTransportKeys}, lowercased and trimmed, as {@link AssertClientInputWritableForUser} matches them. */
|
|
177
|
+
const NORMALIZED_CLIENT_INPUT_TRANSPORT_KEYS = new Set(ClientInputTransportKeys.map((k) => k.trim().toLowerCase()));
|
|
178
|
+
const knownFieldKeysCache = new WeakMap();
|
|
179
|
+
/** Every key that names a field of `entityInfo` or of an IS-A ancestor, lowercased, by Name and CodeName. */
|
|
180
|
+
function knownFieldKeys(entityInfo) {
|
|
181
|
+
const fields = entityInfo.Fields;
|
|
182
|
+
const chain = entityInfo.ParentChain ?? [];
|
|
183
|
+
const generation = CurrentFieldSecurityGeneration();
|
|
184
|
+
const memo = knownFieldKeysCache.get(entityInfo);
|
|
185
|
+
if (memo && memo.fields === fields && memo.chain === chain && memo.generation === generation) {
|
|
186
|
+
return memo.keys;
|
|
187
|
+
}
|
|
188
|
+
const keys = new Set();
|
|
189
|
+
for (const entity of [entityInfo, ...chain]) {
|
|
190
|
+
for (const f of entity.Fields) {
|
|
191
|
+
keys.add(f.Name.trim().toLowerCase());
|
|
192
|
+
keys.add(f.CodeName.trim().toLowerCase());
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
knownFieldKeysCache.set(entityInfo, { fields, chain, generation, keys });
|
|
196
|
+
return keys;
|
|
197
|
+
}
|
|
198
|
+
/** Whether `normalized` is a transport key, built in or one the caller added. */
|
|
199
|
+
function isAllowedTransportKey(normalized, additional) {
|
|
200
|
+
return NORMALIZED_CLIENT_INPUT_TRANSPORT_KEYS.has(normalized) || (additional?.has(normalized) ?? false);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* The write-side gate for transports that apply client input with `SetMany`: throws a
|
|
204
|
+
* {@link FieldSecurityError} when the input carries a key the caller may not send.
|
|
205
|
+
*
|
|
206
|
+
* Applies only to a caller who is denied at least one field on the entity. For that caller, a key
|
|
207
|
+
* is refused when it names a field they cannot READ, or when it names no field at all; both get
|
|
208
|
+
* the identical {@link FieldSecurityClientInputMessage}. Treating unknown keys the same way is
|
|
209
|
+
* what keeps the refusal from revealing whether a column exists: refusing only denied keys would
|
|
210
|
+
* answer "does `Salary` exist?" for any key a caller cares to try.
|
|
211
|
+
*
|
|
212
|
+
* Rejection rather than silent removal: a client holding a denied value can only have fabricated
|
|
213
|
+
* it, because no response it received contained one, so there is no legitimate request to save.
|
|
214
|
+
*
|
|
215
|
+
* Keys in {@link ClientInputTransportKeys}, and any in `options.additionalAllowedKeys`, are
|
|
216
|
+
* accepted. Matching is case- and whitespace-insensitive, by Name and CodeName, like `SetMany`.
|
|
217
|
+
* Callers who are denied nothing, or input for an entity without field security, are unaffected,
|
|
218
|
+
* so `SetMany`'s ordinary handling of unknown keys still applies to them.
|
|
219
|
+
*
|
|
220
|
+
* @param entityInfo The entity the input will be applied to.
|
|
221
|
+
* **Fails open on a missing user.** With no `user` the gate does nothing, so server-internal
|
|
222
|
+
* code, and a client with no per-call user, are unaffected. A server-side transport applying a
|
|
223
|
+
* request's input must therefore pass the requesting user, or nothing is checked.
|
|
224
|
+
*
|
|
225
|
+
* @param values The client input. Never modified.
|
|
226
|
+
* @param user The caller. When absent the gate does nothing (see above).
|
|
227
|
+
* @param options See {@link ClientInputGateOptions}.
|
|
228
|
+
*/
|
|
229
|
+
export function AssertClientInputWritableForUser(entityInfo, values, user, options) {
|
|
230
|
+
if (!values || typeof values !== 'object' || !entityInfo || !user) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
const denied = deniedReadFieldsFor(entityInfo, user);
|
|
234
|
+
if (!denied) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const extra = options?.additionalAllowedKeys;
|
|
238
|
+
const additional = extra && extra.length > 0 ? new Set(extra.map((k) => k.trim().toLowerCase())) : null;
|
|
239
|
+
const known = knownFieldKeys(entityInfo);
|
|
240
|
+
for (const key of Object.keys(values)) {
|
|
241
|
+
const normalized = key.trim().toLowerCase();
|
|
242
|
+
if (isAllowedTransportKey(normalized, additional)) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (denied.has(normalized) || !known.has(normalized)) {
|
|
246
|
+
throw new FieldSecurityError(key, entityInfo.Name, FieldSecurityClientInputMessage(key, entityInfo.Name));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=entityValueFieldSecurity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entityValueFieldSecurity.js","sourceRoot":"","sources":["../../src/generic/entityValueFieldSecurity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { SQLScreeningDialect } from '@memberjunction/global';
|
|
2
|
+
import { EntityInfo, FieldSecurityError } from './entityInfo.js';
|
|
3
|
+
import { UserInfo } from './securityInfo.js';
|
|
4
|
+
/** Why {@link FieldSecuritySQLGate} refuses a piece of SQL. */
|
|
5
|
+
export type FieldSecurityViolationKind = 'DeniedField' | 'WholeRow' | 'Unresolvable' | 'UnknownRelation';
|
|
6
|
+
/** One reason {@link FieldSecuritySQLGate.FindViolation} refuses a piece of SQL. */
|
|
7
|
+
export interface FieldSecurityViolation {
|
|
8
|
+
Kind: FieldSecurityViolationKind;
|
|
9
|
+
/**
|
|
10
|
+
* `DeniedField`: the reference as the database reads it (a quoted identifier's bare name; on
|
|
11
|
+
* PostgreSQL an unquoted name in lower case). `WholeRow`: the construct, such as `FOR JSON`.
|
|
12
|
+
* `Unresolvable`: why the text could not be read. `UnknownRelation`: the relation the catalog
|
|
13
|
+
* does not know.
|
|
14
|
+
*/
|
|
15
|
+
Reference: string;
|
|
16
|
+
/** The entity whose denial the reference reaches; null when the text could not be read. */
|
|
17
|
+
Entity: EntityInfo | null;
|
|
18
|
+
}
|
|
19
|
+
/** What {@link FieldSecuritySQLGate} needs to judge one piece of SQL for one user. */
|
|
20
|
+
export interface FieldSecuritySQLContext {
|
|
21
|
+
/** The acting user. */
|
|
22
|
+
User: UserInfo;
|
|
23
|
+
/** The dialect whose lexical rules the database applies to the text. */
|
|
24
|
+
Dialect: SQLScreeningDialect;
|
|
25
|
+
/** Every entity the provider knows, so base views and tables named in the text are recognized. */
|
|
26
|
+
Catalog: readonly EntityInfo[];
|
|
27
|
+
/**
|
|
28
|
+
* For a filter, sort or aggregate fragment: the entity whose view the fragment runs against.
|
|
29
|
+
* Its columns can be named without a qualifier, so its denied fields are always checked.
|
|
30
|
+
*/
|
|
31
|
+
TargetEntity?: EntityInfo | null;
|
|
32
|
+
/** Entities the SQL is known to read beyond those its text names, such as a query's declared entities. */
|
|
33
|
+
ReadEntities?: readonly EntityInfo[];
|
|
34
|
+
/**
|
|
35
|
+
* For a whole statement, such as a saved query's SQL: refuse, for a user carrying any field
|
|
36
|
+
* denial, a relation that is not a CTE and not the base view or base table of a catalog entity
|
|
37
|
+
* (a custom view, synonym, table-valued function, temp table, or a name in another schema or
|
|
38
|
+
* database), and `EXEC`/`CALL`. What such a relation reads cannot be checked.
|
|
39
|
+
*/
|
|
40
|
+
RefuseUnknownRelations?: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* SECURITY — the field-level security gate for SQL text a caller authors: RunView filters, sorts and
|
|
44
|
+
* aggregate expressions, a saved view's stored clauses, dataset item filters, and the rendered SQL
|
|
45
|
+
* of a saved query.
|
|
46
|
+
*
|
|
47
|
+
* Output projection alone is not enough. A user denied `Salary` who can write
|
|
48
|
+
* `ExtraFilter: "Salary > 200000"`, sort by it, or aggregate `MIN(Salary)` reconstructs its values
|
|
49
|
+
* from which rows come back. This gate refuses such text before it reaches the cache or the
|
|
50
|
+
* database, for every caller: client requests, agents and actions alike.
|
|
51
|
+
*
|
|
52
|
+
* ## How it reads the text
|
|
53
|
+
*
|
|
54
|
+
* The text is split into tokens with the database's own lexical rules
|
|
55
|
+
* (`ScanSQLForScreening`), so a name inside a string literal or a comment is not a reference, and a
|
|
56
|
+
* `[bracketed]` or `"quoted"` name is read as the bare name it is. Every unquoted word and quoted
|
|
57
|
+
* identifier is then compared with every denied field of every entity the SQL reads:
|
|
58
|
+
* - the entity the fragment filters (its columns can be named unqualified);
|
|
59
|
+
* - the entities whose base view or base table the text names, such as a subquery's FROM;
|
|
60
|
+
* - any entities the caller says the SQL reads.
|
|
61
|
+
*
|
|
62
|
+
* A name matches a denied field by the rule `IdentifierMatches` states, or when the two are equal
|
|
63
|
+
* after Unicode compatibility folding, accent stripping and case folding, the comparisons a
|
|
64
|
+
* database collation can make. So a look-alike spelling that the database would still resolve to
|
|
65
|
+
* the denied column is refused too. Each name is compared on its own, so one denied name inside
|
|
66
|
+
* another can never hide it.
|
|
67
|
+
*
|
|
68
|
+
* A reference qualified by an alias or view name that binds only to entities whose denied set
|
|
69
|
+
* does not hold that name is not a reference to a denied field. An unknown qualifier is checked
|
|
70
|
+
* against every restricted entity.
|
|
71
|
+
*
|
|
72
|
+
* ## Whole-row constructs
|
|
73
|
+
*
|
|
74
|
+
* For a user denied something on an entity the SQL reads, constructs that read a whole row without
|
|
75
|
+
* naming its columns are refused: `SELECT *` and `alias.*` (except directly inside `EXISTS (…)`
|
|
76
|
+
* whose body has no set operation or row-picking ORDER BY), `FOR JSON`, `FOR XML`, any function of
|
|
77
|
+
* `*` other than `COUNT(*)`/`COUNT_BIG(*)` (for example `CHECKSUM(*)`), `NATURAL JOIN`, a column
|
|
78
|
+
* alias list on such a relation, and a relation or its alias used as a value (PostgreSQL's
|
|
79
|
+
* whole-row reference, as in `row_to_json(e)` or `TABLE vw`). A user with no denials on the
|
|
80
|
+
* entities read is not affected; ordinary subqueries keep working.
|
|
81
|
+
*
|
|
82
|
+
* ## Failing closed
|
|
83
|
+
*
|
|
84
|
+
* Text the scanner cannot read the way the database does is refused for any user who carries a
|
|
85
|
+
* denial on an entity the SQL could read. So is a name holding a control, format or
|
|
86
|
+
* default-ignorable character (a zero-width joiner, a direction mark), which a collation may give
|
|
87
|
+
* no weight to.
|
|
88
|
+
*
|
|
89
|
+
* ## Unknown columns
|
|
90
|
+
*
|
|
91
|
+
* A reference to a column that does not exist reaches the database, which rejects it. For a user
|
|
92
|
+
* carrying denials, {@link FieldSecuritySQLGate.ErrorForInvalidColumn} turns that rejection into the
|
|
93
|
+
* same {@link FieldSecurityError} a denied column gets, so the two cannot be told apart.
|
|
94
|
+
*/
|
|
95
|
+
export declare class FieldSecuritySQLGate {
|
|
96
|
+
/**
|
|
97
|
+
* The first reason `sql` must be refused for `context.User`, or null when it may run.
|
|
98
|
+
*
|
|
99
|
+
* @param sql A filter, sort or aggregate fragment, or a whole statement
|
|
100
|
+
* @param context The user, dialect, catalog and the entities the SQL is known to read
|
|
101
|
+
*/
|
|
102
|
+
static FindViolation(sql: string | null | undefined, context: FieldSecuritySQLContext): FieldSecurityViolation | null;
|
|
103
|
+
/**
|
|
104
|
+
* The first reason the sort fragment `orderBy` must be refused for `context.User`, or null.
|
|
105
|
+
* Everything {@link FindViolation} refuses, and, for a user denied a field on
|
|
106
|
+
* `context.TargetEntity`, a sort item that is a column position (`3 DESC`). A position names a
|
|
107
|
+
* column of the select list without spelling it, and an entity-object read selects every
|
|
108
|
+
* column, so `ORDER BY 3` can sort by a denied field. It is refused as a whole-row construct.
|
|
109
|
+
*/
|
|
110
|
+
static FindSortViolation(orderBy: string | null | undefined, context: FieldSecuritySQLContext): FieldSecurityViolation | null;
|
|
111
|
+
/**
|
|
112
|
+
* Throws a {@link FieldSecurityError} when {@link FindViolation} refuses `sql`. The error names
|
|
113
|
+
* `entityName`, the entity the caller is reading, never the entity a subquery reaches, so a
|
|
114
|
+
* refusal says nothing a "no such column" error would not.
|
|
115
|
+
*/
|
|
116
|
+
static AssertAllowed(sql: string | null | undefined, context: FieldSecuritySQLContext, entityName: string): void;
|
|
117
|
+
/**
|
|
118
|
+
* Checks a whole statement, such as a saved query's rendered SQL, against the denied fields of
|
|
119
|
+
* every entity it reads. The entities come from the base views and tables the text names, plus
|
|
120
|
+
* `readEntities`. References are checked wherever they appear: the select list, WHERE, JOIN,
|
|
121
|
+
* GROUP BY, ORDER BY and HAVING. For a user carrying any field denial, a relation the catalog
|
|
122
|
+
* does not know is refused too ({@link FieldSecuritySQLContext.RefuseUnknownRelations}).
|
|
123
|
+
* Throws a {@link FieldSecurityError} on the first violation, naming the entity whose denial
|
|
124
|
+
* it reached (or `fallbackEntityName` when none).
|
|
125
|
+
*/
|
|
126
|
+
static AssertStatementAllowed(sql: string, user: UserInfo, catalog: readonly EntityInfo[], dialect: SQLScreeningDialect, readEntities?: readonly EntityInfo[], fallbackEntityName?: string): void;
|
|
127
|
+
/** The error a violation raises, naming `entityName`. */
|
|
128
|
+
static ErrorForViolation(violation: FieldSecurityViolation, entityName: string): FieldSecurityError;
|
|
129
|
+
/**
|
|
130
|
+
* For a user carrying field denials on `entity`, the {@link FieldSecurityError} a database's
|
|
131
|
+
* "no such column" rejection becomes, so a column that does not exist and a column that is
|
|
132
|
+
* denied produce the same error. Null when the user carries no denials there, or the error is
|
|
133
|
+
* not a "no such column" rejection.
|
|
134
|
+
*
|
|
135
|
+
* Reads SQL Server's `Invalid column name 'x'` and `The multi-part identifier "a.x" could not be
|
|
136
|
+
* bound`, and PostgreSQL's `column "x" does not exist` and `column a.x does not exist`.
|
|
137
|
+
*/
|
|
138
|
+
static ErrorForInvalidColumn(error: unknown, entity: EntityInfo | null | undefined, user: UserInfo | null | undefined): FieldSecurityError | null;
|
|
139
|
+
/**
|
|
140
|
+
* The entities whose base view or base table `sql` reads as a relation: a name after FROM, JOIN
|
|
141
|
+
* or APPLY, or in a FROM list, wherever it appears (subqueries and CTE bodies included), with
|
|
142
|
+
* schema qualifiers skipped. A name in a literal or a comment is not a relation. A bare name
|
|
143
|
+
* shared by entities in several schemas yields each of them, so the set never misses a read.
|
|
144
|
+
* A relation inside a parenthesized join and PostgreSQL's `FROM ONLY x` count too. Null when
|
|
145
|
+
* the text cannot be read the way the database reads it.
|
|
146
|
+
*/
|
|
147
|
+
static RelationEntities(sql: string | null | undefined, catalog: readonly EntityInfo[], dialect: SQLScreeningDialect): EntityInfo[] | null;
|
|
148
|
+
/** True when `user` is denied at least one field on at least one of `entities`. */
|
|
149
|
+
static UserCarriesDenials(entities: Iterable<EntityInfo | null | undefined>, user: UserInfo): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* True when `user` is denied at least one field on any entity of `catalog`.
|
|
152
|
+
*
|
|
153
|
+
* Memoized per (catalog, user): after the first call it costs a few comparisons. The memo is
|
|
154
|
+
* discarded when the catalog array changes length, when the field-security generation moves
|
|
155
|
+
* (an entity rebuilt, a flag assigned, a cache invalidated; see
|
|
156
|
+
* `CurrentFieldSecurityGeneration`) or when the user's roles change, which are everything the
|
|
157
|
+
* answer depends on. A catalog swapped for a new array starts a new memo.
|
|
158
|
+
*/
|
|
159
|
+
static CatalogHasDenialsForUser(catalog: readonly EntityInfo[] | null | undefined, user: UserInfo): boolean;
|
|
160
|
+
/**
|
|
161
|
+
* Every field `user` may not read across `catalog`: one `entityID:field,field` entry per
|
|
162
|
+
* entity on which the user is denied something, sorted. '' for a user denied nothing.
|
|
163
|
+
* Memoized exactly as {@link CatalogHasDenialsForUser}.
|
|
164
|
+
*/
|
|
165
|
+
static CatalogDenialProfileKey(catalog: readonly EntityInfo[] | null | undefined, user: UserInfo): string;
|
|
166
|
+
/** The current summary for (`catalog`, `user`), rebuilt when any part of its key moved. */
|
|
167
|
+
private static catalogDenialSummary;
|
|
168
|
+
/** The uncached walk behind {@link CatalogDenialProfileKey}. */
|
|
169
|
+
private static buildDenialProfileKey;
|
|
170
|
+
/**
|
|
171
|
+
* The column a database "no such column" message names, or null. A qualified name gives its
|
|
172
|
+
* last part, which is what the gate reports for the same reference.
|
|
173
|
+
*/
|
|
174
|
+
private static invalidColumnName;
|
|
175
|
+
/** Longest prefix of a database error message scanned for a column name. */
|
|
176
|
+
private static readonly maxErrorMessageScanLength;
|
|
177
|
+
/** The last dot-separated part of an identifier path, unquoted and unescaped. */
|
|
178
|
+
private static lastIdentifierPart;
|
|
179
|
+
/**
|
|
180
|
+
* The identifier path in a PostgreSQL `column <path> does not exist` message (for example
|
|
181
|
+
* `e.salry` or `"E"."Salry"`), or null. Scanned by hand rather than with a regular expression:
|
|
182
|
+
* the path's parts may themselves contain dots, and a pattern that allows that backtracks
|
|
183
|
+
* exponentially on crafted input.
|
|
184
|
+
*/
|
|
185
|
+
private static postgresColumnPath;
|
|
186
|
+
/**
|
|
187
|
+
* Index just past an identifier path starting at `start`: a run of non-space characters in
|
|
188
|
+
* which double-quoted segments (with `""` escapes) may contain spaces. Returns `start` when no
|
|
189
|
+
* path starts there, and -1 for an unterminated quote.
|
|
190
|
+
*/
|
|
191
|
+
private static identifierPathEnd;
|
|
192
|
+
/**
|
|
193
|
+
* The first call of a function the dialect forbids in caller SQL (one that runs SQL given as a
|
|
194
|
+
* string, reads files or other databases), or null. What such a call reads cannot be checked.
|
|
195
|
+
*/
|
|
196
|
+
private static findDynamicSQLCall;
|
|
197
|
+
/**
|
|
198
|
+
* True when the user carries a denial on any entity the SQL could read: the target, the
|
|
199
|
+
* declared read entities (checked directly, as they need not be catalog members), or any
|
|
200
|
+
* catalog entity, since the text can name any of them. The catalog answer is memoized
|
|
201
|
+
* ({@link CatalogHasDenialsForUser}).
|
|
202
|
+
*
|
|
203
|
+
* Deliberately catalog-wide rather than "the target and read entities only". Which other
|
|
204
|
+
* entities a fragment reads is only known once its text has been read the way the database
|
|
205
|
+
* reads it: a subquery, a parenthesized join, a schema-qualified or bracketed name, or a name
|
|
206
|
+
* spelled with look-alike characters all reach another entity's view. And text that cannot be
|
|
207
|
+
* read at all, a dynamic-SQL call or an invisible character must be refused for such a user
|
|
208
|
+
* whatever it appears to name. Every one of those checks needs the tokens, so a user with a
|
|
209
|
+
* denial anywhere has every fragment tokenized; {@link analyze} then derives the read set, and
|
|
210
|
+
* a fragment whose read set carries none of the user's denials passes without the
|
|
211
|
+
* denied-name and whole-row checks.
|
|
212
|
+
*/
|
|
213
|
+
private static mayReadRestrictedEntity;
|
|
214
|
+
/** Builds the relation bindings, the restricted set and its denied names. */
|
|
215
|
+
private static analyze;
|
|
216
|
+
/** Adds `entity` to the restricted set when the user is denied a field on it. */
|
|
217
|
+
private static addIfRestricted;
|
|
218
|
+
/** Records each relation the text names, its alias, and which of them are restricted. */
|
|
219
|
+
private static bindRelations;
|
|
220
|
+
/** Binds a qualifier to entities. */
|
|
221
|
+
private static bind;
|
|
222
|
+
/**
|
|
223
|
+
* Whether the relation name at `i` declares a relation: it follows FROM, JOIN or APPLY, or a
|
|
224
|
+
* comma in a FROM list, with any `schema .` / `database . schema .` qualifiers, a PostgreSQL
|
|
225
|
+
* `ONLY`, and the parentheses of a parenthesized join skipped.
|
|
226
|
+
*/
|
|
227
|
+
private static isRelationDeclaration;
|
|
228
|
+
/** Whether the token at `j` is a FROM, JOIN or APPLY keyword, or a comma in a FROM list. */
|
|
229
|
+
private static introducesRelation;
|
|
230
|
+
/** The index of the first part of the dotted name that ends at `i` (`db . schema . name`). */
|
|
231
|
+
private static qualifierStart;
|
|
232
|
+
/**
|
|
233
|
+
* The clause keyword that governs the token at `i` at its own parenthesis depth: the nearest
|
|
234
|
+
* preceding SELECT, FROM, JOIN, WHERE, ON, GROUP, ORDER or HAVING at that depth. Inside the
|
|
235
|
+
* parentheses of a parenthesized join (`FROM (a x, b y)`) with no such keyword, `from`.
|
|
236
|
+
*/
|
|
237
|
+
private static clauseAt;
|
|
238
|
+
/** Whether the `(` at `j` opens a parenthesized join: it stands where a relation is declared. */
|
|
239
|
+
private static opensJoinGroup;
|
|
240
|
+
/**
|
|
241
|
+
* The first relation the statement reads that is not a CTE and not the base view or base table
|
|
242
|
+
* of a catalog entity, or a stored-procedure call; null when there is none. A name qualified by
|
|
243
|
+
* a schema no entity of that name lives in, or by a database, is unknown; so is a function in
|
|
244
|
+
* a relation position (`FROM f(…)`).
|
|
245
|
+
*/
|
|
246
|
+
private static findUnknownRelation;
|
|
247
|
+
/**
|
|
248
|
+
* Whether the name at `i` is the last part of a relation the statement reads: after FROM (of a
|
|
249
|
+
* SELECT, not `EXTRACT(… FROM x)` or `IS DISTINCT FROM x`), JOIN, APPLY, a FROM-list comma or
|
|
250
|
+
* PostgreSQL's `TABLE`, past qualifiers, `ONLY`, `LATERAL` and join-group parentheses.
|
|
251
|
+
*/
|
|
252
|
+
private static isRelationTarget;
|
|
253
|
+
/** Whether a token is `ONLY` or `LATERAL`. */
|
|
254
|
+
private static isRelationModifier;
|
|
255
|
+
/** Whether the FROM at `j` is a SELECT's FROM clause. */
|
|
256
|
+
private static isSelectFrom;
|
|
257
|
+
/**
|
|
258
|
+
* Whether the relation whose last name part is at `i` is a CTE or a catalog entity's base view
|
|
259
|
+
* or base table, in that entity's schema when qualified.
|
|
260
|
+
*/
|
|
261
|
+
private static isKnownRelation;
|
|
262
|
+
/**
|
|
263
|
+
* The folded names of the CTEs a statement defines: a name after WITH, RECURSIVE or a comma
|
|
264
|
+
* that is followed by an optional column list, AS, an optional `[NOT] MATERIALIZED`, and `(`.
|
|
265
|
+
*/
|
|
266
|
+
private static cteNames;
|
|
267
|
+
/** Whether the tokens from `j` are `[(columns)] AS [NOT] [MATERIALIZED] (`. */
|
|
268
|
+
private static opensCTEBody;
|
|
269
|
+
/** The index of the `)` that closes the `(` at `open`, or -1. */
|
|
270
|
+
private static closingParen;
|
|
271
|
+
/**
|
|
272
|
+
* Whether the body of the `EXISTS (` whose parenthesis is at `open` has, at its own depth, a
|
|
273
|
+
* word that makes a `SELECT *` there read column values ({@link EXISTS_ROW_READING_WORDS}).
|
|
274
|
+
* An unclosed body counts as one that does.
|
|
275
|
+
*/
|
|
276
|
+
private static existsBodyReadsRows;
|
|
277
|
+
/** The index of the alias after the relation at `i` (`x a`, `x AS a`), or null. */
|
|
278
|
+
private static aliasIndexAfter;
|
|
279
|
+
/** The first reference to a denied field, honoring qualifiers that bind to known entities. */
|
|
280
|
+
private static findDeniedReference;
|
|
281
|
+
/**
|
|
282
|
+
* The denied names a reference at `i` could reach: those of the entities its qualifier binds to,
|
|
283
|
+
* or every restricted entity's when it is unqualified or its qualifier is unknown.
|
|
284
|
+
*/
|
|
285
|
+
private static candidatesFor;
|
|
286
|
+
/**
|
|
287
|
+
* A sort item of `orderBy` that is a column position, as a whole-row violation, or null. Only
|
|
288
|
+
* for a user denied a field on the target entity. An item counts when its first token, inside
|
|
289
|
+
* any opening parentheses, is a number.
|
|
290
|
+
*/
|
|
291
|
+
private static findSortByPosition;
|
|
292
|
+
/** The first whole-row construct, or null. */
|
|
293
|
+
private static findWholeRowConstruct;
|
|
294
|
+
/** The whole-row construct that starts at token `i`, or null. */
|
|
295
|
+
private static wholeRowConstructAt;
|
|
296
|
+
/**
|
|
297
|
+
* What a `*` at `i` reads: null for a multiplication, `COUNT(*)`, `EXISTS (SELECT *` whose body
|
|
298
|
+
* has no set operation or row-picking ORDER BY at its own depth, or `alias.*` whose alias binds
|
|
299
|
+
* only to entities the user is denied nothing on.
|
|
300
|
+
*/
|
|
301
|
+
private static starConstruct;
|
|
302
|
+
/**
|
|
303
|
+
* A restricted relation name or alias used other than as a declaration or a qualifier (`x.`):
|
|
304
|
+
* a whole-row value on PostgreSQL, or a column alias list after the declaration.
|
|
305
|
+
*/
|
|
306
|
+
private static relationValueConstruct;
|
|
307
|
+
/** Whether a token can end the left operand of a multiplication. */
|
|
308
|
+
private static endsOperand;
|
|
309
|
+
/** Whether a token can start the right operand of a multiplication. */
|
|
310
|
+
private static startsOperand;
|
|
311
|
+
/**
|
|
312
|
+
* The name a "no such column" error would give for `name` as `sql` writes it, so a denied
|
|
313
|
+
* column is reported exactly as an unknown one. On PostgreSQL, MJ quotes a capitalized or
|
|
314
|
+
* dot-qualified word before the database sees it (`AutoQuoteLeavesWordBare`), so the error
|
|
315
|
+
* names such a word as written; a word left bare is folded to lower case. A quoted name, and
|
|
316
|
+
* every name on SQL Server, is reported as written. The first token spelled `name` decides.
|
|
317
|
+
*
|
|
318
|
+
* @param sql The caller's SQL
|
|
319
|
+
* @param name The reference as the database reads it
|
|
320
|
+
* @param dialect The dialect the SQL runs under
|
|
321
|
+
*/
|
|
322
|
+
static ReportedColumnName(sql: string, name: string, dialect: SQLScreeningDialect): string;
|
|
323
|
+
/** A reference's name as {@link ReportedColumnName} gives it. */
|
|
324
|
+
private static reportedName;
|
|
325
|
+
private static isName;
|
|
326
|
+
private static isWord;
|
|
327
|
+
private static isSymbol;
|
|
328
|
+
/** Folded base view and base table names of every entity in the catalog. */
|
|
329
|
+
private static relationIndex;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* An identifier reduced to the form a database collation may compare it in: Unicode compatibility
|
|
333
|
+
* decomposition (a KELVIN SIGN reads as `K`, a full-width letter as its ASCII form), accents and
|
|
334
|
+
* other combining marks removed, control, format and default-ignorable characters (zero-width
|
|
335
|
+
* joiners, direction marks, a soft hyphen) removed, case folded through upper case (`ß` and `ẞ` both read `ss`, a
|
|
336
|
+
* final `ς` reads `σ`), and surrounding spaces trimmed.
|
|
337
|
+
*/
|
|
338
|
+
export declare function FoldIdentifier(name: string): string;
|
|
339
|
+
/**
|
|
340
|
+
* Whether a reference as written may name `fieldName` on some database: by the rule
|
|
341
|
+
* `IdentifierMatches` states, or when both fold to the same form ({@link FoldIdentifier}). Looser
|
|
342
|
+
* than any collation, so a security check that refuses on it never lets through a reference the
|
|
343
|
+
* database would resolve to the field.
|
|
344
|
+
*/
|
|
345
|
+
export declare function ReferenceMayNameField(reference: string, fieldName: string): boolean;
|
|
346
|
+
/** The message for a whole-row construct refused for a user carrying field denials. */
|
|
347
|
+
export declare function FieldSecurityWholeRowMessage(construct: string, entityName: string): string;
|
|
348
|
+
/** The message for SQL that could not be read well enough to check against field permissions. */
|
|
349
|
+
export declare function FieldSecurityUnresolvableSQLMessage(entityName: string): string;
|
|
350
|
+
//# sourceMappingURL=fieldSecuritySQLGate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fieldSecuritySQLGate.d.ts","sourceRoot":"","sources":["../../src/generic/fieldSecuritySQLGate.ts"],"names":[],"mappings":"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"}
|