@memberjunction/server 6.2.0-edge.2 → 6.2.0-edge.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 +30 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/BridgeHostInstance.d.ts +25 -0
- package/dist/agentSessions/BridgeHostInstance.d.ts.map +1 -0
- package/dist/agentSessions/BridgeHostInstance.js +30 -0
- package/dist/agentSessions/BridgeHostInstance.js.map +1 -0
- package/dist/agentSessions/HostInstance.d.ts +75 -11
- package/dist/agentSessions/HostInstance.d.ts.map +1 -1
- package/dist/agentSessions/HostInstance.js +99 -28
- package/dist/agentSessions/HostInstance.js.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.d.ts.map +1 -1
- package/dist/agentSessions/ReturningVisitorRecap.js +7 -3
- package/dist/agentSessions/ReturningVisitorRecap.js.map +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +173 -25
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +452 -54
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +43 -2
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +99 -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 +129 -0
- package/dist/agentSessions/SessionRunIDVerifier.js.map +1 -0
- package/dist/agentSessions/index.d.ts +2 -0
- package/dist/agentSessions/index.d.ts.map +1 -1
- package/dist/agentSessions/index.js +10 -3
- 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.map +1 -1
- package/dist/auth/index.js +3 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +6 -3
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/newUsers.d.ts +10 -0
- package/dist/auth/newUsers.d.ts.map +1 -1
- package/dist/auth/newUsers.js +80 -11
- package/dist/auth/newUsers.js.map +1 -1
- package/dist/config.d.ts +4567 -71
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +246 -3
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +8 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +25 -34
- package/dist/context.js.map +1 -1
- package/dist/generated/graphql-schemas/__mj.d.ts +1073 -10
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +9188 -3649
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +62 -6
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +179 -35
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +4 -0
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +37 -1
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +214 -159
- package/dist/index.js.map +1 -1
- package/dist/isolatedProvider.d.ts +20 -0
- package/dist/isolatedProvider.d.ts.map +1 -0
- package/dist/isolatedProvider.js +22 -0
- package/dist/isolatedProvider.js.map +1 -0
- 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/realtimeProxy/RealtimeProxyServer.d.ts +44 -4
- package/dist/realtimeProxy/RealtimeProxyServer.d.ts.map +1 -1
- package/dist/realtimeProxy/RealtimeProxyServer.js +121 -16
- package/dist/realtimeProxy/RealtimeProxyServer.js.map +1 -1
- package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts +171 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.d.ts.map +1 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.js +532 -0
- package/dist/realtimeProxy/RealtimeRelayTunnel.js.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.d.ts +148 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.js +191 -0
- package/dist/realtimeSessions/RealtimeSessionEventService.js.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts +66 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js +138 -0
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts +38 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js +98 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts +61 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.d.ts.map +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js +149 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts +57 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.d.ts.map +1 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.js +69 -0
- package/dist/realtimeSessions/SlidingWindowRateLimiter.js.map +1 -0
- package/dist/realtimeSessions/index.d.ts +17 -0
- package/dist/realtimeSessions/index.d.ts.map +1 -0
- package/dist/realtimeSessions/index.js +17 -0
- package/dist/realtimeSessions/index.js.map +1 -0
- package/dist/realtimeSessions/sessionAccess.d.ts +86 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionAccess.js +90 -0
- package/dist/realtimeSessions/sessionAccess.js.map +1 -0
- package/dist/realtimeSessions/sessionConfigStamps.d.ts +70 -0
- package/dist/realtimeSessions/sessionConfigStamps.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionConfigStamps.js +93 -0
- package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -0
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts +65 -0
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -0
- package/dist/realtimeSessions/subscriptionAuthorization.js +71 -0
- package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -0
- package/dist/realtimeSessions/verificationCore.d.ts +243 -0
- package/dist/realtimeSessions/verificationCore.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationCore.js +356 -0
- package/dist/realtimeSessions/verificationCore.js.map +1 -0
- package/dist/realtimeSessions/verificationPages.d.ts +66 -0
- package/dist/realtimeSessions/verificationPages.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationPages.js +110 -0
- package/dist/realtimeSessions/verificationPages.js.map +1 -0
- package/dist/realtimeSessions/verificationPolicy.d.ts +147 -0
- package/dist/realtimeSessions/verificationPolicy.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationPolicy.js +249 -0
- package/dist/realtimeSessions/verificationPolicy.js.map +1 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts +263 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -0
- package/dist/realtimeSessions/verificationWorkflow.js +435 -0
- package/dist/realtimeSessions/verificationWorkflow.js.map +1 -0
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +20 -2
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +31 -2
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- 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 +35 -17
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +92 -166
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/CacheStatsResolver.d.ts +27 -0
- package/dist/resolvers/CacheStatsResolver.d.ts.map +1 -1
- package/dist/resolvers/CacheStatsResolver.js +113 -2
- package/dist/resolvers/CacheStatsResolver.js.map +1 -1
- package/dist/resolvers/ClientToolRequestResolver.d.ts +4 -2
- package/dist/resolvers/ClientToolRequestResolver.d.ts.map +1 -1
- package/dist/resolvers/ClientToolRequestResolver.js +28 -10
- package/dist/resolvers/ClientToolRequestResolver.js.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts +7 -23
- package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js +9 -47
- package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts +12 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +83 -2
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/GetDataResolver.d.ts.map +1 -1
- package/dist/resolvers/GetDataResolver.js +9 -0
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.d.ts +107 -0
- package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -0
- package/dist/resolvers/HumanHandoffResolver.js +330 -0
- package/dist/resolvers/HumanHandoffResolver.js.map +1 -0
- package/dist/resolvers/MCPResolver.d.ts.map +1 -1
- package/dist/resolvers/MCPResolver.js +9 -4
- package/dist/resolvers/MCPResolver.js.map +1 -1
- package/dist/resolvers/MeetingResolver.d.ts +69 -0
- package/dist/resolvers/MeetingResolver.d.ts.map +1 -0
- package/dist/resolvers/MeetingResolver.js +381 -0
- package/dist/resolvers/MeetingResolver.js.map +1 -0
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +88 -3
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +352 -12
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +156 -108
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +395 -293
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RealtimeSessionEventsResolver.d.ts +61 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.js +130 -0
- package/dist/resolvers/RealtimeSessionEventsResolver.js.map +1 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts +85 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.js +188 -0
- package/dist/resolvers/RealtimeSessionVerificationResolver.js.map +1 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +2 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +5 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +10 -0
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +68 -11
- 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/SyncDataResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncDataResolver.js +6 -2
- package/dist/resolvers/SyncDataResolver.js.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.d.ts +2 -0
- package/dist/resolvers/SyncRolesUsersResolver.d.ts.map +1 -1
- package/dist/resolvers/SyncRolesUsersResolver.js +23 -6
- package/dist/resolvers/SyncRolesUsersResolver.js.map +1 -1
- package/dist/resolvers/UserAvatarResolver.d.ts +16 -0
- package/dist/resolvers/UserAvatarResolver.d.ts.map +1 -0
- package/dist/resolvers/UserAvatarResolver.js +124 -0
- package/dist/resolvers/UserAvatarResolver.js.map +1 -0
- package/dist/resolvers/UserResolver.d.ts.map +1 -1
- package/dist/resolvers/UserResolver.js +2 -17
- package/dist/resolvers/UserResolver.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/resolvers/agentVisionConsentAudit.d.ts +37 -0
- package/dist/resolvers/agentVisionConsentAudit.d.ts.map +1 -0
- package/dist/resolvers/agentVisionConsentAudit.js +63 -0
- package/dist/resolvers/agentVisionConsentAudit.js.map +1 -0
- package/dist/resolvers/avatarInputValidation.d.ts +40 -0
- package/dist/resolvers/avatarInputValidation.d.ts.map +1 -0
- package/dist/resolvers/avatarInputValidation.js +97 -0
- package/dist/resolvers/avatarInputValidation.js.map +1 -0
- package/dist/resolvers/currentUserRoles.d.ts +29 -0
- package/dist/resolvers/currentUserRoles.d.ts.map +1 -1
- package/dist/resolvers/currentUserRoles.js +30 -0
- package/dist/resolvers/currentUserRoles.js.map +1 -1
- package/dist/resolvers/meetings/index.d.ts +5 -0
- package/dist/resolvers/meetings/index.d.ts.map +1 -0
- package/dist/resolvers/meetings/index.js +5 -0
- package/dist/resolvers/meetings/index.js.map +1 -0
- package/dist/resolvers/meetings/meetingHelpers.d.ts +17 -0
- package/dist/resolvers/meetings/meetingHelpers.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingHelpers.js +212 -0
- package/dist/resolvers/meetings/meetingHelpers.js.map +1 -0
- package/dist/resolvers/meetings/meetingMutations.d.ts +18 -0
- package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingMutations.js +439 -0
- package/dist/resolvers/meetings/meetingMutations.js.map +1 -0
- package/dist/resolvers/meetings/meetingQueries.d.ts +7 -0
- package/dist/resolvers/meetings/meetingQueries.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingQueries.js +113 -0
- package/dist/resolvers/meetings/meetingQueries.js.map +1 -0
- package/dist/resolvers/meetings/meetingTypes.d.ts +89 -0
- package/dist/resolvers/meetings/meetingTypes.d.ts.map +1 -0
- package/dist/resolvers/meetings/meetingTypes.js +345 -0
- package/dist/resolvers/meetings/meetingTypes.js.map +1 -0
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +8 -5
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts +8 -0
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +27 -1
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +4 -3
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +13 -7
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/services/WorkQueueHostService.d.ts +36 -0
- package/dist/services/WorkQueueHostService.d.ts.map +1 -0
- package/dist/services/WorkQueueHostService.js +97 -0
- package/dist/services/WorkQueueHostService.js.map +1 -0
- package/dist/services/workQueueConfig.d.ts +103 -0
- package/dist/services/workQueueConfig.d.ts.map +1 -0
- package/dist/services/workQueueConfig.js +48 -0
- package/dist/services/workQueueConfig.js.map +1 -0
- package/dist/sharedCache.d.ts +105 -0
- package/dist/sharedCache.d.ts.map +1 -0
- package/dist/sharedCache.js +276 -0
- package/dist/sharedCache.js.map +1 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +97 -94
- 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 +3 -3
- package/src/__tests__/AdhocQueryResolver.sqlAuthorizer.test.ts +104 -0
- package/src/__tests__/BridgeHostInstance.test.ts +232 -0
- package/src/__tests__/ClientToolRequestResolver.test.ts +116 -0
- package/src/__tests__/EntityRecordNameResolver.fieldSecurity.test.ts +27 -72
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +285 -0
- package/src/__tests__/HostInstance.test.ts +84 -0
- package/src/__tests__/HumanHandoffResolver.test.ts +250 -0
- package/src/__tests__/MeetingResolver.test.ts +448 -0
- package/src/__tests__/RealtimeBridgeResolver.test.ts +651 -4
- package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +383 -0
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +682 -58
- package/src/__tests__/RealtimeProxyServer.test.ts +16 -1
- package/src/__tests__/RealtimeRelay.test.ts +876 -0
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
- package/src/__tests__/ResolverBase.adhocReadScope.test.ts +96 -0
- package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +6 -3
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +21 -2
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +291 -0
- package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +169 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +4 -4
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +166 -0
- package/src/__tests__/RunTemplateResolver.test.ts +132 -0
- package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +63 -0
- package/src/__tests__/SessionJanitor.test.ts +845 -18
- package/src/__tests__/SessionManager.test.ts +213 -17
- package/src/__tests__/SessionRunIDVerifier.test.ts +251 -0
- package/src/__tests__/SyncRolesUsersResolver.userRoles.test.ts +97 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +41 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +50 -3
- package/src/__tests__/UserAvatarResolver.test.ts +255 -0
- package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
- package/src/__tests__/WorkQueueHostService.test.ts +133 -0
- package/src/__tests__/actionAuthorization.test.ts +130 -0
- package/src/__tests__/adhocTimeoutClamp.test.ts +26 -0
- package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
- package/src/__tests__/avatarInputValidation.test.ts +160 -0
- package/src/__tests__/isolatedProviderTransactions.test.ts +169 -0
- package/src/__tests__/magicLinkService.consume.test.ts +6 -0
- package/src/__tests__/magicLinkService.provisionLog.test.ts +5 -0
- package/src/__tests__/newUsers.template.test.ts +151 -0
- package/src/__tests__/newUsers.test.ts +74 -5
- package/src/__tests__/orphanedConversationDetailReconciler.test.ts +17 -2
- package/src/__tests__/postgresqlPoolSettings.test.ts +131 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionEventService.test.ts +201 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +215 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionVerificationResolver.test.ts +102 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionVerificationService.test.ts +349 -0
- package/src/__tests__/realtimeSessions/RealtimeVerifyRouter.test.ts +131 -0
- package/src/__tests__/realtimeSessions/SlidingWindowRateLimiter.test.ts +56 -0
- package/src/__tests__/realtimeSessions/helpers.ts +4 -0
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +129 -0
- package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +124 -0
- package/src/__tests__/realtimeSessions/verificationCore.test.ts +355 -0
- package/src/__tests__/realtimeSessions/verificationPages.test.ts +84 -0
- package/src/__tests__/realtimeSessions/verificationPolicy.test.ts +226 -0
- package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +633 -0
- package/src/__tests__/resolverBase.fls.test.ts +87 -4
- package/src/__tests__/serverExtensionReservedRoots.test.ts +2 -0
- package/src/__tests__/sharedCache.test.ts +310 -0
- package/src/__tests__/translateBracketsToPG.test.ts +20 -0
- package/src/__tests__/widgetGuestElevation.test.ts +44 -0
- package/src/agentSessions/BridgeHostInstance.ts +32 -0
- package/src/agentSessions/HostInstance.ts +124 -28
- package/src/agentSessions/ReturningVisitorRecap.ts +7 -3
- package/src/agentSessions/SessionJanitor.ts +543 -57
- package/src/agentSessions/SessionManager.ts +125 -25
- package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
- package/src/agentSessions/index.ts +10 -3
- package/src/auth/actionAuthorization.ts +116 -0
- package/src/auth/index.ts +3 -8
- package/src/auth/magicLink/MagicLinkService.ts +6 -3
- package/src/auth/newUsers.ts +99 -11
- package/src/config.ts +255 -3
- package/src/context.ts +26 -34
- package/src/generated/graphql-schemas/__mj.ts +22701 -18838
- package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
- package/src/generic/ResolverBase.ts +210 -36
- package/src/generic/RunViewResolver.ts +37 -2
- package/src/index.ts +236 -170
- package/src/isolatedProvider.ts +22 -0
- package/src/postgresqlCompat.ts +21 -0
- package/src/postgresqlPoolSettings.ts +213 -0
- package/src/realtimeProxy/RealtimeProxyServer.ts +148 -16
- package/src/realtimeProxy/RealtimeRelayTunnel.ts +645 -0
- package/src/realtimeSessions/README.md +179 -0
- package/src/realtimeSessions/RealtimeSessionEventService.ts +274 -0
- package/src/realtimeSessions/RealtimeSessionVerificationService.ts +167 -0
- package/src/realtimeSessions/RealtimeVerifyRouter.ts +108 -0
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
- package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
- package/src/realtimeSessions/index.ts +16 -0
- package/src/realtimeSessions/sessionAccess.ts +141 -0
- package/src/realtimeSessions/sessionConfigStamps.ts +123 -0
- package/src/realtimeSessions/subscriptionAuthorization.ts +116 -0
- package/src/realtimeSessions/verificationCore.ts +502 -0
- package/src/realtimeSessions/verificationPages.ts +149 -0
- package/src/realtimeSessions/verificationPolicy.ts +321 -0
- package/src/realtimeSessions/verificationWorkflow.ts +702 -0
- package/src/realtimeWidget/widgetGuestElevation.ts +34 -2
- package/src/resolvers/ActionResolver.ts +84 -21
- package/src/resolvers/AdhocQueryResolver.ts +105 -187
- package/src/resolvers/CacheStatsResolver.ts +89 -2
- package/src/resolvers/ClientToolRequestResolver.ts +25 -7
- package/src/resolvers/EntityRecordNameResolver.ts +9 -51
- package/src/resolvers/FetchEntityVectorsResolver.ts +97 -2
- package/src/resolvers/GetDataResolver.ts +10 -0
- package/src/resolvers/HumanHandoffResolver.ts +312 -0
- package/src/resolvers/MCPResolver.ts +8 -5
- package/src/resolvers/MeetingResolver.ts +438 -0
- package/src/resolvers/RealtimeBridgeResolver.ts +385 -19
- package/src/resolvers/RealtimeClientSessionResolver.ts +503 -344
- package/src/resolvers/RealtimeSessionEventsResolver.ts +133 -0
- package/src/resolvers/RealtimeSessionVerificationResolver.ts +174 -0
- package/src/resolvers/RemoteBrowserActionResolver.ts +11 -1
- package/src/resolvers/RunAIAgentResolver.ts +78 -11
- package/src/resolvers/RunTemplateResolver.ts +13 -12
- package/src/resolvers/SyncDataResolver.ts +6 -2
- package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
- package/src/resolvers/UserAvatarResolver.ts +111 -0
- package/src/resolvers/UserResolver.ts +2 -16
- package/src/resolvers/adhocTimeout.ts +12 -0
- package/src/resolvers/agentVisionConsentAudit.ts +82 -0
- package/src/resolvers/avatarInputValidation.ts +123 -0
- package/src/resolvers/currentUserRoles.ts +32 -1
- package/src/resolvers/meetings/index.ts +4 -0
- package/src/resolvers/meetings/meetingHelpers.ts +307 -0
- package/src/resolvers/meetings/meetingMutations.ts +556 -0
- package/src/resolvers/meetings/meetingQueries.ts +172 -0
- package/src/resolvers/meetings/meetingTypes.ts +238 -0
- package/src/rest/ViewOperationsHandler.ts +8 -5
- package/src/services/TaskGraphActionRunner.ts +29 -1
- package/src/services/TaskGraphContinuationDeliverer.ts +13 -7
- package/src/services/WorkQueueHostService.ts +129 -0
- package/src/services/workQueueConfig.ts +52 -0
- package/src/sharedCache.ts +302 -0
- package/src/types.ts +12 -0
- package/dist/resolvers/adhoc-query-helpers.d.ts +0 -33
- package/dist/resolvers/adhoc-query-helpers.d.ts.map +0 -1
- package/dist/resolvers/adhoc-query-helpers.js +0 -50
- 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 -67
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LogError, LogStatus, RunView } from '@memberjunction/core';
|
|
2
|
-
import {
|
|
2
|
+
import { IndexByUUID } from '@memberjunction/global';
|
|
3
3
|
import { UserCache } from '@memberjunction/sqlserver-dataprovider';
|
|
4
4
|
/**
|
|
5
5
|
* Grace period after a run reaches a terminal state before its conversation detail is considered
|
|
@@ -200,12 +200,12 @@ async function loadConversationOwners(rv, conversationIds, contextUser) {
|
|
|
200
200
|
if (!result.Success) {
|
|
201
201
|
return owners;
|
|
202
202
|
}
|
|
203
|
-
const
|
|
203
|
+
const usersByID = IndexByUUID(UserCache.Instance.Users);
|
|
204
204
|
for (const row of result.Results ?? []) {
|
|
205
205
|
if (!row.UserID) {
|
|
206
206
|
continue;
|
|
207
207
|
}
|
|
208
|
-
const user =
|
|
208
|
+
const user = usersByID.Get(row.UserID);
|
|
209
209
|
if (user) {
|
|
210
210
|
owners.set(row.ID, user);
|
|
211
211
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrphanedConversationDetailReconciler.js","sourceRoot":"","sources":["../../src/generic/OrphanedConversationDetailReconciler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"OrphanedConversationDetailReconciler.js","sourceRoot":"","sources":["../../src/generic/OrphanedConversationDetailReconciler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpE,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,wCAAwC,CAAC;AAInE;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAEpD,mFAAmF;AACnF,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC;;;GAGG;AACH,MAAM,uBAAuB,GAAmC,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;AAElG;;;GAGG;AACH,MAAM,qBAAqB,GAAmC,CAAC,GAAG,uBAAuB,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AAElH;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,oCAAoC,CACtD,QAA2B,EAC3B,WAAqB,EACrB,UAAkB,sBAAsB;IAExC,IAAI,CAAC;QACD,MAAM,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;QAElD,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,OAAO,CACjC;YACI,UAAU,EAAE,0BAA0B;YACtC,WAAW,EAAE,yCAAyC,oBAAoB,CAAC,QAAQ,CAAC,EAAE;YACtF,OAAO,EAAE,oBAAoB;YAC7B,OAAO,EAAE,oBAAoB;YAC7B,UAAU,EAAE,eAAe;SAC9B,EACD,WAAW,CACd,CAAC;QACF,IAAI,CAAC,YAAY,CAAC,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;YACzD,OAAO,CAAC,CAAC;QACb,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,gBAAgB,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,WAAW,CAAC,CAAC;QAClG,IAAI,YAAY,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,CAAC;QACb,CAAC;QAED,MAAM,oBAAoB,GAAG,MAAM,sBAAsB,CACrD,EAAE,EACF,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAClF,WAAW,CACd,CAAC;QAEF,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;QACpC,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,KAAK,MAAM,MAAM,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;YACxC,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxC,IAAI,CAAC,GAAG,EAAE,CAAC;gBACP,gFAAgF;gBAChF,SAAS;YACb,CAAC;YACD,MAAM,WAAW,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;YAChF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,GAAG,MAAM,EAAE,CAAC;gBACxD,iFAAiF;gBACjF,SAAS;YACb,CAAC;YAED,6DAA6D;YAC7D,EAAE;YACF,wFAAwF;YACxF,gFAAgF;YAChF,iFAAiF;YACjF,6EAA6E;YAC7E,qFAAqF;YACrF,qFAAqF;YACrF,4DAA4D;YAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAClG,IAAI,CAAC,KAAK,EAAE,CAAC;gBACT,QAAQ,CAAC,+DAA+D,MAAM,CAAC,cAAc,oBAAoB,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;gBAC9H,SAAS;YACb,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,eAAe,CAA6B,0BAA0B,EAAE,KAAK,CAAC,CAAC;YAC/G,IAAI,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;gBACpC,QAAQ,CAAC,kDAAkD,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC;gBAClF,SAAS;YACb,CAAC;YACD,IAAI,QAAQ,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;gBACpC,qEAAqE;gBACrE,SAAS;YACb,CAAC;YACD,sFAAsF;YACtF,oFAAoF;YACpF,IAAI,uBAAuB,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC/C,QAAQ,CAAC,MAAM,GAAG,UAAU,CAAC;YACjC,CAAC;iBAAM,CAAC;gBACJ,QAAQ,CAAC,MAAM,GAAG,OAAO,CAAC;gBAC1B,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;oBACpB,QAAQ,CAAC,OAAO,GAAG,GAAG,CAAC,YAAY,IAAI,UAAU,CAAC;gBACtD,CAAC;YACL,CAAC;YAED,IAAI,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;gBACxB,MAAM,EAAE,CAAC;YACb,CAAC;iBAAM,CAAC;gBACJ,MAAM,MAAM,GAAG,QAAQ,CAAC,YAAY,CAAC;gBACrC,QAAQ,CACJ,mDAAmD,MAAM,CAAC,EAAE,IAAI;oBAChE,GAAG,MAAM,EAAE,eAAe,IAAI,MAAM,EAAE,OAAO,IAAI,YAAY,EAAE,CAClE,CAAC;YACN,CAAC;QACL,CAAC;QAED,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACb,SAAS,CAAC,mCAAmC,MAAM,sDAAsD,CAAC,CAAC;QAC/G,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACX,4DAA4D;QAC5D,QAAQ,CAAC,yCAAyC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACtG,OAAO,CAAC,CAAC;IACb,CAAC;AACL,CAAC;AAQD,SAAS,iBAAiB,CAAC,QAA2B;IAClD,MAAM,CAAC,GAAG,QAAwD,CAAC;IACnE,OAAO,OAAO,CAAC,CAAC,eAAe,KAAK,UAAU,IAAI,OAAO,CAAC,CAAC,kBAAkB,KAAK,UAAU,CAAC;AACjG,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,oBAAoB,CAAC,QAA2B;IACrD,MAAM,IAAI,GAAG,QAAQ,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;IACxD,IAAI,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,CAAC;IACd,CAAC;IACD,MAAM,QAAQ,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpE,OAAO,CACH,sBAAsB,QAAQ,CAAC,eAAe,CAAC,sBAAsB,CAAC,EAAE;QACxE,SAAS,QAAQ,CAAC,kBAAkB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE;QACtE,UAAU,QAAQ,CAAC,eAAe,CAAC,QAAQ,CAAC,QAAQ,QAAQ,IAAI,CACnE,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,gBAAgB,CAC3B,EAAW,EACX,SAAmB,EACnB,WAAqB;IAErB,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAC3B;QACI,UAAU,EAAE,mBAAmB;QAC/B,WAAW,EAAE,4BAA4B,MAAM,GAAG;QAClD,OAAO,EAAE,qBAAqB;QAC9B,UAAU,EAAE,eAAe;KAC9B,EACD,WAAW,CACd,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAClB,OAAO,IAAI,GAAG,EAAE,CAAC;IACrB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAA8B,CAAC;IACrD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,GAAG,CAAC,oBAAoB,CAAC;QAC1C,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YACpC,SAAS,CAAC,uCAAuC;QACrD,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,yDAAyD;IACzD,KAAK,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;QACnC,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,sBAAsB,CACjC,EAAW,EACX,eAAyB,EACzB,WAAqB;IAErB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC3C,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,MAAM,CAAC;IAClB,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChG,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAC3B;QACI,UAAU,EAAE,mBAAmB;QAC/B,WAAW,EAAE,UAAU,MAAM,GAAG;QAChC,MAAM,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC;QACxB,UAAU,EAAE,QAAQ;KACvB,EACD,WAAW,CACd,CAAC;IACF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAClB,OAAO,MAAM,CAAC;IAClB,CAAC;IACD,MAAM,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACrC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;YACd,SAAS;QACb,CAAC;QACD,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,IAAI,EAAE,CAAC;YACP,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QAC7B,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC"}
|
|
@@ -169,7 +169,26 @@ export declare class ResolverBase {
|
|
|
169
169
|
* into the view. Subqueries against another entity's BaseView therefore do not inherit that
|
|
170
170
|
* entity's RLS; they are still restricted to the view (not the table).
|
|
171
171
|
*/
|
|
172
|
-
protected assertClientClauseUsesEntityBaseViews(clause: string | undefined | null, label: string, provider?: IMetadataProvider
|
|
172
|
+
protected assertClientClauseUsesEntityBaseViews(clause: string | undefined | null, label: string, provider?: IMetadataProvider, user?: UserInfo, options?: {
|
|
173
|
+
LeaveUnparseableToProvider?: boolean;
|
|
174
|
+
}): void;
|
|
175
|
+
/**
|
|
176
|
+
* SECURITY — screen for a FULL client-supplied SQL statement (ad-hoc query path). In addition
|
|
177
|
+
* to the SELECT-only validation the caller performs, every table reference must resolve to an
|
|
178
|
+
* entity BaseView the acting user can read UNSCOPED. CTE names defined by the statement itself
|
|
179
|
+
* are excluded from the check. Fails closed: no user, or an unresolvable reference, refuses.
|
|
180
|
+
*
|
|
181
|
+
* Entity CanRead is not unscoped base-view read authority: RunView narrows a granted read with
|
|
182
|
+
* row-level security and denied-field projection, and raw SQL applies neither. Rather than try
|
|
183
|
+
* to rewrite arbitrary SQL (joins, CTEs) to compose that policy, this refuses any entity that
|
|
184
|
+
* carries a row filter or a denied field for the caller — those reads belong on RunView. For
|
|
185
|
+
* the remaining entities CanRead genuinely means "every row and field of the view".
|
|
186
|
+
*/
|
|
187
|
+
protected assertFullQueryUsesReadableEntityViews(sqlText: string, provider: IMetadataProvider | undefined, user: UserInfo | undefined, label?: string): void;
|
|
188
|
+
/** Refuses an entity whose read is narrowed for this user by row-level security or field-level denials. */
|
|
189
|
+
private assertUnscopedRead;
|
|
190
|
+
/** Walks a parsed AST collecting the names of CTEs the statement itself defines. */
|
|
191
|
+
private collectCTENames;
|
|
173
192
|
/**
|
|
174
193
|
* Applies {@link assertClientClauseUsesEntityBaseViews} to every client-supplied clause
|
|
175
194
|
* a view request can carry that is actually a SQL fragment. GraphQL entry points
|
|
@@ -191,25 +210,54 @@ export declare class ResolverBase {
|
|
|
191
210
|
* The single place that is not true is a field carrying `UserSearchParamFormatAPI`, whose
|
|
192
211
|
* admin-authored format may splice the term in unquoted; `createViewUserSearchSQL` re-applies
|
|
193
212
|
* `ValidateUserProvidedSQLClause` for exactly those entities.
|
|
213
|
+
*
|
|
214
|
+
* Each `Aggregates[].expression` gets the same screen, except that text the parser cannot read
|
|
215
|
+
* goes on to the provider, which refuses it per aggregate. The provider never runs the caller's
|
|
216
|
+
* aggregate text: it runs the SQL that `CheckAggregateExpression` rebuilds from a single
|
|
217
|
+
* aggregate call over the entity's columns, and refuses everything else per aggregate.
|
|
194
218
|
*/
|
|
195
219
|
protected screenClientViewClauses(clauses: {
|
|
196
220
|
extraFilter?: string | null;
|
|
197
221
|
orderBy?: string | null;
|
|
198
222
|
userSearchString?: string | null;
|
|
199
223
|
overrideExcludeFilter?: string | null;
|
|
200
|
-
|
|
224
|
+
aggregates?: AggregateExpression[] | null;
|
|
225
|
+
}, provider?: IMetadataProvider, user?: UserInfo): void;
|
|
201
226
|
/** Same write-node walk as EDS `sqlReadOnlyScreen.astContainsWriteNode`. */
|
|
202
227
|
private astContainsWriteNode;
|
|
203
228
|
private static readonly WRITE_NODE_TYPES;
|
|
204
229
|
private dialectForProvider;
|
|
230
|
+
/** True for a reference with no schema — which `SQLParser.ExtractTableRefs` reports as `dbo`. */
|
|
231
|
+
private isUnqualifiedSchema;
|
|
205
232
|
private stripSqlIdent;
|
|
233
|
+
/**
|
|
234
|
+
* `bare` maps an unqualified view name to its entity, or to `null` when the name exists in
|
|
235
|
+
* more than one schema — the database, not this screen, would decide which one runs.
|
|
236
|
+
*/
|
|
206
237
|
private entityBaseViewAllowList;
|
|
238
|
+
/**
|
|
239
|
+
* SECURITY — resolves a table reference against the entity BaseView allow-list and, when an
|
|
240
|
+
* acting user is supplied, additionally requires that user to hold CanRead on the referenced
|
|
241
|
+
* entity. Base views do not embed RLS and entity permissions are otherwise checked only on
|
|
242
|
+
* the TOP entity of a request, so without this check a subquery (or ad-hoc query) could read
|
|
243
|
+
* entities the caller has no read grant on.
|
|
244
|
+
*
|
|
245
|
+
* A schema-qualified reference must match that exact schema — `secret.vwFoo` never resolves
|
|
246
|
+
* through another schema's `vwFoo`. An unqualified reference resolves only when the view name
|
|
247
|
+
* is unique across schemas; an ambiguous one must be qualified, because the database's
|
|
248
|
+
* default-schema resolution, not this screen, would pick which view actually runs.
|
|
249
|
+
*
|
|
250
|
+
* `SQLParser.ExtractTableRefs` reports an unqualified reference as schema `dbo`, so `dbo` is
|
|
251
|
+
* indistinguishable from "no schema" here and both take the unqualified path (an exact
|
|
252
|
+
* `dbo.<view>` entity wins first).
|
|
253
|
+
*/
|
|
254
|
+
private assertTableRefReadable;
|
|
207
255
|
/**
|
|
208
256
|
* Optimized RunViewGenericInternal implementation with:
|
|
209
257
|
* - Field filtering at source (Fix #7)
|
|
210
258
|
* - Improved error handling (Fix #9)
|
|
211
259
|
*/
|
|
212
|
-
protected RunViewGenericInternal(provider: DatabaseProviderBase, viewInfo: MJUserViewEntityExtended, extraFilter: string, orderBy: string, userSearchString: string, excludeUserViewRunID: string | undefined, overrideExcludeFilter: string | undefined, saveViewResults: boolean | undefined, fields: string[] | undefined, ignoreMaxRows: boolean | undefined, excludeDataFromAllPriorViewRuns: boolean | undefined, forceAuditLog: boolean | undefined, auditLogDescription: string | undefined, resultType: string | undefined, userPayload: UserPayload | null, maxRows: number | undefined, startRow: number | undefined, aggregates?: AggregateExpression[], afterKey?: CompositeKey, bypassCache?: boolean, dataSource?: 'Live' | 'Materialized'): Promise<RunViewResult<any>>;
|
|
260
|
+
protected RunViewGenericInternal(provider: DatabaseProviderBase, viewInfo: MJUserViewEntityExtended, extraFilter: string, orderBy: string, userSearchString: string, excludeUserViewRunID: string | undefined, overrideExcludeFilter: string | undefined, saveViewResults: boolean | undefined, fields: string[] | undefined, ignoreMaxRows: boolean | undefined, excludeDataFromAllPriorViewRuns: boolean | undefined, forceAuditLog: boolean | undefined, auditLogDescription: string | undefined, resultType: string | undefined, userPayload: UserPayload | null, maxRows: number | undefined, startRow: number | undefined, aggregates?: AggregateExpression[], afterKey?: CompositeKey, bypassCache?: boolean, dataSource?: 'Live' | 'Materialized', includeBinaryFields?: boolean): Promise<RunViewResult<any>>;
|
|
213
261
|
/**
|
|
214
262
|
* Optimized implementation that:
|
|
215
263
|
* 1. Fetches user info only once (fixes N+1 query)
|
|
@@ -299,20 +347,28 @@ export declare class ResolverBase {
|
|
|
299
347
|
* it never applies them to the entity. An update-denied field can then only become dirty by being
|
|
300
348
|
* named in the mutation input itself, which is precisely the case that check does catch.
|
|
301
349
|
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
350
|
+
* The flag comes after the cheap terms, so the extra load lands only on entities that have the
|
|
351
|
+
* feature switched on, which is almost none of them.
|
|
304
352
|
*
|
|
305
353
|
* `MJ: Record Changes` always loads from the database: its server class allows an update only when
|
|
306
354
|
* Comments is the one dirty field, and "dirty" compared against client-supplied OldValues lets a
|
|
307
355
|
* caller pin forged audit columns as both old and new values. It doesn't track its own changes, so
|
|
308
356
|
* nothing else forces the load.
|
|
357
|
+
*
|
|
358
|
+
* A caller under an Update row filter (a role's Update RLS filter or an API-key row filter) always
|
|
359
|
+
* loads from the database too (#4919). The save's post-image check skips its query when no column
|
|
360
|
+
* the filter reads is dirty, and dirtiness measured against client OldValues is the caller's to
|
|
361
|
+
* choose: claiming the new value of a filter column as its old value made it clean, so the update
|
|
362
|
+
* moved the row outside the caller's scope without the check ever running. Evaluated last: it
|
|
363
|
+
* builds the filter clause, and only callers with such a filter pay for the load. A missing user
|
|
364
|
+
* (a user-cache miss) also loads: with no user the filter can't be ruled out.
|
|
309
365
|
*/
|
|
310
366
|
protected MustLoadTruthFromDatabase(entityInfo: EntityInfo, input: {
|
|
311
367
|
OldValues___?: Array<{
|
|
312
368
|
Key: string;
|
|
313
369
|
Value: unknown;
|
|
314
370
|
}>;
|
|
315
|
-
}, hasDeniedReadFields: boolean, hasNarrowedAuditPayload: boolean): boolean;
|
|
371
|
+
}, hasDeniedReadFields: boolean, hasNarrowedAuditPayload: boolean, user: UserInfo | undefined): boolean;
|
|
316
372
|
/**
|
|
317
373
|
* Field-level security guard for the update path.
|
|
318
374
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResolverBase.d.ts","sourceRoot":"","sources":["../../src/generic/ResolverBase.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,UAAU,EAEV,YAAY,EACZ,oBAAoB,EACpB,eAAe,EAEf,UAAU,EACV,oBAAoB,EAEpB,iBAAiB,EAQjB,kCAAkC,EAIlC,aAAa,EACb,QAAQ,EACT,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAsC,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAG7G,OAAO,EAAE,YAAY,EAAsB,MAAM,cAAc,CAAC;AAOhE,OAAO,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"ResolverBase.d.ts","sourceRoot":"","sources":["../../src/generic/ResolverBase.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseEntity, BaseEntityEvent, CompositeKey, EntityFieldTSType, KeyValuePair, LogDebug, LogError, LogStatus, Metadata, ReadableFieldsTransportKey, RecordChangeFieldSecurityProjector, RecordChangePayloadFields, } from '@memberjunction/core';
|
|
1
|
+
import { BaseEntity, BaseEntityEvent, CompositeKey, EntityFieldTSType, EntityPermissionType, KeyValuePair, LogDebug, LogError, LogStatus, Metadata, ReadableFieldsTransportKey, RecordChangeFieldSecurityProjector, RecordChangePayloadFields, } from '@memberjunction/core';
|
|
2
2
|
import { UserCache } from '@memberjunction/generic-database-provider';
|
|
3
3
|
import { AuthorizationError } from 'type-graphql';
|
|
4
4
|
import { GraphQLError } from 'graphql';
|
|
@@ -6,10 +6,10 @@ import { RefusalExtensions } from './refusalExtensions.js';
|
|
|
6
6
|
import { GetAPIKeyEngine } from '@memberjunction/api-keys';
|
|
7
7
|
import { httpTransport, CloudEvent, emitterFor } from 'cloudevents';
|
|
8
8
|
import { DeleteOptionsInput } from './DeleteOptionsInput.js';
|
|
9
|
-
import { MJEventType, MJGlobal, ENCRYPTED_SENTINEL, EscapeSQLString, IsValueEncrypted, IsOnlyTimezoneShift } from '@memberjunction/global';
|
|
9
|
+
import { MJEventType, MJGlobal, ENCRYPTED_SENTINEL, ReplaceByteArraysWithBase64, EscapeSQLString, IsValueEncrypted, IsOnlyTimezoneShift } from '@memberjunction/global';
|
|
10
10
|
import { SQLParser } from '@memberjunction/sql-parser';
|
|
11
11
|
import { PostgreSQLDialect, SQLServerDialect } from '@memberjunction/sql-dialect';
|
|
12
|
-
import { EncryptionEngine } from '@memberjunction/encryption';
|
|
12
|
+
import { EncryptionEngine, IsEncryptionKeyUnavailable, ReportEncryptionKeyUnavailable } from '@memberjunction/encryption';
|
|
13
13
|
import { PublishStatusUpdate } from './PushStatusResolver.js';
|
|
14
14
|
import { CACHE_INVALIDATION_TOPIC, MayBroadcastRecordData } from './CacheInvalidationResolver.js';
|
|
15
15
|
import { PubSubManager } from './PubSubManager.js';
|
|
@@ -57,7 +57,10 @@ export class ResolverBase {
|
|
|
57
57
|
return null;
|
|
58
58
|
}
|
|
59
59
|
// Shallow copy up front so every write below lands on our object, never the caller's.
|
|
60
|
-
|
|
60
|
+
// Binary values are base64 strings everywhere above the providers; any byte array that still
|
|
61
|
+
// reaches a resolver (custom code, an external driver) is converted here, because GraphQL's
|
|
62
|
+
// String scalar cannot serialize a Buffer and would fail the whole response.
|
|
63
|
+
dataObject = ReplaceByteArraysWithBase64({ ...dataObject });
|
|
61
64
|
// for the given entity name provided, check to see if there are any fields
|
|
62
65
|
// where the code name is different from the field name, and for just those
|
|
63
66
|
// fields, iterate through the dataObject and REPLACE the property that has the field name
|
|
@@ -148,7 +151,12 @@ export class ResolverBase {
|
|
|
148
151
|
dataObject[fieldName] = decryptedValue;
|
|
149
152
|
}
|
|
150
153
|
catch (err) {
|
|
151
|
-
|
|
154
|
+
if (IsEncryptionKeyUnavailable(err)) {
|
|
155
|
+
ReportEncryptionKeyUnavailable(err);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
LogError(`Failed to decrypt field ${fieldName} for API response: ${err}`);
|
|
159
|
+
}
|
|
152
160
|
// On decryption failure, use sentinel for safety
|
|
153
161
|
dataObject[fieldName] = ENCRYPTED_SENTINEL;
|
|
154
162
|
}
|
|
@@ -462,7 +470,7 @@ export class ResolverBase {
|
|
|
462
470
|
const viewInfo = result.Results[0];
|
|
463
471
|
return this.RunViewGenericInternal(provider, viewInfo, viewInput.ExtraFilter, viewInput.OrderBy, viewInput.UserSearchString, viewInput.ExcludeUserViewRunID, viewInput.OverrideExcludeFilter, viewInput.SaveViewResults, viewInput.Fields, viewInput.IgnoreMaxRows, viewInput.ExcludeDataFromAllPriorViewRuns, viewInput.ForceAuditLog, viewInput.AuditLogDescription, viewInput.ResultType, userPayload, viewInput.MaxRows, viewInput.StartRow, viewInput.Aggregates, viewInput.AfterKey
|
|
464
472
|
? CompositeKey.FromKeyValuePairs(viewInput.AfterKey.KeyValuePairs)
|
|
465
|
-
: undefined, viewInput.BypassCache, viewInput.DataSource);
|
|
473
|
+
: undefined, viewInput.BypassCache, viewInput.DataSource, viewInput.IncludeBinaryFields);
|
|
466
474
|
}
|
|
467
475
|
else {
|
|
468
476
|
LogError(`RunViewByNameGeneric: View ${viewInput.ViewName} not found or no results returned`);
|
|
@@ -483,7 +491,7 @@ export class ResolverBase {
|
|
|
483
491
|
const contextUser = this.GetUserFromPayload(userPayload);
|
|
484
492
|
const viewInfo = await provider.GetEntityObject('MJ: User Views', contextUser);
|
|
485
493
|
await viewInfo.Load(viewInput.ViewID);
|
|
486
|
-
return this.RunViewGenericInternal(provider, viewInfo, viewInput.ExtraFilter, viewInput.OrderBy, viewInput.UserSearchString, viewInput.ExcludeUserViewRunID, viewInput.OverrideExcludeFilter, viewInput.SaveViewResults, viewInput.Fields, viewInput.IgnoreMaxRows, viewInput.ExcludeDataFromAllPriorViewRuns, viewInput.ForceAuditLog, viewInput.AuditLogDescription, viewInput.ResultType, userPayload, viewInput.MaxRows, viewInput.StartRow, viewInput.Aggregates, undefined, viewInput.BypassCache, viewInput.DataSource);
|
|
494
|
+
return this.RunViewGenericInternal(provider, viewInfo, viewInput.ExtraFilter, viewInput.OrderBy, viewInput.UserSearchString, viewInput.ExcludeUserViewRunID, viewInput.OverrideExcludeFilter, viewInput.SaveViewResults, viewInput.Fields, viewInput.IgnoreMaxRows, viewInput.ExcludeDataFromAllPriorViewRuns, viewInput.ForceAuditLog, viewInput.AuditLogDescription, viewInput.ResultType, userPayload, viewInput.MaxRows, viewInput.StartRow, viewInput.Aggregates, undefined, viewInput.BypassCache, viewInput.DataSource, viewInput.IncludeBinaryFields);
|
|
487
495
|
}
|
|
488
496
|
catch (err) {
|
|
489
497
|
console.log(err);
|
|
@@ -506,7 +514,7 @@ export class ResolverBase {
|
|
|
506
514
|
EntityID: entity.ID,
|
|
507
515
|
EntityBaseView: entity.BaseView,
|
|
508
516
|
}; // only providing a few bits of data here, but it's enough to get the view to run
|
|
509
|
-
return this.RunViewGenericInternal(provider, viewInfo, viewInput.ExtraFilter, viewInput.OrderBy, viewInput.UserSearchString, viewInput.ExcludeUserViewRunID, viewInput.OverrideExcludeFilter, false, viewInput.Fields, viewInput.IgnoreMaxRows, false, viewInput.ForceAuditLog, viewInput.AuditLogDescription, viewInput.ResultType, userPayload, viewInput.MaxRows, viewInput.StartRow, viewInput.Aggregates, undefined, viewInput.BypassCache, viewInput.DataSource);
|
|
517
|
+
return this.RunViewGenericInternal(provider, viewInfo, viewInput.ExtraFilter, viewInput.OrderBy, viewInput.UserSearchString, viewInput.ExcludeUserViewRunID, viewInput.OverrideExcludeFilter, false, viewInput.Fields, viewInput.IgnoreMaxRows, false, viewInput.ForceAuditLog, viewInput.AuditLogDescription, viewInput.ResultType, userPayload, viewInput.MaxRows, viewInput.StartRow, viewInput.Aggregates, undefined, viewInput.BypassCache, viewInput.DataSource, viewInput.IncludeBinaryFields);
|
|
510
518
|
}
|
|
511
519
|
catch (err) {
|
|
512
520
|
console.log(err);
|
|
@@ -576,6 +584,7 @@ export class ResolverBase {
|
|
|
576
584
|
aggregates: viewInput.Aggregates,
|
|
577
585
|
bypassCache: viewInput.BypassCache,
|
|
578
586
|
dataSource: viewInput.DataSource,
|
|
587
|
+
includeBinaryFields: viewInput.IncludeBinaryFields,
|
|
579
588
|
});
|
|
580
589
|
}
|
|
581
590
|
catch (err) {
|
|
@@ -729,7 +738,7 @@ export class ResolverBase {
|
|
|
729
738
|
* into the view. Subqueries against another entity's BaseView therefore do not inherit that
|
|
730
739
|
* entity's RLS; they are still restricted to the view (not the table).
|
|
731
740
|
*/
|
|
732
|
-
assertClientClauseUsesEntityBaseViews(clause, label, provider) {
|
|
741
|
+
assertClientClauseUsesEntityBaseViews(clause, label, provider, user, options) {
|
|
733
742
|
if (!clause?.trim())
|
|
734
743
|
return;
|
|
735
744
|
const dialect = this.dialectForProvider(provider);
|
|
@@ -740,6 +749,10 @@ export class ResolverBase {
|
|
|
740
749
|
? `SELECT 1 FROM __mj_clause_screen ORDER BY ${clause}`
|
|
741
750
|
: `SELECT 1 FROM __mj_clause_screen WHERE (${clause})`;
|
|
742
751
|
const parser = new SQLParser(wrapped, dialect);
|
|
752
|
+
// A clause the provider checks again on its own (an aggregate expression) leaves text the parser
|
|
753
|
+
// cannot read to that per-item check instead of failing the whole request.
|
|
754
|
+
if (!parser.IsValid && options?.LeaveUnparseableToProvider)
|
|
755
|
+
return;
|
|
743
756
|
if (!parser.IsValid || parser.HasWriteStatement || parser.StatementKind !== 'select') {
|
|
744
757
|
throw new Error(`Invalid ${label}: not a safe read-only filter fragment — refusing under uncertainty`);
|
|
745
758
|
}
|
|
@@ -753,12 +766,78 @@ export class ResolverBase {
|
|
|
753
766
|
const schema = this.stripSqlIdent(t.SchemaName);
|
|
754
767
|
if (table.toLowerCase() === '__mj_clause_screen')
|
|
755
768
|
continue;
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
769
|
+
this.assertTableRefReadable(allowed, schema, table, label, user);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
/**
|
|
773
|
+
* SECURITY — screen for a FULL client-supplied SQL statement (ad-hoc query path). In addition
|
|
774
|
+
* to the SELECT-only validation the caller performs, every table reference must resolve to an
|
|
775
|
+
* entity BaseView the acting user can read UNSCOPED. CTE names defined by the statement itself
|
|
776
|
+
* are excluded from the check. Fails closed: no user, or an unresolvable reference, refuses.
|
|
777
|
+
*
|
|
778
|
+
* Entity CanRead is not unscoped base-view read authority: RunView narrows a granted read with
|
|
779
|
+
* row-level security and denied-field projection, and raw SQL applies neither. Rather than try
|
|
780
|
+
* to rewrite arbitrary SQL (joins, CTEs) to compose that policy, this refuses any entity that
|
|
781
|
+
* carries a row filter or a denied field for the caller — those reads belong on RunView. For
|
|
782
|
+
* the remaining entities CanRead genuinely means "every row and field of the view".
|
|
783
|
+
*/
|
|
784
|
+
assertFullQueryUsesReadableEntityViews(sqlText, provider, user, label = 'SQL') {
|
|
785
|
+
if (!user) {
|
|
786
|
+
throw new Error(`Invalid ${label}: no acting user resolved for ad-hoc SQL — refusing`);
|
|
787
|
+
}
|
|
788
|
+
const dialect = this.dialectForProvider(provider);
|
|
789
|
+
const parser = new SQLParser(sqlText, dialect);
|
|
790
|
+
if (!parser.IsValid || parser.HasWriteStatement || parser.StatementKind !== 'select') {
|
|
791
|
+
throw new Error(`Invalid ${label}: not a safe read-only statement — refusing under uncertainty`);
|
|
792
|
+
}
|
|
793
|
+
if (this.astContainsWriteNode(parser.AST)) {
|
|
794
|
+
throw new Error(`Invalid ${label}: write/DDL nested in a subquery is not permitted`);
|
|
795
|
+
}
|
|
796
|
+
const cteNames = new Set();
|
|
797
|
+
this.collectCTENames(parser.AST, cteNames);
|
|
798
|
+
const allowed = this.entityBaseViewAllowList(provider);
|
|
799
|
+
const tables = SQLParser.ExtractTableRefs(sqlText, dialect);
|
|
800
|
+
for (const t of tables) {
|
|
801
|
+
const table = this.stripSqlIdent(t.TableName);
|
|
802
|
+
const schema = this.stripSqlIdent(t.SchemaName);
|
|
803
|
+
if (this.isUnqualifiedSchema(schema) && cteNames.has(table.toLowerCase()))
|
|
759
804
|
continue;
|
|
760
|
-
|
|
805
|
+
const entity = this.assertTableRefReadable(allowed, schema, table, label, user);
|
|
806
|
+
this.assertUnscopedRead(entity, user, label);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
/** Refuses an entity whose read is narrowed for this user by row-level security or field-level denials. */
|
|
810
|
+
assertUnscopedRead(entity, user, label) {
|
|
811
|
+
if (entity.GetEffectiveRowFilterWhereClause(user, EntityPermissionType.Read, '').length > 0) {
|
|
812
|
+
throw new Error(`Invalid ${label}: entity '${entity.Name}' is row-level-security filtered for you — ad-hoc SQL cannot apply that filter; use RunView`);
|
|
813
|
+
}
|
|
814
|
+
if (entity.GetDeniedReadFields(user).size > 0) {
|
|
815
|
+
throw new Error(`Invalid ${label}: entity '${entity.Name}' has fields you are not permitted to read — ad-hoc SQL cannot project them away; use RunView`);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
/** Walks a parsed AST collecting the names of CTEs the statement itself defines. */
|
|
819
|
+
collectCTENames(node, names) {
|
|
820
|
+
if (!node || typeof node !== 'object')
|
|
821
|
+
return;
|
|
822
|
+
if (Array.isArray(node)) {
|
|
823
|
+
for (const n of node)
|
|
824
|
+
this.collectCTENames(n, names);
|
|
825
|
+
return;
|
|
761
826
|
}
|
|
827
|
+
const obj = node;
|
|
828
|
+
const withClause = obj.with;
|
|
829
|
+
if (Array.isArray(withClause)) {
|
|
830
|
+
for (const cte of withClause) {
|
|
831
|
+
const name = cte?.name;
|
|
832
|
+
if (typeof name === 'string')
|
|
833
|
+
names.add(name.toLowerCase());
|
|
834
|
+
else if (name && typeof name === 'object' && typeof name.value === 'string') {
|
|
835
|
+
names.add(name.value.toLowerCase());
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
for (const v of Object.values(obj))
|
|
840
|
+
this.collectCTENames(v, names);
|
|
762
841
|
}
|
|
763
842
|
/**
|
|
764
843
|
* Applies {@link assertClientClauseUsesEntityBaseViews} to every client-supplied clause
|
|
@@ -781,11 +860,19 @@ export class ResolverBase {
|
|
|
781
860
|
* The single place that is not true is a field carrying `UserSearchParamFormatAPI`, whose
|
|
782
861
|
* admin-authored format may splice the term in unquoted; `createViewUserSearchSQL` re-applies
|
|
783
862
|
* `ValidateUserProvidedSQLClause` for exactly those entities.
|
|
863
|
+
*
|
|
864
|
+
* Each `Aggregates[].expression` gets the same screen, except that text the parser cannot read
|
|
865
|
+
* goes on to the provider, which refuses it per aggregate. The provider never runs the caller's
|
|
866
|
+
* aggregate text: it runs the SQL that `CheckAggregateExpression` rebuilds from a single
|
|
867
|
+
* aggregate call over the entity's columns, and refuses everything else per aggregate.
|
|
784
868
|
*/
|
|
785
|
-
screenClientViewClauses(clauses, provider) {
|
|
786
|
-
this.assertClientClauseUsesEntityBaseViews(clauses.extraFilter, 'ExtraFilter', provider);
|
|
787
|
-
this.assertClientClauseUsesEntityBaseViews(clauses.orderBy, 'OrderBy', provider);
|
|
788
|
-
this.assertClientClauseUsesEntityBaseViews(clauses.overrideExcludeFilter, 'OverrideExcludeFilter', provider);
|
|
869
|
+
screenClientViewClauses(clauses, provider, user) {
|
|
870
|
+
this.assertClientClauseUsesEntityBaseViews(clauses.extraFilter, 'ExtraFilter', provider, user);
|
|
871
|
+
this.assertClientClauseUsesEntityBaseViews(clauses.orderBy, 'OrderBy', provider, user);
|
|
872
|
+
this.assertClientClauseUsesEntityBaseViews(clauses.overrideExcludeFilter, 'OverrideExcludeFilter', provider, user);
|
|
873
|
+
for (const aggregate of clauses.aggregates ?? []) {
|
|
874
|
+
this.assertClientClauseUsesEntityBaseViews(aggregate?.expression, 'Aggregate', provider, user, { LeaveUnparseableToProvider: true });
|
|
875
|
+
}
|
|
789
876
|
}
|
|
790
877
|
/** Same write-node walk as EDS `sqlReadOnlyScreen.astContainsWriteNode`. */
|
|
791
878
|
astContainsWriteNode(node) {
|
|
@@ -810,41 +897,77 @@ export class ResolverBase {
|
|
|
810
897
|
return new PostgreSQLDialect();
|
|
811
898
|
return new SQLServerDialect();
|
|
812
899
|
}
|
|
900
|
+
/** True for a reference with no schema — which `SQLParser.ExtractTableRefs` reports as `dbo`. */
|
|
901
|
+
isUnqualifiedSchema(schema) {
|
|
902
|
+
return !schema || schema.toLowerCase() === 'dbo';
|
|
903
|
+
}
|
|
813
904
|
stripSqlIdent(name) {
|
|
814
905
|
if (!name)
|
|
815
906
|
return '';
|
|
816
907
|
return name.replace(/^\[|\]$/g, '').replace(/^"|"$/g, '').replace(/^`|`$/g, '');
|
|
817
908
|
}
|
|
909
|
+
/**
|
|
910
|
+
* `bare` maps an unqualified view name to its entity, or to `null` when the name exists in
|
|
911
|
+
* more than one schema — the database, not this screen, would decide which one runs.
|
|
912
|
+
*/
|
|
818
913
|
entityBaseViewAllowList(provider) {
|
|
819
|
-
const qualified = new
|
|
820
|
-
const bare = new
|
|
914
|
+
const qualified = new Map();
|
|
915
|
+
const bare = new Map();
|
|
821
916
|
const entities = provider?.Entities ?? [];
|
|
822
917
|
for (const e of entities) {
|
|
823
918
|
const view = this.stripSqlIdent(e.BaseView);
|
|
824
919
|
if (!view)
|
|
825
920
|
continue;
|
|
826
921
|
const schema = this.stripSqlIdent(e.SchemaName);
|
|
827
|
-
|
|
922
|
+
const bareKey = view.toLowerCase();
|
|
923
|
+
bare.set(bareKey, bare.has(bareKey) ? null : e);
|
|
828
924
|
if (schema)
|
|
829
|
-
qualified.
|
|
925
|
+
qualified.set(`${schema}.${view}`.toLowerCase(), e);
|
|
830
926
|
}
|
|
831
927
|
return { qualified, bare };
|
|
832
928
|
}
|
|
929
|
+
/**
|
|
930
|
+
* SECURITY — resolves a table reference against the entity BaseView allow-list and, when an
|
|
931
|
+
* acting user is supplied, additionally requires that user to hold CanRead on the referenced
|
|
932
|
+
* entity. Base views do not embed RLS and entity permissions are otherwise checked only on
|
|
933
|
+
* the TOP entity of a request, so without this check a subquery (or ad-hoc query) could read
|
|
934
|
+
* entities the caller has no read grant on.
|
|
935
|
+
*
|
|
936
|
+
* A schema-qualified reference must match that exact schema — `secret.vwFoo` never resolves
|
|
937
|
+
* through another schema's `vwFoo`. An unqualified reference resolves only when the view name
|
|
938
|
+
* is unique across schemas; an ambiguous one must be qualified, because the database's
|
|
939
|
+
* default-schema resolution, not this screen, would pick which view actually runs.
|
|
940
|
+
*
|
|
941
|
+
* `SQLParser.ExtractTableRefs` reports an unqualified reference as schema `dbo`, so `dbo` is
|
|
942
|
+
* indistinguishable from "no schema" here and both take the unqualified path (an exact
|
|
943
|
+
* `dbo.<view>` entity wins first).
|
|
944
|
+
*/
|
|
945
|
+
assertTableRefReadable(allowed, schema, table, label, user) {
|
|
946
|
+
const qualified = allowed.qualified.get(`${schema}.${table}`.toLowerCase());
|
|
947
|
+
const entity = this.isUnqualifiedSchema(schema) ? qualified ?? allowed.bare.get(table.toLowerCase()) : qualified;
|
|
948
|
+
if (entity === null) {
|
|
949
|
+
throw new Error(`Invalid ${label}: '${table}' is a base view in more than one schema — qualify it with its schema`);
|
|
950
|
+
}
|
|
951
|
+
if (!entity) {
|
|
952
|
+
throw new Error(`Invalid ${label}: subquery must use an entity base view, not '${schema ? schema + '.' : ''}${table}'`);
|
|
953
|
+
}
|
|
954
|
+
if (user) {
|
|
955
|
+
const perms = entity.GetUserPermisions(user);
|
|
956
|
+
if (!perms.CanRead) {
|
|
957
|
+
throw new Error(`Invalid ${label}: you do not have read permission on entity '${entity.Name}' referenced by '${schema ? schema + '.' : ''}${table}'`);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
return entity;
|
|
961
|
+
}
|
|
833
962
|
/**
|
|
834
963
|
* Optimized RunViewGenericInternal implementation with:
|
|
835
964
|
* - Field filtering at source (Fix #7)
|
|
836
965
|
* - Improved error handling (Fix #9)
|
|
837
966
|
*/
|
|
838
|
-
async RunViewGenericInternal(provider, viewInfo, extraFilter, orderBy, userSearchString, excludeUserViewRunID, overrideExcludeFilter, saveViewResults, fields, ignoreMaxRows, excludeDataFromAllPriorViewRuns, forceAuditLog, auditLogDescription, resultType, userPayload, maxRows, startRow, aggregates, afterKey, bypassCache, dataSource) {
|
|
967
|
+
async RunViewGenericInternal(provider, viewInfo, extraFilter, orderBy, userSearchString, excludeUserViewRunID, overrideExcludeFilter, saveViewResults, fields, ignoreMaxRows, excludeDataFromAllPriorViewRuns, forceAuditLog, auditLogDescription, resultType, userPayload, maxRows, startRow, aggregates, afterKey, bypassCache, dataSource, includeBinaryFields) {
|
|
839
968
|
try {
|
|
840
969
|
if (!viewInfo || !userPayload)
|
|
841
970
|
return null;
|
|
842
|
-
// SECURITY: GraphQL ExtraFilter may contain IN (SELECT … FROM <base view>).
|
|
843
|
-
// Screen at this boundary: parse, reject writes, allow only entity BaseViews.
|
|
844
|
-
this.screenClientViewClauses({ extraFilter, orderBy, userSearchString, overrideExcludeFilter }, provider);
|
|
845
|
-
// Check API key scope authorization for view operations
|
|
846
|
-
await this.CheckAPIKeyScopeAuthorization('view:run', viewInfo.Entity, userPayload);
|
|
847
|
-
const md = provider;
|
|
848
971
|
// Prefer the authenticated session's payload user — it is the authoritative per-request
|
|
849
972
|
// identity and (for magic-link sessions) carries the per-session resource scope / synthesized
|
|
850
973
|
// roles that drive RLS. The cached lookup is a fallback for paths where the payload user
|
|
@@ -853,6 +976,13 @@ export class ResolverBase {
|
|
|
853
976
|
?? UserCache.Users.find((u) => u.Email.toLowerCase().trim() === userPayload?.email.toLowerCase().trim());
|
|
854
977
|
if (!user)
|
|
855
978
|
throw new Error(`User ${userPayload?.email} not found in metadata`);
|
|
979
|
+
// SECURITY: GraphQL ExtraFilter may contain IN (SELECT … FROM <base view>).
|
|
980
|
+
// Screen at this boundary: parse, reject writes, allow only entity BaseViews the
|
|
981
|
+
// acting user can read (subqueries do not inherit the top entity's permission check).
|
|
982
|
+
this.screenClientViewClauses({ extraFilter, orderBy, userSearchString, overrideExcludeFilter, aggregates }, provider, user);
|
|
983
|
+
// Check API key scope authorization for view operations
|
|
984
|
+
await this.CheckAPIKeyScopeAuthorization('view:run', viewInfo.Entity, userPayload);
|
|
985
|
+
const md = provider;
|
|
856
986
|
const entityInfo = md.Entities.find((e) => e.Name === viewInfo.Entity);
|
|
857
987
|
if (!entityInfo)
|
|
858
988
|
throw new Error(`Entity ${viewInfo.Entity} not found in metadata`);
|
|
@@ -899,6 +1029,7 @@ export class ResolverBase {
|
|
|
899
1029
|
Aggregates: aggregates,
|
|
900
1030
|
BypassCache: bypassCache,
|
|
901
1031
|
DataSource: dataSource,
|
|
1032
|
+
IncludeBinaryFields: includeBinaryFields,
|
|
902
1033
|
}, user);
|
|
903
1034
|
// Log aggregate results for debugging
|
|
904
1035
|
if (aggregates?.length) {
|
|
@@ -988,7 +1119,8 @@ export class ResolverBase {
|
|
|
988
1119
|
orderBy: param.orderBy,
|
|
989
1120
|
userSearchString: param.userSearchString,
|
|
990
1121
|
overrideExcludeFilter: param.overrideExcludeFilter,
|
|
991
|
-
|
|
1122
|
+
aggregates: param.aggregates,
|
|
1123
|
+
}, md, contextUser ?? undefined);
|
|
992
1124
|
if (param.viewInfo) {
|
|
993
1125
|
// Validate entity only once per entity type
|
|
994
1126
|
const entityName = param.viewInfo.Entity;
|
|
@@ -1028,6 +1160,7 @@ export class ResolverBase {
|
|
|
1028
1160
|
Aggregates: param.aggregates,
|
|
1029
1161
|
BypassCache: param.bypassCache,
|
|
1030
1162
|
DataSource: param.dataSource,
|
|
1163
|
+
IncludeBinaryFields: param.includeBinaryFields,
|
|
1031
1164
|
});
|
|
1032
1165
|
}
|
|
1033
1166
|
// Fix #4: Run views in a single batch through RunViews
|
|
@@ -1366,9 +1499,10 @@ export class ResolverBase {
|
|
|
1366
1499
|
// has TrackRecordChanges off, so without it a client sending OldValues takes the branch below
|
|
1367
1500
|
// that hydrates from those values — never loading what the database actually holds.
|
|
1368
1501
|
const hasNarrowedAuditPayload = this.StripRecordChangePayloadFromClientInput(entityInfo, userInfo, input, clientNewValues);
|
|
1369
|
-
if (this.MustLoadTruthFromDatabase(entityInfo, input, hasDeniedReadFields, hasNarrowedAuditPayload)) {
|
|
1502
|
+
if (this.MustLoadTruthFromDatabase(entityInfo, input, hasDeniedReadFields, hasNarrowedAuditPayload, userInfo)) {
|
|
1370
1503
|
// We get here because the entity tracks record changes, OR the client did not provide OldValues,
|
|
1371
|
-
// OR field-level security is in play — in every case we need the true
|
|
1504
|
+
// OR field-level security or an Update row filter is in play — in every case we need the true
|
|
1505
|
+
// old values from the DB
|
|
1372
1506
|
const cKey = new CompositeKey(entityInfo.PrimaryKeys.map((pk) => {
|
|
1373
1507
|
return {
|
|
1374
1508
|
FieldName: pk.Name,
|
|
@@ -1461,21 +1595,31 @@ export class ResolverBase {
|
|
|
1461
1595
|
* it never applies them to the entity. An update-denied field can then only become dirty by being
|
|
1462
1596
|
* named in the mutation input itself, which is precisely the case that check does catch.
|
|
1463
1597
|
*
|
|
1464
|
-
*
|
|
1465
|
-
*
|
|
1598
|
+
* The flag comes after the cheap terms, so the extra load lands only on entities that have the
|
|
1599
|
+
* feature switched on, which is almost none of them.
|
|
1466
1600
|
*
|
|
1467
1601
|
* `MJ: Record Changes` always loads from the database: its server class allows an update only when
|
|
1468
1602
|
* Comments is the one dirty field, and "dirty" compared against client-supplied OldValues lets a
|
|
1469
1603
|
* caller pin forged audit columns as both old and new values. It doesn't track its own changes, so
|
|
1470
1604
|
* nothing else forces the load.
|
|
1605
|
+
*
|
|
1606
|
+
* A caller under an Update row filter (a role's Update RLS filter or an API-key row filter) always
|
|
1607
|
+
* loads from the database too (#4919). The save's post-image check skips its query when no column
|
|
1608
|
+
* the filter reads is dirty, and dirtiness measured against client OldValues is the caller's to
|
|
1609
|
+
* choose: claiming the new value of a filter column as its old value made it clean, so the update
|
|
1610
|
+
* moved the row outside the caller's scope without the check ever running. Evaluated last: it
|
|
1611
|
+
* builds the filter clause, and only callers with such a filter pay for the load. A missing user
|
|
1612
|
+
* (a user-cache miss) also loads: with no user the filter can't be ruled out.
|
|
1471
1613
|
*/
|
|
1472
|
-
MustLoadTruthFromDatabase(entityInfo, input, hasDeniedReadFields, hasNarrowedAuditPayload) {
|
|
1614
|
+
MustLoadTruthFromDatabase(entityInfo, input, hasDeniedReadFields, hasNarrowedAuditPayload, user) {
|
|
1473
1615
|
return (entityInfo.TrackRecordChanges ||
|
|
1474
1616
|
entityInfo.Name.trim().toLowerCase() === 'mj: record changes' ||
|
|
1475
1617
|
!input.OldValues___ ||
|
|
1476
1618
|
hasDeniedReadFields ||
|
|
1477
1619
|
hasNarrowedAuditPayload ||
|
|
1478
|
-
entityInfo.EnableFieldLevelSecurity
|
|
1620
|
+
entityInfo.EnableFieldLevelSecurity ||
|
|
1621
|
+
!user ||
|
|
1622
|
+
entityInfo.GetEffectiveRowFilterWhereClause(user, EntityPermissionType.Update, '').length > 0);
|
|
1479
1623
|
}
|
|
1480
1624
|
/**
|
|
1481
1625
|
* Field-level security guard for the update path.
|