@memberjunction/server 5.51.3 → 5.51.4
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/README.md +34 -1
- package/dist/agentSessions/SessionManager.d.ts +5 -2
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +17 -8
- package/dist/agentSessions/SessionManager.js.map +1 -1
- package/dist/agentSessions/SessionRunIDVerifier.d.ts +63 -0
- package/dist/agentSessions/SessionRunIDVerifier.d.ts.map +1 -0
- package/dist/agentSessions/SessionRunIDVerifier.js +136 -0
- package/dist/agentSessions/SessionRunIDVerifier.js.map +1 -0
- package/dist/agentSessions/index.d.ts +1 -0
- package/dist/agentSessions/index.d.ts.map +1 -1
- package/dist/agentSessions/index.js +1 -0
- package/dist/agentSessions/index.js.map +1 -1
- package/dist/auth/actionAuthorization.d.ts +40 -0
- package/dist/auth/actionAuthorization.d.ts.map +1 -0
- package/dist/auth/actionAuthorization.js +94 -0
- package/dist/auth/actionAuthorization.js.map +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/scopeLimitedPrincipal.d.ts +39 -0
- package/dist/auth/scopeLimitedPrincipal.d.ts.map +1 -0
- package/dist/auth/scopeLimitedPrincipal.js +49 -0
- package/dist/auth/scopeLimitedPrincipal.js.map +1 -0
- package/dist/config.d.ts +53 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +34 -1
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +6 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +24 -34
- package/dist/context.js.map +1 -1
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +3262 -3273
- package/dist/generated/generated.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +124 -4
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +170 -15
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/FireAndForgetHeartbeat.d.ts +5 -0
- package/dist/generic/FireAndForgetHeartbeat.d.ts.map +1 -1
- package/dist/generic/FireAndForgetHeartbeat.js +5 -4
- package/dist/generic/FireAndForgetHeartbeat.js.map +1 -1
- package/dist/generic/PushStatusResolver.d.ts +58 -0
- package/dist/generic/PushStatusResolver.d.ts.map +1 -1
- package/dist/generic/PushStatusResolver.js +42 -3
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +148 -3
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +287 -30
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +12 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +56 -48
- package/dist/index.js.map +1 -1
- package/dist/multiTenancy/index.d.ts.map +1 -1
- package/dist/multiTenancy/index.js +22 -2
- package/dist/multiTenancy/index.js.map +1 -1
- package/dist/postgresqlCompat.d.ts +8 -0
- package/dist/postgresqlCompat.d.ts.map +1 -0
- package/dist/postgresqlCompat.js +19 -0
- package/dist/postgresqlCompat.js.map +1 -0
- package/dist/postgresqlPoolSettings.d.ts +108 -0
- package/dist/postgresqlPoolSettings.d.ts.map +1 -0
- package/dist/postgresqlPoolSettings.js +134 -0
- package/dist/postgresqlPoolSettings.js.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +2 -2
- package/dist/realtimeWidget/widgetGuestElevation.js +2 -2
- package/dist/resolvers/ActionResolver.d.ts +31 -5
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +75 -14
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts +39 -17
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +97 -157
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/DatasetResolver.d.ts +24 -3
- package/dist/resolvers/DatasetResolver.d.ts.map +1 -1
- package/dist/resolvers/DatasetResolver.js +57 -8
- package/dist/resolvers/DatasetResolver.js.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts +14 -4
- package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js +18 -6
- package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.d.ts +33 -7
- package/dist/resolvers/GetDataContextDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataContextDataResolver.js +117 -38
- package/dist/resolvers/GetDataContextDataResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +13 -1
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +13 -16
- package/dist/resolvers/MCPResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +26 -14
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +63 -46
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +24 -27
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +30 -40
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +12 -11
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTestResolver.js +90 -112
- package/dist/resolvers/RunTestResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +11 -2
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/VersionHistoryResolver.d.ts.map +1 -1
- package/dist/resolvers/VersionHistoryResolver.js +8 -12
- package/dist/resolvers/VersionHistoryResolver.js.map +1 -1
- package/dist/resolvers/adhocTimeout.d.ts +7 -0
- package/dist/resolvers/adhocTimeout.d.ts.map +1 -0
- package/dist/resolvers/adhocTimeout.js +12 -0
- package/dist/resolvers/adhocTimeout.js.map +1 -0
- package/dist/rest/ViewOperationsHandler.d.ts +3 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +29 -17
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/services/TaskOrchestrator.d.ts.map +1 -1
- package/dist/services/TaskOrchestrator.js +17 -15
- package/dist/services/TaskOrchestrator.js.map +1 -1
- package/package.json +90 -89
- package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
- package/src/__tests__/AdhocQueryResolver.bugs.test.ts +14 -35
- package/src/__tests__/AdhocQueryResolver.pagination.test.ts +6 -2
- package/src/__tests__/AdhocQueryResolver.scopeGuard.test.ts +84 -0
- package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
- package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
- package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
- package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
- package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +136 -0
- package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +318 -0
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +66 -27
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +134 -0
- package/src/__tests__/ResolverBase.adhocReadScope.test.ts +221 -0
- package/src/__tests__/ResolverBase.aggregateScreen.test.ts +173 -0
- package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
- package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +116 -0
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +351 -0
- package/src/__tests__/RunTemplateResolver.test.ts +132 -0
- package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +164 -0
- package/src/__tests__/SessionManager.test.ts +88 -13
- package/src/__tests__/SessionRunIDVerifier.test.ts +255 -0
- package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
- package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +155 -0
- package/src/__tests__/actionAuthorization.test.ts +130 -0
- package/src/__tests__/adhocTimeoutClamp.test.ts +26 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +241 -0
- package/src/__tests__/multiTenancy.postgresRewrite.test.ts +180 -0
- package/src/__tests__/multiTenancy.security.test.ts +51 -0
- package/src/__tests__/multiTenancy.test.ts +1 -1
- package/src/__tests__/newUserRolesDefault.test.ts +81 -0
- package/src/__tests__/postgresqlPoolSettings.test.ts +131 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +56 -0
- package/src/__tests__/resolverBase.refusalMessage.test.ts +88 -0
- package/src/__tests__/resolverBase.rls.test.ts +2 -0
- package/src/__tests__/translateBracketsToPG.test.ts +20 -0
- package/src/agentSessions/SessionManager.ts +24 -25
- package/src/agentSessions/SessionRunIDVerifier.ts +179 -0
- package/src/agentSessions/index.ts +1 -0
- package/src/auth/actionAuthorization.ts +119 -0
- package/src/auth/index.ts +1 -0
- package/src/auth/scopeLimitedPrincipal.ts +50 -0
- package/src/config.ts +34 -1
- package/src/context.ts +25 -34
- package/src/generated/generated.ts +3262 -3273
- package/src/generic/CacheInvalidationResolver.ts +206 -15
- package/src/generic/FireAndForgetHeartbeat.ts +10 -4
- package/src/generic/PushStatusResolver.ts +79 -4
- package/src/generic/ResolverBase.ts +339 -31
- package/src/generic/RunViewResolver.ts +17 -0
- package/src/index.ts +63 -50
- package/src/multiTenancy/index.ts +24 -2
- package/src/postgresqlCompat.ts +21 -0
- package/src/postgresqlPoolSettings.ts +213 -0
- package/src/realtimeWidget/widgetGuestElevation.ts +2 -2
- package/src/resolvers/ActionResolver.ts +85 -22
- package/src/resolvers/AdhocQueryResolver.ts +110 -177
- package/src/resolvers/DatasetResolver.ts +70 -10
- package/src/resolvers/EntityRecordNameResolver.ts +24 -7
- package/src/resolvers/GetDataContextDataResolver.ts +139 -41
- package/src/resolvers/GetDataResolver.ts +14 -1
- package/src/resolvers/MCPResolver.ts +13 -15
- package/src/resolvers/RealtimeClientSessionResolver.ts +82 -50
- package/src/resolvers/RemoteBrowserActionResolver.ts +30 -26
- package/src/resolvers/RunAIAgentResolver.ts +30 -40
- package/src/resolvers/RunTemplateResolver.ts +13 -12
- package/src/resolvers/RunTestResolver.ts +90 -112
- package/src/resolvers/SyncRolesUsersResolver.ts +11 -2
- package/src/resolvers/VersionHistoryResolver.ts +10 -13
- package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
- package/src/resolvers/__tests__/ReportResolver.test.ts +2 -0
- package/src/resolvers/adhocTimeout.ts +12 -0
- package/src/rest/ViewOperationsHandler.ts +37 -18
- package/src/services/TaskOrchestrator.ts +17 -17
- package/dist/resolvers/adhoc-query-helpers.d.ts +0 -27
- package/dist/resolvers/adhoc-query-helpers.d.ts.map +0 -1
- package/dist/resolvers/adhoc-query-helpers.js +0 -42
- package/dist/resolvers/adhoc-query-helpers.js.map +0 -1
- package/src/__tests__/adhoc-query-helpers.test.ts +0 -50
- package/src/resolvers/adhoc-query-helpers.ts +0 -50
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { DatabaseProviderBase, UserInfo } from '@memberjunction/core';
|
|
2
|
+
import type { AdhocSQLAuthorizer } from '@memberjunction/actions';
|
|
1
3
|
import { AppContext } from '../types.js';
|
|
2
4
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
3
5
|
import { RunQueryResultType } from './QueryResolver.js';
|
|
@@ -14,34 +16,54 @@ declare class AdhocQueryInput {
|
|
|
14
16
|
/**
|
|
15
17
|
* Resolver for executing ad-hoc (unsaved) SQL queries.
|
|
16
18
|
*
|
|
19
|
+
* The query runs through the read-only provider's own ad-hoc path (`RunQuery` with `SQL`), the
|
|
20
|
+
* same one in-process callers use, so it behaves identically on every database platform: the SQL
|
|
21
|
+
* is validated and rendered there, paged in the database with a count, run under the caller's
|
|
22
|
+
* timeout, and on PostgreSQL inside a read-only transaction that is rolled back.
|
|
23
|
+
*
|
|
17
24
|
* Security:
|
|
18
|
-
* - SQL validated
|
|
19
|
-
* -
|
|
20
|
-
* -
|
|
25
|
+
* - SQL validated by the provider (SQLExpressionValidator, single read query) — blocks mutations
|
|
26
|
+
* - Authorizes the RENDERED SQL: every table reference must be an entity base view the caller
|
|
27
|
+
* reads unscoped (CanRead, no row-level filter, no denied fields). Entities with row/field
|
|
28
|
+
* policy for this user are refused — raw SQL cannot apply that policy, so those reads belong
|
|
29
|
+
* on RunView.
|
|
30
|
+
* - Read-only provider only (no fallback to read-write)
|
|
31
|
+
* - Configurable timeout (default 30s), enforced by the database
|
|
21
32
|
* - Requires authenticated user (standard GraphQL auth, no @RequireSystemUser)
|
|
33
|
+
* - Refuses scope-limited principals (see {@link IsScopeLimitedPrincipal}). Raw SQL bypasses
|
|
34
|
+
* RunView, entity permissions and row-level security, so the RLS scope tokens that confine a
|
|
35
|
+
* magic-link session are never applied on this path — there is no narrower filter to fall back
|
|
36
|
+
* to, only refusal.
|
|
22
37
|
*
|
|
23
38
|
* Auto-discovered by MJServer's dynamic resolver import.
|
|
24
39
|
*/
|
|
25
40
|
export declare class AdhocQueryResolver extends ResolverBase {
|
|
26
41
|
ExecuteAdhocQuery(input: AdhocQueryInput, context: AppContext): Promise<RunQueryResultType>;
|
|
27
42
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
43
|
+
* SECURITY: authorizes the READ itself, against the SQL that will actually run. The provider
|
|
44
|
+
* validates that the SQL is a single read statement but places no restriction on WHAT is read.
|
|
45
|
+
* Rendering comes first because composition (`{{query:"..."}}`) and templates can introduce
|
|
46
|
+
* table references the raw input never named. Every table reference must be an entity base
|
|
47
|
+
* view the caller can read UNSCOPED — CanRead with no row-level filter and no denied fields —
|
|
48
|
+
* because raw SQL applies neither (see assertFullQueryUsesReadableEntityViews). The provider
|
|
49
|
+
* renders the same input the same way and only wraps it in server-built paging and a count,
|
|
50
|
+
* so the rendered SQL is the full set of reads. Returns the refusal message, or null.
|
|
31
51
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* CTE wrap. A count failure must never sink the whole result, so we log it and
|
|
35
|
-
* report a lower-bound total (`startRow + rowsReturned`) and let the data render.
|
|
36
|
-
*/
|
|
37
|
-
private executeDataAndCount;
|
|
38
|
-
/**
|
|
39
|
-
* Executes one SQL statement on the read-only pool, racing it against the shared
|
|
40
|
-
* wall-clock `deadline`. The timer is always cleared on completion so a settled
|
|
41
|
-
* query never leaves a dangling timeout armed.
|
|
52
|
+
* `provider` is the read-only provider the SQL will run on; it supplies the platform and the
|
|
53
|
+
* entity metadata. {@link CreateAdhocSQLAuthorizer} exposes this check to the action engine.
|
|
42
54
|
*/
|
|
43
|
-
|
|
55
|
+
AuthorizeRenderedSQL(sqlText: string, provider: DatabaseProviderBase, contextUser: UserInfo | undefined): string | null;
|
|
56
|
+
/** Maps the provider's result to the GraphQL shape, naming a timeout plainly. */
|
|
57
|
+
private toResultType;
|
|
44
58
|
private buildErrorResult;
|
|
45
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* The ad-hoc SQL checks MJServer registers with the action engine, so that Run Ad-hoc Query applies
|
|
62
|
+
* the same checks as {@link AdhocQueryResolver.ExecuteAdhocQuery}: the rendered SQL must be one read
|
|
63
|
+
* statement that reads only entity views the caller may read in full, and the timeout is limited by
|
|
64
|
+
* the server's request timeout.
|
|
65
|
+
* @param requestTimeoutMs the server's request timeout in milliseconds; 0 or less means no limit
|
|
66
|
+
*/
|
|
67
|
+
export declare function CreateAdhocSQLAuthorizer(requestTimeoutMs: number): AdhocSQLAuthorizer;
|
|
46
68
|
export {};
|
|
47
69
|
//# sourceMappingURL=AdhocQueryResolver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdhocQueryResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/AdhocQueryResolver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdhocQueryResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/AdhocQueryResolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAkB,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE3F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAGxD;;;GAGG;AACH,cACM,eAAe;IAEjB,GAAG,EAAE,MAAM,CAAC;IAGZ,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,OAAO,CAAC,EAAE,MAAM,CAAC;IAGjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBACa,kBAAmB,SAAQ,YAAY;IAE1C,iBAAiB,CACkB,KAAK,EAAE,eAAe,EACpD,OAAO,EAAE,UAAU,GAC3B,OAAO,CAAC,kBAAkB,CAAC;IAoC9B;;;;;;;;;;;;OAYG;IACI,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,EAAE,WAAW,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI;IAU9H,iFAAiF;IACjF,OAAO,CAAC,YAAY;IA6BpB,OAAO,CAAC,gBAAgB;CAc3B;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,gBAAgB,EAAE,MAAM,GAAG,kBAAkB,CAMrF"}
|
|
@@ -12,13 +12,13 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
12
12
|
};
|
|
13
13
|
import { Arg, Ctx, Query, Resolver, Field, Int, InputType } from 'type-graphql';
|
|
14
14
|
import { LogError } from '@memberjunction/core';
|
|
15
|
-
import { SQLExpressionValidator } from '@memberjunction/global';
|
|
16
15
|
import { RenderPipeline } from '@memberjunction/generic-database-provider';
|
|
17
|
-
import {
|
|
16
|
+
import { configInfo } from '../config.js';
|
|
17
|
+
import { GetReadOnlyProvider } from '../util.js';
|
|
18
18
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
19
|
+
import { IsScopeLimitedPrincipal } from '../auth/scopeLimitedPrincipal.js';
|
|
19
20
|
import { RunQueryResultType } from './QueryResolver.js';
|
|
20
|
-
import {
|
|
21
|
-
import sql from 'mssql';
|
|
21
|
+
import { ClampAdhocTimeoutSeconds } from './adhocTimeout.js';
|
|
22
22
|
/**
|
|
23
23
|
* Input type for executing ad-hoc SQL queries directly.
|
|
24
24
|
* The SQL is validated server-side to ensure it's a safe SELECT/WITH statement.
|
|
@@ -47,182 +47,108 @@ AdhocQueryInput = __decorate([
|
|
|
47
47
|
/**
|
|
48
48
|
* Resolver for executing ad-hoc (unsaved) SQL queries.
|
|
49
49
|
*
|
|
50
|
+
* The query runs through the read-only provider's own ad-hoc path (`RunQuery` with `SQL`), the
|
|
51
|
+
* same one in-process callers use, so it behaves identically on every database platform: the SQL
|
|
52
|
+
* is validated and rendered there, paged in the database with a count, run under the caller's
|
|
53
|
+
* timeout, and on PostgreSQL inside a read-only transaction that is rolled back.
|
|
54
|
+
*
|
|
50
55
|
* Security:
|
|
51
|
-
* - SQL validated
|
|
52
|
-
* -
|
|
53
|
-
* -
|
|
56
|
+
* - SQL validated by the provider (SQLExpressionValidator, single read query) — blocks mutations
|
|
57
|
+
* - Authorizes the RENDERED SQL: every table reference must be an entity base view the caller
|
|
58
|
+
* reads unscoped (CanRead, no row-level filter, no denied fields). Entities with row/field
|
|
59
|
+
* policy for this user are refused — raw SQL cannot apply that policy, so those reads belong
|
|
60
|
+
* on RunView.
|
|
61
|
+
* - Read-only provider only (no fallback to read-write)
|
|
62
|
+
* - Configurable timeout (default 30s), enforced by the database
|
|
54
63
|
* - Requires authenticated user (standard GraphQL auth, no @RequireSystemUser)
|
|
64
|
+
* - Refuses scope-limited principals (see {@link IsScopeLimitedPrincipal}). Raw SQL bypasses
|
|
65
|
+
* RunView, entity permissions and row-level security, so the RLS scope tokens that confine a
|
|
66
|
+
* magic-link session are never applied on this path — there is no narrower filter to fall back
|
|
67
|
+
* to, only refusal.
|
|
55
68
|
*
|
|
56
69
|
* Auto-discovered by MJServer's dynamic resolver import.
|
|
57
70
|
*/
|
|
58
71
|
let AdhocQueryResolver = class AdhocQueryResolver extends ResolverBase {
|
|
59
72
|
async ExecuteAdhocQuery(input, context) {
|
|
60
73
|
const startTime = Date.now();
|
|
74
|
+
// SECURITY: refuse principals whose read authority is narrower than their role grant.
|
|
75
|
+
// Ad-hoc SQL does not go through RunView, entity permissions or row-level security, so the
|
|
76
|
+
// RLS scope tokens that confine a magic-link session are never substituted. There is no
|
|
77
|
+
// filter to narrow, so the only correct answer for such a principal is no.
|
|
78
|
+
if (IsScopeLimitedPrincipal(context.userPayload?.userRecord)) {
|
|
79
|
+
return this.buildErrorResult('Ad-hoc SQL execution is not permitted for scope-limited sessions.');
|
|
80
|
+
}
|
|
81
|
+
const provider = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: false });
|
|
82
|
+
if (!provider) {
|
|
83
|
+
return this.buildErrorResult('No read-only data source available for ad-hoc query execution');
|
|
84
|
+
}
|
|
85
|
+
const contextUser = context.userPayload?.userRecord;
|
|
86
|
+
const refusal = this.AuthorizeRenderedSQL(input.SQL, provider, contextUser);
|
|
87
|
+
if (refusal) {
|
|
88
|
+
return this.buildErrorResult(refusal, Date.now() - startTime);
|
|
89
|
+
}
|
|
90
|
+
const timeoutSeconds = ClampAdhocTimeoutSeconds(input.TimeoutSeconds, configInfo.databaseSettings.requestTimeout);
|
|
91
|
+
// A negative offset must not reach paging, where it would turn paging off.
|
|
92
|
+
const startRow = Math.max(0, Number.isInteger(input.StartRow) ? input.StartRow : 0);
|
|
61
93
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let readOnlyDS;
|
|
70
|
-
try {
|
|
71
|
-
readOnlyDS = GetReadOnlyDataSource(context.dataSources, { allowFallbackToReadWrite: false });
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
return this.buildErrorResult('No read-only data source available for ad-hoc query execution');
|
|
75
|
-
}
|
|
76
|
-
// 3. Resolve platform from the read-only provider for the render pipeline.
|
|
77
|
-
let platform = 'sqlserver';
|
|
78
|
-
try {
|
|
79
|
-
const provider = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: false });
|
|
80
|
-
if (provider?.PlatformKey)
|
|
81
|
-
platform = provider.PlatformKey;
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
// Provider not configured — keep the default platform.
|
|
85
|
-
}
|
|
86
|
-
const contextUser = context.userPayload?.userRecord;
|
|
87
|
-
// 4. Route the SQL through RenderPipeline so composition tokens
|
|
88
|
-
// resolve, comments and templates are processed, and the row cap
|
|
89
|
-
// is applied at the database (via TOP / LIMIT / OFFSET-FETCH).
|
|
90
|
-
//
|
|
91
|
-
// Page whenever a positive MaxRows is requested — INCLUDING the first
|
|
92
|
-
// page (StartRow 0). This yields OFFSET/FETCH data SQL *and* a
|
|
93
|
-
// COUNT(*) query so the response reports the true total row count
|
|
94
|
-
// rather than just the returned page size. It mirrors the saved-query
|
|
95
|
-
// path (GenericDatabaseProvider.InternalRunQuery → WrapWithPaging).
|
|
96
|
-
// (Previously paging was gated on StartRow > 0, so page 1 fell back to
|
|
97
|
-
// a TOP-N cap with TotalRowCount = page size — hiding the pager.)
|
|
98
|
-
// Clamp StartRow to a non-negative integer. A negative offset must not slip
|
|
99
|
-
// past the paging gate (which would then run the query with no row cap at all).
|
|
100
|
-
const startRow = Math.max(0, Number.isInteger(input.StartRow) ? input.StartRow : 0);
|
|
101
|
-
const maxRows = input.MaxRows;
|
|
102
|
-
const usePaging = maxRows != null && Number.isInteger(maxRows) && maxRows > 0;
|
|
103
|
-
let dataSQL;
|
|
104
|
-
let countSQL = null;
|
|
105
|
-
try {
|
|
106
|
-
const rendered = RenderPipeline.Run(input.SQL, {
|
|
107
|
-
Platform: platform,
|
|
108
|
-
ContextUser: contextUser,
|
|
109
|
-
...(usePaging ? { Paging: { StartRow: startRow, MaxRows: maxRows } } : {}),
|
|
110
|
-
});
|
|
111
|
-
dataSQL = rendered.FinalSQL;
|
|
112
|
-
countSQL = rendered.PagingResult?.CountSQL ?? null;
|
|
113
|
-
}
|
|
114
|
-
catch (renderErr) {
|
|
115
|
-
const renderMsg = renderErr instanceof Error ? renderErr.message : String(renderErr);
|
|
116
|
-
return this.buildErrorResult(`Ad-hoc query rendering failed: ${renderMsg}`);
|
|
117
|
-
}
|
|
118
|
-
// 5. Execute the page (and, only when a full page needs it, the count) under
|
|
119
|
-
// a shared wall-clock deadline derived from the request's timeout budget.
|
|
120
|
-
const deadline = startTime + (input.TimeoutSeconds ?? 30) * 1000;
|
|
121
|
-
const { recordset, totalRowCount } = await this.executeDataAndCount(readOnlyDS, dataSQL, countSQL, startRow, usePaging ? maxRows : null, deadline);
|
|
122
|
-
const executionTimeMs = Date.now() - startTime;
|
|
123
|
-
// 6. Return as RunQueryResultType
|
|
124
|
-
return {
|
|
125
|
-
QueryID: '',
|
|
126
|
-
QueryName: 'Ad-Hoc Query',
|
|
127
|
-
Success: true,
|
|
128
|
-
Results: JSON.stringify(recordset),
|
|
129
|
-
RowCount: recordset.length,
|
|
130
|
-
TotalRowCount: totalRowCount,
|
|
131
|
-
PageNumber: usePaging ? Math.floor(startRow / maxRows) + 1 : undefined,
|
|
132
|
-
PageSize: usePaging ? maxRows : undefined,
|
|
133
|
-
ExecutionTime: executionTimeMs,
|
|
134
|
-
ErrorMessage: ''
|
|
135
|
-
};
|
|
94
|
+
const result = await provider.RunQuery({
|
|
95
|
+
SQL: input.SQL,
|
|
96
|
+
MaxRows: input.MaxRows,
|
|
97
|
+
StartRow: startRow,
|
|
98
|
+
TimeoutSeconds: timeoutSeconds,
|
|
99
|
+
}, contextUser);
|
|
100
|
+
return this.toResultType(result, startRow, input.MaxRows, timeoutSeconds, Date.now() - startTime);
|
|
136
101
|
}
|
|
137
102
|
catch (err) {
|
|
138
|
-
const executionTimeMs = Date.now() - startTime;
|
|
139
103
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
140
|
-
// Handle timeout
|
|
141
|
-
if (errorMessage.includes('timeout') || errorMessage.includes('Timeout')) {
|
|
142
|
-
return {
|
|
143
|
-
QueryID: '',
|
|
144
|
-
QueryName: 'Ad-Hoc Query',
|
|
145
|
-
Success: false,
|
|
146
|
-
Results: '[]',
|
|
147
|
-
RowCount: 0,
|
|
148
|
-
TotalRowCount: 0,
|
|
149
|
-
PageNumber: undefined,
|
|
150
|
-
PageSize: undefined,
|
|
151
|
-
ExecutionTime: executionTimeMs,
|
|
152
|
-
ErrorMessage: `Query execution exceeded ${input.TimeoutSeconds ?? 30} second timeout`
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
104
|
LogError(`Ad-hoc query execution failed: ${errorMessage}`);
|
|
156
|
-
return {
|
|
157
|
-
QueryID: '',
|
|
158
|
-
QueryName: 'Ad-Hoc Query',
|
|
159
|
-
Success: false,
|
|
160
|
-
Results: '[]',
|
|
161
|
-
RowCount: 0,
|
|
162
|
-
TotalRowCount: 0,
|
|
163
|
-
PageNumber: undefined,
|
|
164
|
-
PageSize: undefined,
|
|
165
|
-
ExecutionTime: executionTimeMs,
|
|
166
|
-
ErrorMessage: `Query execution failed: ${errorMessage}`
|
|
167
|
-
};
|
|
105
|
+
return this.buildErrorResult(`Query execution failed: ${errorMessage}`, Date.now() - startTime);
|
|
168
106
|
}
|
|
169
107
|
}
|
|
170
108
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
109
|
+
* SECURITY: authorizes the READ itself, against the SQL that will actually run. The provider
|
|
110
|
+
* validates that the SQL is a single read statement but places no restriction on WHAT is read.
|
|
111
|
+
* Rendering comes first because composition (`{{query:"..."}}`) and templates can introduce
|
|
112
|
+
* table references the raw input never named. Every table reference must be an entity base
|
|
113
|
+
* view the caller can read UNSCOPED — CanRead with no row-level filter and no denied fields —
|
|
114
|
+
* because raw SQL applies neither (see assertFullQueryUsesReadableEntityViews). The provider
|
|
115
|
+
* renders the same input the same way and only wraps it in server-built paging and a count,
|
|
116
|
+
* so the rendered SQL is the full set of reads. Returns the refusal message, or null.
|
|
174
117
|
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
* CTE wrap. A count failure must never sink the whole result, so we log it and
|
|
178
|
-
* report a lower-bound total (`startRow + rowsReturned`) and let the data render.
|
|
118
|
+
* `provider` is the read-only provider the SQL will run on; it supplies the platform and the
|
|
119
|
+
* entity metadata. {@link CreateAdhocSQLAuthorizer} exposes this check to the action engine.
|
|
179
120
|
*/
|
|
180
|
-
|
|
181
|
-
const dataResult = await this.runSqlWithDeadline(ds, dataSQL, deadline);
|
|
182
|
-
const recordset = (dataResult.recordset ?? []);
|
|
183
|
-
// Total already known from the page alone (unpaged, or a short page)? Skip the count.
|
|
184
|
-
const exact = exactTotalFromPage(startRow, recordset.length, maxRows);
|
|
185
|
-
if (exact != null || !countSQL) {
|
|
186
|
-
return { recordset, totalRowCount: exact ?? recordset.length };
|
|
187
|
-
}
|
|
188
|
-
// Full page — a COUNT(*) is required to know the true total.
|
|
189
|
-
const lowerBound = startRow + recordset.length;
|
|
121
|
+
AuthorizeRenderedSQL(sqlText, provider, contextUser) {
|
|
190
122
|
try {
|
|
191
|
-
const
|
|
192
|
-
|
|
123
|
+
const rendered = RenderPipeline.Run(sqlText, { Platform: provider.PlatformKey, ContextUser: contextUser, RequireReadStatement: true });
|
|
124
|
+
this.assertFullQueryUsesReadableEntityViews(rendered.Trace.AfterTemplates, provider, contextUser, 'ad-hoc SQL');
|
|
125
|
+
return null;
|
|
193
126
|
}
|
|
194
|
-
catch (
|
|
195
|
-
|
|
196
|
-
LogError(`Ad-hoc query row-count failed; reporting a lower-bound total (${lowerBound}). ${msg}`);
|
|
197
|
-
return { recordset, totalRowCount: lowerBound };
|
|
127
|
+
catch (err) {
|
|
128
|
+
return err instanceof Error ? err.message : String(err);
|
|
198
129
|
}
|
|
199
130
|
}
|
|
200
|
-
/**
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
async runSqlWithDeadline(ds, sqlText, deadline) {
|
|
206
|
-
const remaining = deadline - Date.now();
|
|
207
|
-
if (remaining <= 0) {
|
|
208
|
-
throw new Error('Query timeout exceeded');
|
|
209
|
-
}
|
|
210
|
-
let timer;
|
|
211
|
-
try {
|
|
212
|
-
return await Promise.race([
|
|
213
|
-
new sql.Request(ds).query(sqlText),
|
|
214
|
-
new Promise((_, reject) => {
|
|
215
|
-
timer = setTimeout(() => reject(new Error('Query timeout exceeded')), remaining);
|
|
216
|
-
}),
|
|
217
|
-
]);
|
|
218
|
-
}
|
|
219
|
-
finally {
|
|
220
|
-
if (timer) {
|
|
221
|
-
clearTimeout(timer);
|
|
222
|
-
}
|
|
131
|
+
/** Maps the provider's result to the GraphQL shape, naming a timeout plainly. */
|
|
132
|
+
toResultType(result, startRow, maxRows, timeoutSeconds, executionTimeMs) {
|
|
133
|
+
if (!result.Success) {
|
|
134
|
+
const message = result.ErrorMessage ?? 'Ad-hoc query failed';
|
|
135
|
+
return this.buildErrorResult(/timeout/i.test(message) ? `Query execution exceeded ${timeoutSeconds} second timeout` : message, executionTimeMs);
|
|
223
136
|
}
|
|
137
|
+
const paged = maxRows != null && Number.isInteger(maxRows) && maxRows > 0;
|
|
138
|
+
return {
|
|
139
|
+
QueryID: '',
|
|
140
|
+
QueryName: 'Ad-Hoc Query',
|
|
141
|
+
Success: true,
|
|
142
|
+
Results: JSON.stringify(result.Results),
|
|
143
|
+
RowCount: result.RowCount,
|
|
144
|
+
TotalRowCount: result.TotalRowCount,
|
|
145
|
+
PageNumber: paged ? Math.floor(startRow / maxRows) + 1 : undefined,
|
|
146
|
+
PageSize: paged ? maxRows : undefined,
|
|
147
|
+
ExecutionTime: executionTimeMs,
|
|
148
|
+
ErrorMessage: ''
|
|
149
|
+
};
|
|
224
150
|
}
|
|
225
|
-
buildErrorResult(errorMessage) {
|
|
151
|
+
buildErrorResult(errorMessage, executionTimeMs = 0) {
|
|
226
152
|
return {
|
|
227
153
|
QueryID: '',
|
|
228
154
|
QueryName: 'Ad-Hoc Query',
|
|
@@ -232,7 +158,7 @@ let AdhocQueryResolver = class AdhocQueryResolver extends ResolverBase {
|
|
|
232
158
|
TotalRowCount: 0,
|
|
233
159
|
PageNumber: undefined,
|
|
234
160
|
PageSize: undefined,
|
|
235
|
-
ExecutionTime:
|
|
161
|
+
ExecutionTime: executionTimeMs,
|
|
236
162
|
ErrorMessage: errorMessage
|
|
237
163
|
};
|
|
238
164
|
}
|
|
@@ -249,4 +175,18 @@ AdhocQueryResolver = __decorate([
|
|
|
249
175
|
Resolver()
|
|
250
176
|
], AdhocQueryResolver);
|
|
251
177
|
export { AdhocQueryResolver };
|
|
178
|
+
/**
|
|
179
|
+
* The ad-hoc SQL checks MJServer registers with the action engine, so that Run Ad-hoc Query applies
|
|
180
|
+
* the same checks as {@link AdhocQueryResolver.ExecuteAdhocQuery}: the rendered SQL must be one read
|
|
181
|
+
* statement that reads only entity views the caller may read in full, and the timeout is limited by
|
|
182
|
+
* the server's request timeout.
|
|
183
|
+
* @param requestTimeoutMs the server's request timeout in milliseconds; 0 or less means no limit
|
|
184
|
+
*/
|
|
185
|
+
export function CreateAdhocSQLAuthorizer(requestTimeoutMs) {
|
|
186
|
+
const resolver = new AdhocQueryResolver();
|
|
187
|
+
return {
|
|
188
|
+
Authorize: (sql, provider, contextUser) => resolver.AuthorizeRenderedSQL(sql, provider, contextUser),
|
|
189
|
+
ClampTimeoutSeconds: (requestedSeconds) => ClampAdhocTimeoutSeconds(requestedSeconds, requestTimeoutMs),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
252
192
|
//# sourceMappingURL=AdhocQueryResolver.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdhocQueryResolver.js","sourceRoot":"","sources":["../../src/resolvers/AdhocQueryResolver.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,
|
|
1
|
+
{"version":3,"file":"AdhocQueryResolver.js","sourceRoot":"","sources":["../../src/resolvers/AdhocQueryResolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { AppContext } from '../types.js';
|
|
1
|
+
import { AppContext, UserPayload } from '../types.js';
|
|
2
|
+
import { DatabaseProviderBase, DatasetItemFilterType, DatasetItemResultType, UserInfo } from '@memberjunction/core';
|
|
2
3
|
import { ResolverBase } from '../generic/ResolverBase.js';
|
|
3
4
|
export declare class DatasetResultType {
|
|
4
5
|
DatasetID: string;
|
|
@@ -12,7 +13,27 @@ export declare class DatasetItemFilterTypeGQL {
|
|
|
12
13
|
ItemCode: string;
|
|
13
14
|
Filter: string;
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Request handling shared by the dataset queries. Every read runs as the session's user, so the
|
|
18
|
+
* provider applies that user's entity read permission and row-level security per item, and every
|
|
19
|
+
* client ItemFilter passes the same AST screen as a RunView ExtraFilter before a provider sees it.
|
|
20
|
+
*/
|
|
21
|
+
export declare abstract class DatasetResolverBase extends ResolverBase {
|
|
22
|
+
/** The session's user. A dataset read never runs without one: the provider applies no read checks then. */
|
|
23
|
+
protected RequireDatasetUser(userPayload: UserPayload): UserInfo;
|
|
24
|
+
/**
|
|
25
|
+
* Screens every client ItemFilter: one statement, read-only, and subqueries only over entity base
|
|
26
|
+
* views the user can read. Throws on the first refusal. Returns the filters without null entries.
|
|
27
|
+
*/
|
|
28
|
+
protected ScreenItemFilters(itemFilters: DatasetItemFilterTypeGQL[] | undefined, provider: DatabaseProviderBase, user: UserInfo): DatasetItemFilterType[] | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Applies the API encryption policy to each item's rows, as every other GraphQL read path does:
|
|
31
|
+
* an encrypted field the entity does not allow decrypted over the API leaves as ciphertext or the
|
|
32
|
+
* sentinel. Works on copies, because the rows can be the server cache's own frozen objects.
|
|
33
|
+
*/
|
|
34
|
+
protected ProtectEncryptedFields(items: DatasetItemResultType[], provider: DatabaseProviderBase, user: UserInfo): Promise<DatasetItemResultType[]>;
|
|
35
|
+
}
|
|
36
|
+
export declare class DatasetResolverExtended extends DatasetResolverBase {
|
|
16
37
|
GetDatasetByName(DatasetName: string, { providers, userPayload }: AppContext, ItemFilters?: DatasetItemFilterTypeGQL[]): Promise<{
|
|
17
38
|
DatasetID: string;
|
|
18
39
|
DatasetName: string;
|
|
@@ -30,7 +51,7 @@ export declare class DatasetStatusResultType {
|
|
|
30
51
|
LatestUpdateDate: Date;
|
|
31
52
|
EntityUpdateDates: string;
|
|
32
53
|
}
|
|
33
|
-
export declare class DatasetStatusResolver extends
|
|
54
|
+
export declare class DatasetStatusResolver extends DatasetResolverBase {
|
|
34
55
|
GetDatasetStatusByName(DatasetName: string, { providers, userPayload }: AppContext, ItemFilters?: DatasetItemFilterTypeGQL[]): Promise<{
|
|
35
56
|
DatasetID: string;
|
|
36
57
|
DatasetName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatasetResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/DatasetResolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"DatasetResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/DatasetResolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,qBAAqB,EAAsB,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAExI,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,qBACa,iBAAiB;IAE5B,SAAS,EAAE,MAAM,CAAC;IAGlB,WAAW,EAAE,MAAM,CAAC;IAGpB,OAAO,EAAE,OAAO,CAAC;IAGjB,MAAM,EAAE,MAAM,CAAC;IAGf,gBAAgB,EAAE,IAAI,CAAC;IAGvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBACa,wBAAwB;IAEnC,QAAQ,EAAE,MAAM,CAAC;IAGjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,8BAAsB,mBAAoB,SAAQ,YAAY;IAC5D,2GAA2G;IAC3G,SAAS,CAAC,kBAAkB,CAAC,WAAW,EAAE,WAAW,GAAG,QAAQ;IAQhE;;;OAGG;IACH,SAAS,CAAC,iBAAiB,CACzB,WAAW,EAAE,wBAAwB,EAAE,GAAG,SAAS,EACnD,QAAQ,EAAE,oBAAoB,EAC9B,IAAI,EAAE,QAAQ,GACb,qBAAqB,EAAE,GAAG,SAAS;IAUtC;;;;OAIG;cACa,sBAAsB,CACpC,KAAK,EAAE,qBAAqB,EAAE,EAC9B,QAAQ,EAAE,oBAAoB,EAC9B,IAAI,EAAE,QAAQ,GACb,OAAO,CAAC,qBAAqB,EAAE,CAAC;CAYpC;AAED,qBACa,uBAAwB,SAAQ,mBAAmB;IAExD,gBAAgB,CACc,WAAW,EAAE,MAAM,EAC9C,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,UAAU,EACuC,WAAW,CAAC,EAAE,wBAAwB,EAAE;;;;;;;;CA2B/H;AAED,qBACa,uBAAuB;IAElC,SAAS,EAAE,MAAM,CAAC;IAGlB,WAAW,EAAE,MAAM,CAAC;IAGpB,OAAO,EAAE,OAAO,CAAC;IAGjB,MAAM,EAAE,MAAM,CAAC;IAGf,gBAAgB,EAAE,IAAI,CAAC;IAGvB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,qBACa,qBAAsB,SAAQ,mBAAmB;IAEtD,sBAAsB,CACQ,WAAW,EAAE,MAAM,EAC9C,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,UAAU,EACuC,WAAW,CAAC,EAAE,wBAAwB,EAAE;;;;;;;;IA4B9H;;;OAGG;IAEG,8BAA8B,CACG,YAAY,EAAE,MAAM,EAAE,EACpD,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,UAAU,GAC5C,OAAO,CAAC,uBAAuB,EAAE,CAAC;CA4BtC"}
|
|
@@ -58,13 +58,59 @@ DatasetItemFilterTypeGQL = __decorate([
|
|
|
58
58
|
InputType()
|
|
59
59
|
], DatasetItemFilterTypeGQL);
|
|
60
60
|
export { DatasetItemFilterTypeGQL };
|
|
61
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Request handling shared by the dataset queries. Every read runs as the session's user, so the
|
|
63
|
+
* provider applies that user's entity read permission and row-level security per item, and every
|
|
64
|
+
* client ItemFilter passes the same AST screen as a RunView ExtraFilter before a provider sees it.
|
|
65
|
+
*/
|
|
66
|
+
export class DatasetResolverBase extends ResolverBase {
|
|
67
|
+
/** The session's user. A dataset read never runs without one: the provider applies no read checks then. */
|
|
68
|
+
RequireDatasetUser(userPayload) {
|
|
69
|
+
const user = this.GetUserFromPayload(userPayload);
|
|
70
|
+
if (!user) {
|
|
71
|
+
throw new Error('Dataset request refused: the session resolves to no user');
|
|
72
|
+
}
|
|
73
|
+
return user;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Screens every client ItemFilter: one statement, read-only, and subqueries only over entity base
|
|
77
|
+
* views the user can read. Throws on the first refusal. Returns the filters without null entries.
|
|
78
|
+
*/
|
|
79
|
+
ScreenItemFilters(itemFilters, provider, user) {
|
|
80
|
+
if (!itemFilters)
|
|
81
|
+
return undefined;
|
|
82
|
+
itemFilters.forEach((itemFilter, index) => {
|
|
83
|
+
if (itemFilter) {
|
|
84
|
+
this.assertClientClauseUsesEntityBaseViews(itemFilter.Filter, `ItemFilters[${index}].Filter`, provider, user);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return itemFilters.filter((itemFilter) => itemFilter != null);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Applies the API encryption policy to each item's rows, as every other GraphQL read path does:
|
|
91
|
+
* an encrypted field the entity does not allow decrypted over the API leaves as ciphertext or the
|
|
92
|
+
* sentinel. Works on copies, because the rows can be the server cache's own frozen objects.
|
|
93
|
+
*/
|
|
94
|
+
async ProtectEncryptedFields(items, provider, user) {
|
|
95
|
+
return Promise.all(items.map(async (item) => {
|
|
96
|
+
const entity = provider.EntityByName(item.EntityName);
|
|
97
|
+
if (!item.Results?.length || !entity?.EncryptedFields.some((field) => !field.AllowDecryptInAPI)) {
|
|
98
|
+
return item;
|
|
99
|
+
}
|
|
100
|
+
const rows = item.Results.map((row) => ({ ...row }));
|
|
101
|
+
return { ...item, Results: await this.ArrayFilterEncryptedFieldsForAPI(item.EntityName, rows, user, provider, 'Name') };
|
|
102
|
+
}));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
let DatasetResolverExtended = class DatasetResolverExtended extends DatasetResolverBase {
|
|
62
106
|
async GetDatasetByName(DatasetName, { providers, userPayload }, ItemFilters) {
|
|
63
107
|
// Check API key scope authorization for dataset read
|
|
64
108
|
await this.CheckAPIKeyScopeAuthorization('dataset:read', DatasetName, userPayload);
|
|
109
|
+
const md = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
|
|
110
|
+
const user = this.RequireDatasetUser(userPayload);
|
|
111
|
+
const itemFilters = this.ScreenItemFilters(ItemFilters, md, user);
|
|
65
112
|
try {
|
|
66
|
-
const
|
|
67
|
-
const result = await md.GetDatasetByName(DatasetName, ItemFilters);
|
|
113
|
+
const result = await md.GetDatasetByName(DatasetName, itemFilters, user);
|
|
68
114
|
if (result) {
|
|
69
115
|
return {
|
|
70
116
|
DatasetID: result.DatasetID,
|
|
@@ -72,7 +118,7 @@ let DatasetResolverExtended = class DatasetResolverExtended extends ResolverBase
|
|
|
72
118
|
Success: result.Success,
|
|
73
119
|
Status: result.Status,
|
|
74
120
|
LatestUpdateDate: result.LatestUpdateDate,
|
|
75
|
-
Results: JSON.stringify(result.Results),
|
|
121
|
+
Results: JSON.stringify(await this.ProtectEncryptedFields(result.Results, md, user)),
|
|
76
122
|
};
|
|
77
123
|
}
|
|
78
124
|
else {
|
|
@@ -128,13 +174,15 @@ DatasetStatusResultType = __decorate([
|
|
|
128
174
|
ObjectType()
|
|
129
175
|
], DatasetStatusResultType);
|
|
130
176
|
export { DatasetStatusResultType };
|
|
131
|
-
let DatasetStatusResolver = class DatasetStatusResolver extends
|
|
177
|
+
let DatasetStatusResolver = class DatasetStatusResolver extends DatasetResolverBase {
|
|
132
178
|
async GetDatasetStatusByName(DatasetName, { providers, userPayload }, ItemFilters) {
|
|
133
179
|
// Check API key scope authorization for dataset read
|
|
134
180
|
await this.CheckAPIKeyScopeAuthorization('dataset:read', DatasetName, userPayload);
|
|
181
|
+
const md = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
|
|
182
|
+
const user = this.RequireDatasetUser(userPayload);
|
|
183
|
+
const itemFilters = this.ScreenItemFilters(ItemFilters, md, user);
|
|
135
184
|
try {
|
|
136
|
-
const
|
|
137
|
-
const result = await md.GetDatasetStatusByName(DatasetName, ItemFilters);
|
|
185
|
+
const result = await md.GetDatasetStatusByName(DatasetName, itemFilters, user);
|
|
138
186
|
if (result) {
|
|
139
187
|
return {
|
|
140
188
|
DatasetID: result.DatasetID,
|
|
@@ -160,11 +208,12 @@ let DatasetStatusResolver = class DatasetStatusResolver extends ResolverBase {
|
|
|
160
208
|
*/
|
|
161
209
|
async GetMultipleDatasetStatusByName(DatasetNames, { providers, userPayload }) {
|
|
162
210
|
const md = GetReadOnlyProvider(providers, { allowFallbackToReadWrite: true });
|
|
211
|
+
const user = this.RequireDatasetUser(userPayload);
|
|
163
212
|
const results = [];
|
|
164
213
|
// Execute all status checks in parallel
|
|
165
214
|
const statusPromises = DatasetNames.map(async (name) => {
|
|
166
215
|
await this.CheckAPIKeyScopeAuthorization('dataset:read', name, userPayload);
|
|
167
|
-
return md.GetDatasetStatusByName(name);
|
|
216
|
+
return md.GetDatasetStatusByName(name, undefined, user);
|
|
168
217
|
});
|
|
169
218
|
const statuses = await Promise.all(statusPromises);
|
|
170
219
|
for (const result of statuses) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatasetResolver.js","sourceRoot":"","sources":["../../src/resolvers/DatasetResolver.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAO,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAE5F,OAAO,
|
|
1
|
+
{"version":3,"file":"DatasetResolver.js","sourceRoot":"","sources":["../../src/resolvers/DatasetResolver.ts"],"names":[],"mappings":"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"}
|