@memberjunction/server 6.2.0-edge.3 → 6.2.0-edge.5
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 +20 -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 +236 -27
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +682 -54
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +48 -3
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +112 -9
- 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/apiRoot.d.ts +32 -0
- package/dist/apiRoot.d.ts.map +1 -0
- package/dist/apiRoot.js +67 -0
- package/dist/apiRoot.js.map +1 -0
- package/dist/auth/AuthProviderCatalogRouter.d.ts.map +1 -1
- package/dist/auth/AuthProviderCatalogRouter.js +25 -0
- package/dist/auth/AuthProviderCatalogRouter.js.map +1 -1
- package/dist/auth/LoginScreenEngine.d.ts +22 -0
- package/dist/auth/LoginScreenEngine.d.ts.map +1 -0
- package/dist/auth/LoginScreenEngine.js +57 -0
- package/dist/auth/LoginScreenEngine.js.map +1 -0
- 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 +4 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkRouter.js +3 -2
- package/dist/auth/magicLink/MagicLinkRouter.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 +2514 -22
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +166 -4
- 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 +14 -11
- package/dist/context.js.map +1 -1
- package/dist/coreRoutes.d.ts +29 -0
- package/dist/coreRoutes.d.ts.map +1 -0
- package/dist/coreRoutes.js +142 -0
- package/dist/coreRoutes.js.map +1 -0
- package/dist/generated/graphql-schemas/__mj.d.ts +504 -3
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +2456 -7
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +43 -8
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +59 -19
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +3 -3
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/PushStatusResolver.js +1 -1
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +123 -48
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +293 -305
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +16 -0
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/globalCors.d.ts +33 -0
- package/dist/globalCors.d.ts.map +1 -0
- package/dist/globalCors.js +58 -0
- package/dist/globalCors.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +219 -278
- 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/multiTenancy/index.d.ts.map +1 -1
- package/dist/multiTenancy/index.js +22 -2
- package/dist/multiTenancy/index.js.map +1 -1
- 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 +139 -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 +99 -0
- package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts +100 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.d.ts.map +1 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.js +173 -0
- package/dist/realtimeSessions/RecordingAssemblyScheduler.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 +19 -0
- package/dist/realtimeSessions/index.d.ts.map +1 -0
- package/dist/realtimeSessions/index.js +19 -0
- package/dist/realtimeSessions/index.js.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts +30 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js +47 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
- package/dist/realtimeSessions/sessionAccess.d.ts +92 -0
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -0
- package/dist/realtimeSessions/sessionAccess.js +94 -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 +14 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +21 -5
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/ArtifactFileResolver.d.ts +5 -1
- package/dist/resolvers/ArtifactFileResolver.d.ts.map +1 -1
- package/dist/resolvers/ArtifactFileResolver.js +9 -7
- package/dist/resolvers/ArtifactFileResolver.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/ConversationRunEventsResolver.d.ts +8 -4
- package/dist/resolvers/ConversationRunEventsResolver.d.ts.map +1 -1
- package/dist/resolvers/ConversationRunEventsResolver.js +6 -6
- package/dist/resolvers/ConversationRunEventsResolver.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 +2 -1
- package/dist/resolvers/EntityRecordNameResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityRecordNameResolver.js +3 -2
- package/dist/resolvers/EntityRecordNameResolver.js.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts +27 -1
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +148 -2
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +82 -11
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +279 -101
- package/dist/resolvers/FileResolver.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 +6 -3
- package/dist/resolvers/GetDataResolver.js.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.d.ts.map +1 -1
- package/dist/resolvers/HumanHandoffResolver.js +7 -1
- package/dist/resolvers/HumanHandoffResolver.js.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +4 -2
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- 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/QueryResolver.d.ts +2 -0
- package/dist/resolvers/QueryResolver.d.ts.map +1 -1
- package/dist/resolvers/QueryResolver.js +13 -0
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +54 -2
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.js +155 -10
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +157 -33
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +404 -116
- 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 +100 -14
- 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/SearchKnowledgeStreamResolver.d.ts +12 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.d.ts.map +1 -1
- package/dist/resolvers/SearchKnowledgeStreamResolver.js +15 -2
- package/dist/resolvers/SearchKnowledgeStreamResolver.js.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.d.ts.map +1 -1
- package/dist/resolvers/SqlLoggingConfigResolver.js +2 -3
- package/dist/resolvers/SqlLoggingConfigResolver.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/TaskGraphFrameResolver.d.ts +2 -1
- package/dist/resolvers/TaskGraphFrameResolver.d.ts.map +1 -1
- package/dist/resolvers/TaskGraphFrameResolver.js +28 -1
- package/dist/resolvers/TaskGraphFrameResolver.js.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.d.ts +24 -0
- package/dist/resolvers/TestQuerySQLResolver.d.ts.map +1 -1
- package/dist/resolvers/TestQuerySQLResolver.js +65 -13
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts +19 -2
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +102 -24
- package/dist/resolvers/TransactionGroupResolver.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/agent-run-data-guard.d.ts +31 -0
- package/dist/resolvers/agent-run-data-guard.d.ts.map +1 -0
- package/dist/resolvers/agent-run-data-guard.js +72 -0
- package/dist/resolvers/agent-run-data-guard.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/meetingMutations.d.ts +6 -0
- package/dist/resolvers/meetings/meetingMutations.d.ts.map +1 -1
- package/dist/resolvers/meetings/meetingMutations.js +21 -0
- package/dist/resolvers/meetings/meetingMutations.js.map +1 -1
- package/dist/rest/RealtimeRecordingHandler.d.ts +49 -0
- package/dist/rest/RealtimeRecordingHandler.d.ts.map +1 -0
- package/dist/rest/RealtimeRecordingHandler.js +383 -0
- package/dist/rest/RealtimeRecordingHandler.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 +21 -12
- package/dist/rest/ViewOperationsHandler.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts +6 -0
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +6 -0
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/dist/serverExtensionReservedRoots.d.ts +12 -10
- package/dist/serverExtensionReservedRoots.d.ts.map +1 -1
- package/dist/serverExtensionReservedRoots.js +14 -17
- package/dist/serverExtensionReservedRoots.js.map +1 -1
- package/dist/services/MJStorageBlobStore.d.ts +8 -1
- package/dist/services/MJStorageBlobStore.d.ts.map +1 -1
- package/dist/services/MJStorageBlobStore.js +25 -19
- package/dist/services/MJStorageBlobStore.js.map +1 -1
- package/dist/services/StartTaskGraphDispatcher.js +2 -2
- package/dist/services/StartTaskGraphDispatcher.js.map +1 -1
- package/dist/services/TaskGraphActionRunner.d.ts +5 -3
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +27 -5
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/dist/services/TaskGraphAgentRunner.d.ts +14 -2
- package/dist/services/TaskGraphAgentRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphAgentRunner.js +157 -9
- package/dist/services/TaskGraphAgentRunner.js.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.d.ts +57 -4
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +607 -18
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/sharedCache.d.ts +105 -0
- package/dist/sharedCache.d.ts.map +1 -0
- package/dist/sharedCache.js +279 -0
- package/dist/sharedCache.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +99 -96
- package/src/__tests__/ActionResolver.authorization.test.ts +353 -0
- 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__/DatasetResolver.itemFilters.test.ts +201 -0
- package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +433 -0
- package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
- package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -0
- package/src/__tests__/HostInstance.test.ts +84 -0
- package/src/__tests__/HumanHandoffResolver.test.ts +17 -0
- package/src/__tests__/ListOperationsResolver.adhocScreen.test.ts +137 -0
- package/src/__tests__/LoginScreenEngine.test.ts +86 -0
- package/src/__tests__/MJStorageBlobStore.storageGate.test.ts +63 -0
- package/src/__tests__/MeetingResolver.test.ts +75 -1
- package/src/__tests__/RealtimeBridgeResolver.test.ts +415 -6
- 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__/RecordingAssemblyScheduler.test.ts +178 -0
- package/src/__tests__/RemoteBrowserActionResolver.goalRunLink.test.ts +129 -0
- package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
- package/src/__tests__/ResolverBase.aggregateScreen.test.ts +174 -0
- package/src/__tests__/ResolverBase.balancedClause.test.ts +155 -0
- package/src/__tests__/ResolverBase.clauseUnscopedRead.test.ts +127 -0
- package/src/__tests__/ResolverBase.clientOldValues.test.ts +212 -6
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
- package/src/__tests__/ReturningVisitorRecap.test.ts +115 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +170 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +6 -4
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +258 -0
- 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__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
- package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
- package/src/__tests__/SessionJanitor.test.ts +1203 -21
- package/src/__tests__/SessionManager.test.ts +240 -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 +25 -0
- package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +403 -22
- package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
- package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
- package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
- package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
- package/src/__tests__/UserAvatarResolver.test.ts +255 -0
- package/src/__tests__/UserResolver.currentUser.test.ts +16 -1
- package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
- package/src/__tests__/actionAuthorization.test.ts +130 -0
- package/src/__tests__/agent-run-data-guard.test.ts +130 -0
- package/src/__tests__/agentVisionConsentAudit.test.ts +118 -0
- package/src/__tests__/apiRootBoot.test.ts +202 -0
- package/src/__tests__/avatarInputValidation.test.ts +160 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +64 -0
- package/src/__tests__/conversationRunEvents.test.ts +56 -14
- package/src/__tests__/globalCors.test.ts +217 -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__/multiTenancy.postgresRewrite.test.ts +181 -0
- package/src/__tests__/multiTenancy.security.test.ts +51 -0
- package/src/__tests__/multiTenancy.test.ts +1 -1
- 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__/ownedRealtimeSession.test.ts +39 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +7 -0
- package/src/__tests__/realtimeRecordingHandler.test.ts +652 -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__/serverExtensionHostOverlay.test.ts +66 -0
- package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -96
- package/src/__tests__/sharedCache.test.ts +312 -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 +821 -59
- package/src/agentSessions/SessionManager.ts +138 -26
- package/src/agentSessions/SessionRunIDVerifier.ts +171 -0
- package/src/agentSessions/index.ts +10 -3
- package/src/apiRoot.ts +78 -0
- package/src/auth/AuthProviderCatalogRouter.ts +26 -0
- package/src/auth/LoginScreenEngine.ts +50 -0
- package/src/auth/actionAuthorization.ts +116 -0
- package/src/auth/index.ts +4 -8
- package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
- package/src/auth/magicLink/MagicLinkService.ts +6 -3
- package/src/auth/newUsers.ts +99 -11
- package/src/config.ts +171 -5
- package/src/context.ts +15 -10
- package/src/coreRoutes.ts +166 -0
- package/src/generated/graphql-schemas/__mj.ts +1815 -57
- package/src/generic/CacheInvalidationResolver.ts +73 -18
- package/src/generic/OrphanedConversationDetailReconciler.ts +3 -3
- package/src/generic/PushStatusResolver.ts +1 -1
- package/src/generic/ResolverBase.ts +335 -326
- package/src/generic/RunViewResolver.ts +21 -0
- package/src/globalCors.ts +71 -0
- package/src/index.ts +231 -300
- package/src/isolatedProvider.ts +22 -0
- package/src/multiTenancy/index.ts +24 -2
- 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 +168 -0
- package/src/realtimeSessions/RealtimeVerifyRouter.ts +109 -0
- package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +189 -0
- package/src/realtimeSessions/SlidingWindowRateLimiter.ts +92 -0
- package/src/realtimeSessions/index.ts +18 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +55 -0
- package/src/realtimeSessions/sessionAccess.ts +145 -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 +24 -6
- package/src/resolvers/ArtifactFileResolver.ts +9 -8
- package/src/resolvers/CacheStatsResolver.ts +89 -2
- package/src/resolvers/ClientToolRequestResolver.ts +25 -7
- package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
- package/src/resolvers/DatasetResolver.ts +70 -10
- package/src/resolvers/EntityRecordNameResolver.ts +3 -2
- package/src/resolvers/FetchEntityVectorsResolver.ts +179 -2
- package/src/resolvers/FileResolver.ts +321 -106
- package/src/resolvers/GetDataContextDataResolver.ts +139 -41
- package/src/resolvers/GetDataResolver.ts +6 -3
- package/src/resolvers/HumanHandoffResolver.ts +8 -0
- package/src/resolvers/IntegrationDiscoveryResolver.ts +4 -2
- package/src/resolvers/MCPResolver.ts +8 -5
- package/src/resolvers/QueryResolver.ts +11 -0
- package/src/resolvers/RealtimeBridgeResolver.ts +163 -11
- package/src/resolvers/RealtimeClientSessionResolver.ts +511 -129
- 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 +116 -14
- package/src/resolvers/RunTemplateResolver.ts +13 -12
- package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
- package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
- package/src/resolvers/SyncDataResolver.ts +6 -2
- package/src/resolvers/SyncRolesUsersResolver.ts +24 -6
- package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
- package/src/resolvers/TestQuerySQLResolver.ts +68 -13
- package/src/resolvers/TransactionGroupResolver.ts +131 -27
- package/src/resolvers/UserAvatarResolver.ts +111 -0
- package/src/resolvers/UserResolver.ts +2 -16
- package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
- package/src/resolvers/agent-run-data-guard.ts +89 -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/meetingMutations.ts +21 -0
- package/src/rest/RealtimeRecordingHandler.ts +464 -0
- package/src/rest/ViewOperationsHandler.ts +29 -13
- package/src/rest/setupRESTEndpoints.ts +6 -0
- package/src/serverExtensionReservedRoots.ts +14 -17
- package/src/services/MJStorageBlobStore.ts +31 -30
- package/src/services/StartTaskGraphDispatcher.ts +2 -2
- package/src/services/TaskGraphActionRunner.ts +25 -5
- package/src/services/TaskGraphAgentRunner.ts +188 -11
- package/src/services/TaskGraphContinuationDeliverer.ts +713 -17
- package/src/sharedCache.ts +305 -0
- package/src/types.ts +7 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Tests for UserAvatarResolver.UpdateMyAvatar.
|
|
5
|
+
*
|
|
6
|
+
* The decorators are stubbed to no-ops (see TagGovernanceResolver.test.ts for the same shape),
|
|
7
|
+
* and the provider hands back a small fake `MJ: Users` entity that tracks dirty state the way
|
|
8
|
+
* BaseEntity does, so the tests can assert what the row looks like AT THE MOMENT OF SAVE.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const SYSTEM_USER = { ID: 'system-user-id', Email: 'system@example.com', Type: 'Owner' };
|
|
12
|
+
const CALLER = { ID: 'caller-id', Email: 'caller@example.com', Type: 'User' };
|
|
13
|
+
|
|
14
|
+
/** The stored row the fake entity loads. */
|
|
15
|
+
const STORED_ROW: Record<string, string | null> = {
|
|
16
|
+
ID: CALLER.ID,
|
|
17
|
+
Name: 'caller@example.com',
|
|
18
|
+
Email: 'caller@example.com',
|
|
19
|
+
FirstName: 'Alice',
|
|
20
|
+
Type: 'User',
|
|
21
|
+
UserImageURL: 'https://example.com/old.png',
|
|
22
|
+
UserImageIconClass: null,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
class FakeUserEntity {
|
|
26
|
+
public ContextUser: unknown;
|
|
27
|
+
public LoadedID: string | undefined;
|
|
28
|
+
public SavedSnapshot: Record<string, string | null> | undefined;
|
|
29
|
+
public SavedDirtyFields: string[] = [];
|
|
30
|
+
public LoadResult = true;
|
|
31
|
+
public SaveResult = true;
|
|
32
|
+
public SaveThrows: Error | undefined;
|
|
33
|
+
public DirtyOnLoad: Record<string, string> = {};
|
|
34
|
+
public LatestResult = { CompleteMessage: 'spUpdateUser failed: boom' };
|
|
35
|
+
private oldValues: Record<string, string | null> = {};
|
|
36
|
+
private values: Record<string, string | null> = {};
|
|
37
|
+
|
|
38
|
+
constructor(contextUser: unknown) {
|
|
39
|
+
this.ContextUser = contextUser;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async Load(id: string): Promise<boolean> {
|
|
43
|
+
this.LoadedID = id;
|
|
44
|
+
if (!this.LoadResult) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
this.oldValues = { ...STORED_ROW };
|
|
48
|
+
// Simulate state left dirty by something other than this resolver (a subclass hook, say).
|
|
49
|
+
this.values = { ...STORED_ROW, ...this.DirtyOnLoad };
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Revert(): boolean {
|
|
54
|
+
this.values = { ...this.oldValues };
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get UserImageURL(): string | null { return this.values.UserImageURL; }
|
|
59
|
+
set UserImageURL(v: string | null) { this.values.UserImageURL = v; }
|
|
60
|
+
get UserImageIconClass(): string | null { return this.values.UserImageIconClass; }
|
|
61
|
+
set UserImageIconClass(v: string | null) { this.values.UserImageIconClass = v; }
|
|
62
|
+
|
|
63
|
+
async Save(): Promise<boolean> {
|
|
64
|
+
if (this.SaveThrows) {
|
|
65
|
+
throw this.SaveThrows;
|
|
66
|
+
}
|
|
67
|
+
this.SavedSnapshot = { ...this.values };
|
|
68
|
+
this.SavedDirtyFields = Object.keys(this.values).filter((k) => this.values[k] !== this.oldValues[k]);
|
|
69
|
+
return this.SaveResult;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const { state, logStatus, logError, scopeCheck } = vi.hoisted(() => ({
|
|
74
|
+
state: {
|
|
75
|
+
entity: undefined as FakeUserEntity | undefined,
|
|
76
|
+
entityName: undefined as string | undefined,
|
|
77
|
+
configure: undefined as ((e: FakeUserEntity) => void) | undefined,
|
|
78
|
+
systemUserThrows: false,
|
|
79
|
+
},
|
|
80
|
+
logStatus: vi.fn(),
|
|
81
|
+
logError: vi.fn(),
|
|
82
|
+
scopeCheck: vi.fn(async () => undefined),
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
vi.mock('type-graphql', () => ({
|
|
86
|
+
Resolver: () => () => undefined,
|
|
87
|
+
Mutation: () => () => undefined,
|
|
88
|
+
Ctx: () => () => undefined,
|
|
89
|
+
Arg: () => () => undefined,
|
|
90
|
+
ObjectType: () => () => undefined,
|
|
91
|
+
Field: () => () => undefined,
|
|
92
|
+
}));
|
|
93
|
+
|
|
94
|
+
vi.mock('@memberjunction/core', () => ({ LogStatus: logStatus, LogError: logError }));
|
|
95
|
+
|
|
96
|
+
vi.mock('../generic/ResolverBase.js', () => ({
|
|
97
|
+
ResolverBase: class {
|
|
98
|
+
CheckAPIKeyScopeAuthorization = scopeCheck;
|
|
99
|
+
},
|
|
100
|
+
}));
|
|
101
|
+
|
|
102
|
+
vi.mock('../auth/index.js', () => ({
|
|
103
|
+
GetSystemUser: async () => {
|
|
104
|
+
if (state.systemUserThrows) {
|
|
105
|
+
throw new Error('System user not found');
|
|
106
|
+
}
|
|
107
|
+
return SYSTEM_USER;
|
|
108
|
+
},
|
|
109
|
+
}));
|
|
110
|
+
|
|
111
|
+
vi.mock('../util.js', () => ({
|
|
112
|
+
GetReadWriteProvider: () => ({
|
|
113
|
+
GetEntityObject: async (entityName: string, contextUser: unknown) => {
|
|
114
|
+
state.entityName = entityName;
|
|
115
|
+
state.entity = new FakeUserEntity(contextUser);
|
|
116
|
+
state.configure?.(state.entity);
|
|
117
|
+
return state.entity;
|
|
118
|
+
},
|
|
119
|
+
}),
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
import { UserAvatarResolver } from '../resolvers/UserAvatarResolver.js';
|
|
123
|
+
import type { AppContext } from '../types.js';
|
|
124
|
+
|
|
125
|
+
function ctxFor(userRecord: unknown): AppContext {
|
|
126
|
+
return { userPayload: { email: 'caller@example.com', userRecord, sessionId: 's-1' }, providers: [] } as unknown as AppContext;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const PNG = `data:image/png;base64,${Buffer.alloc(16, 1).toString('base64')}`;
|
|
130
|
+
|
|
131
|
+
describe('UserAvatarResolver.UpdateMyAvatar', () => {
|
|
132
|
+
let resolver: UserAvatarResolver;
|
|
133
|
+
|
|
134
|
+
beforeEach(() => {
|
|
135
|
+
state.entity = undefined;
|
|
136
|
+
state.entityName = undefined;
|
|
137
|
+
state.configure = undefined;
|
|
138
|
+
state.systemUserThrows = false;
|
|
139
|
+
logStatus.mockClear();
|
|
140
|
+
logError.mockClear();
|
|
141
|
+
scopeCheck.mockReset();
|
|
142
|
+
scopeCheck.mockImplementation(async () => undefined);
|
|
143
|
+
resolver = new UserAvatarResolver();
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('refuses an unauthenticated request without touching the database', async () => {
|
|
147
|
+
await expect(resolver.UpdateMyAvatar(ctxFor(undefined), PNG, null)).rejects.toThrow('not authenticated');
|
|
148
|
+
expect(state.entity).toBeUndefined();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it.each([
|
|
152
|
+
['an anonymous magic-link guest (the shared Anonymous principal)', { ...CALLER, IsMagicLinkAnonymous: true }],
|
|
153
|
+
['a resource-scoped magic-link session', { ...CALLER, MagicLinkScope: { ResourceID: 'r-1', ResourceType: 'Conversation' } }],
|
|
154
|
+
])('refuses %s without touching the database', async (_label, caller) => {
|
|
155
|
+
const result = await resolver.UpdateMyAvatar(ctxFor(caller), PNG, null);
|
|
156
|
+
expect(result.Success).toBe(false);
|
|
157
|
+
expect(result.ErrorMessage).toContain('scope-limited');
|
|
158
|
+
expect(state.entity).toBeUndefined();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('runs the API key scope check as entity:update on MJ: Users, and stops if it throws', async () => {
|
|
162
|
+
scopeCheck.mockImplementation(async () => {
|
|
163
|
+
throw new Error('scope denied');
|
|
164
|
+
});
|
|
165
|
+
const ctx = ctxFor(CALLER);
|
|
166
|
+
await expect(resolver.UpdateMyAvatar(ctx, PNG, null)).rejects.toThrow('scope denied');
|
|
167
|
+
expect(scopeCheck).toHaveBeenCalledWith('entity:update', 'MJ: Users', ctx.userPayload);
|
|
168
|
+
expect(state.entity).toBeUndefined();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('refuses invalid input before loading anything', async () => {
|
|
172
|
+
const result = await resolver.UpdateMyAvatar(ctxFor(CALLER), 'javascript:alert(1)', null);
|
|
173
|
+
expect(result.Success).toBe(false);
|
|
174
|
+
expect(result.ErrorMessage).toContain('http or https');
|
|
175
|
+
expect(state.entity).toBeUndefined();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("loads the CALLER's own row, taken from the request context", async () => {
|
|
179
|
+
const result = await resolver.UpdateMyAvatar(ctxFor(CALLER), PNG, null);
|
|
180
|
+
expect(result).toEqual({ Success: true });
|
|
181
|
+
expect(state.entityName).toBe('MJ: Users');
|
|
182
|
+
expect(state.entity?.LoadedID).toBe(CALLER.ID);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('makes the write as the system user, and logs which user asked', async () => {
|
|
186
|
+
await resolver.UpdateMyAvatar(ctxFor(CALLER), PNG, null);
|
|
187
|
+
expect(state.entity?.ContextUser).toBe(SYSTEM_USER);
|
|
188
|
+
expect(logStatus).toHaveBeenCalledWith(expect.stringContaining(CALLER.ID));
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('writes the two avatar columns', async () => {
|
|
192
|
+
await resolver.UpdateMyAvatar(ctxFor(CALLER), PNG, null);
|
|
193
|
+
expect(state.entity?.SavedSnapshot).toMatchObject({ UserImageURL: PNG, UserImageIconClass: null });
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('writes ONLY the two avatar columns, even when the loaded entity carried other dirty state', async () => {
|
|
197
|
+
state.configure = (e) => {
|
|
198
|
+
e.DirtyOnLoad = { Email: 'someone-else@example.com', FirstName: 'Mallory', Type: 'Owner' };
|
|
199
|
+
};
|
|
200
|
+
const result = await resolver.UpdateMyAvatar(ctxFor(CALLER), null, 'fa-solid fa-user');
|
|
201
|
+
expect(result.Success).toBe(true);
|
|
202
|
+
expect(state.entity?.SavedDirtyFields.sort()).toEqual(['UserImageIconClass', 'UserImageURL']);
|
|
203
|
+
expect(state.entity?.SavedSnapshot).toEqual({ ...STORED_ROW, UserImageURL: null, UserImageIconClass: 'fa-solid fa-user' });
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('clears both columns when both are null (revert to default)', async () => {
|
|
207
|
+
const result = await resolver.UpdateMyAvatar(ctxFor(CALLER), null, null);
|
|
208
|
+
expect(result.Success).toBe(true);
|
|
209
|
+
expect(state.entity?.SavedSnapshot).toMatchObject({ UserImageURL: null, UserImageIconClass: null });
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('failure paths', () => {
|
|
213
|
+
it('returns a failure when the row cannot be loaded', async () => {
|
|
214
|
+
state.configure = (e) => {
|
|
215
|
+
e.LoadResult = false;
|
|
216
|
+
};
|
|
217
|
+
const result = await resolver.UpdateMyAvatar(ctxFor(CALLER), PNG, null);
|
|
218
|
+
expect(result).toEqual({ Success: false, ErrorMessage: 'Could not load your user record.' });
|
|
219
|
+
expect(state.entity?.SavedSnapshot).toBeUndefined();
|
|
220
|
+
expect(logError).toHaveBeenCalledWith(expect.stringContaining(CALLER.ID));
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("logs the save's CompleteMessage but returns only a generic message when Save() returns false", async () => {
|
|
224
|
+
state.configure = (e) => {
|
|
225
|
+
e.SaveResult = false;
|
|
226
|
+
};
|
|
227
|
+
const result = await resolver.UpdateMyAvatar(ctxFor(CALLER), PNG, null);
|
|
228
|
+
expect(result.Success).toBe(false);
|
|
229
|
+
expect(result.ErrorMessage).toContain('Could not save your avatar');
|
|
230
|
+
expect(result.ErrorMessage).not.toContain('spUpdateUser');
|
|
231
|
+
expect(logError).toHaveBeenCalledWith(expect.stringContaining('spUpdateUser failed: boom'));
|
|
232
|
+
expect(logError).toHaveBeenCalledWith(expect.stringContaining(CALLER.ID));
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('returns a failure (does not throw) when Save() throws', async () => {
|
|
236
|
+
state.configure = (e) => {
|
|
237
|
+
e.SaveThrows = new Error('connection reset');
|
|
238
|
+
};
|
|
239
|
+
const result = await resolver.UpdateMyAvatar(ctxFor(CALLER), PNG, null);
|
|
240
|
+
expect(result.Success).toBe(false);
|
|
241
|
+
expect(result.ErrorMessage).toContain('Could not save your avatar');
|
|
242
|
+
expect(result.ErrorMessage).not.toContain('connection reset');
|
|
243
|
+
expect(logError).toHaveBeenCalledWith(expect.stringContaining('connection reset'));
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it('returns a failure when the system user cannot be resolved', async () => {
|
|
247
|
+
state.systemUserThrows = true;
|
|
248
|
+
const result = await resolver.UpdateMyAvatar(ctxFor(CALLER), PNG, null);
|
|
249
|
+
expect(result.Success).toBe(false);
|
|
250
|
+
expect(result.ErrorMessage).not.toContain('System user');
|
|
251
|
+
expect(logError).toHaveBeenCalledWith(expect.stringContaining('System user not found'));
|
|
252
|
+
expect(state.entity).toBeUndefined();
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
2
|
-
import { LogError } from '@memberjunction/core';
|
|
2
|
+
import { LogError, UserInfo } from '@memberjunction/core';
|
|
3
3
|
import {
|
|
4
4
|
AssertRoleLoadSucceeded,
|
|
5
5
|
RequireContextUser,
|
|
6
6
|
RequireLoadedUser,
|
|
7
7
|
RequireRoleProvider,
|
|
8
|
+
SessionUserFields,
|
|
8
9
|
UserIdExtraFilter,
|
|
9
10
|
} from '../resolvers/currentUserRoles.js';
|
|
10
11
|
|
|
@@ -85,3 +86,17 @@ describe('CurrentUser role-load guards', () => {
|
|
|
85
86
|
});
|
|
86
87
|
});
|
|
87
88
|
});
|
|
89
|
+
|
|
90
|
+
describe('SessionUserFields', () => {
|
|
91
|
+
it('returns every UserInfo field the client asks CurrentUser for', () => {
|
|
92
|
+
// GraphQLDataProvider requests each UserInfo key that doesn't start with a single underscore.
|
|
93
|
+
const requested = Object.keys(new UserInfo(null, null)).filter((k) => k.startsWith('__mj_') || !k.startsWith('_'));
|
|
94
|
+
const returned = Object.keys(SessionUserFields(new UserInfo(null, { ID: 'u-1' })));
|
|
95
|
+
expect(requested.filter((k) => !returned.includes(k))).toEqual([]);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('carries IsTemplate, which the template-user banner reads', () => {
|
|
99
|
+
const user = new UserInfo(null, { ID: 'u-1', Email: 'demo@example.org', IsTemplate: true });
|
|
100
|
+
expect(SessionUserFields(user)).toMatchObject({ ID: 'u-1', Email: 'demo@example.org', IsTemplate: true });
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The REST view routes (`POST /views/:entityName`, `POST /views/batch`, `GET /entities/:entityName`)
|
|
3
|
+
* hand client `ExtraFilter` / `OrderBy` / `OverrideExcludeFilter` text to `RunView` through
|
|
4
|
+
* `ViewOperationsHandler`. These tests pin that the text passes the same base-view AST screen as
|
|
5
|
+
* the GraphQL RunView resolvers before any view runs. Payloads are harmless (stacked `SELECT`s);
|
|
6
|
+
* the point is only whether they reach `RunView`.
|
|
7
|
+
*
|
|
8
|
+
* `@memberjunction/core`'s `Metadata` and `RunView` are replaced at the module boundary, as in
|
|
9
|
+
* ViewOperationsHandler.test.ts; the screen itself runs unmodified.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
12
|
+
|
|
13
|
+
const { mockRunViewFn, mockRunViewsFn, mockEntityList } = vi.hoisted(() => ({
|
|
14
|
+
mockRunViewFn: vi.fn(),
|
|
15
|
+
mockRunViewsFn: vi.fn(),
|
|
16
|
+
mockEntityList: [] as Array<{
|
|
17
|
+
Name: string;
|
|
18
|
+
SchemaName: string;
|
|
19
|
+
BaseView: string;
|
|
20
|
+
GetUserPermisions: (u: unknown) => { CanRead: boolean };
|
|
21
|
+
GetEffectiveRowFilterWhereClause: () => string;
|
|
22
|
+
GetDeniedReadFields: () => Set<string>;
|
|
23
|
+
}>,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
vi.mock('@memberjunction/core', async (importOriginal) => {
|
|
27
|
+
const actual = await importOriginal<typeof import('@memberjunction/core')>();
|
|
28
|
+
class MockMetadata {
|
|
29
|
+
public get Entities() {
|
|
30
|
+
return mockEntityList;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
class MockRunView {
|
|
34
|
+
public RunView(params: unknown, contextUser?: unknown): Promise<unknown> {
|
|
35
|
+
return mockRunViewFn(params, contextUser);
|
|
36
|
+
}
|
|
37
|
+
public RunViews(params: unknown[], contextUser?: unknown): Promise<unknown> {
|
|
38
|
+
return mockRunViewsFn(params, contextUser);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { ...actual, Metadata: MockMetadata, RunView: MockRunView, LogError: vi.fn() };
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
import type { BaseEntity, PlatformSQL, RunViewParams, UserInfo } from '@memberjunction/core';
|
|
45
|
+
import { ViewOperationsHandler } from '../rest/ViewOperationsHandler.js';
|
|
46
|
+
|
|
47
|
+
const BRACKET_STACKED =
|
|
48
|
+
"1 = (SELECT 1 AS [a'])) ; SELECT 1 AS [x] ; SELECT * FROM sales.vwCustomers WHERE (1 = (SELECT 1 AS [b'])";
|
|
49
|
+
|
|
50
|
+
const USER = { ID: 'user-1', Name: 'Reader' } as unknown as UserInfo;
|
|
51
|
+
const OK_RESULT = { Success: true, Results: [{ ID: '1' }] };
|
|
52
|
+
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
vi.clearAllMocks();
|
|
55
|
+
mockEntityList.length = 0;
|
|
56
|
+
const unscoped = { GetEffectiveRowFilterWhereClause: () => '', GetDeniedReadFields: () => new Set<string>() };
|
|
57
|
+
mockEntityList.push(
|
|
58
|
+
{ Name: 'Customers', SchemaName: 'sales', BaseView: 'vwCustomers', GetUserPermisions: () => ({ CanRead: true }), ...unscoped },
|
|
59
|
+
{ Name: 'Ledger', SchemaName: 'sales', BaseView: 'vwLedger', GetUserPermisions: () => ({ CanRead: false }), ...unscoped },
|
|
60
|
+
{
|
|
61
|
+
Name: 'Orders',
|
|
62
|
+
SchemaName: 'sales',
|
|
63
|
+
BaseView: 'vwOrders',
|
|
64
|
+
GetUserPermisions: () => ({ CanRead: true }),
|
|
65
|
+
GetEffectiveRowFilterWhereClause: () => "(OwnerID = 'user-1')",
|
|
66
|
+
GetDeniedReadFields: () => new Set<string>(),
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
mockRunViewFn.mockResolvedValue(OK_RESULT);
|
|
70
|
+
mockRunViewsFn.mockResolvedValue([OK_RESULT]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('ViewOperationsHandler — REST clause screen', () => {
|
|
74
|
+
it('RunView refuses a stacked statement hidden behind a bracket identifier, before RunView', async () => {
|
|
75
|
+
const outcome = await ViewOperationsHandler.RunView({ EntityName: 'Customers', ExtraFilter: BRACKET_STACKED }, USER);
|
|
76
|
+
|
|
77
|
+
expect(mockRunViewFn).not.toHaveBeenCalled();
|
|
78
|
+
expect(outcome.success).toBe(false);
|
|
79
|
+
expect(outcome.error).toMatch(/Invalid ExtraFilter: multiple statements/);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('RunView screens OrderBy and OverrideExcludeFilter too', async () => {
|
|
83
|
+
const cases: RunViewParams[] = [
|
|
84
|
+
{ EntityName: 'Customers', OrderBy: "[a'] ; SELECT 1 AS [x] ; SELECT 1 AS [b']" },
|
|
85
|
+
{ EntityName: 'Customers', OverrideExcludeFilter: BRACKET_STACKED },
|
|
86
|
+
];
|
|
87
|
+
for (const params of cases) {
|
|
88
|
+
const outcome = await ViewOperationsHandler.RunView(params, USER);
|
|
89
|
+
expect(outcome.success).toBe(false);
|
|
90
|
+
}
|
|
91
|
+
expect(mockRunViewFn).not.toHaveBeenCalled();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('RunView refuses a subquery against a base table or an entity the caller cannot read', async () => {
|
|
95
|
+
for (const filter of [
|
|
96
|
+
`EXISTS (SELECT 1 FROM __mj.[User] WHERE [Type] = 'Owner')`,
|
|
97
|
+
`ID IN (SELECT CustomerID FROM sales.vwLedger)`,
|
|
98
|
+
]) {
|
|
99
|
+
const outcome = await ViewOperationsHandler.RunView({ EntityName: 'Customers', ExtraFilter: filter }, USER);
|
|
100
|
+
expect(outcome.success).toBe(false);
|
|
101
|
+
}
|
|
102
|
+
expect(mockRunViewFn).not.toHaveBeenCalled();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('RunView refuses a subquery over an entity that is row-level-security filtered for the caller', async () => {
|
|
106
|
+
const outcome = await ViewOperationsHandler.RunView({ EntityName: 'Customers', ExtraFilter: `ID IN (SELECT CustomerID FROM sales.vwOrders)` }, USER);
|
|
107
|
+
|
|
108
|
+
expect(mockRunViewFn).not.toHaveBeenCalled();
|
|
109
|
+
expect(outcome.success).toBe(false);
|
|
110
|
+
expect(outcome.error).toMatch(/'Orders' is row-level-security filtered for you/);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('RunView screens every variant of a platform-specific filter', async () => {
|
|
114
|
+
const filter: PlatformSQL = { default: `Name = 'x'`, sqlserver: BRACKET_STACKED };
|
|
115
|
+
|
|
116
|
+
const outcome = await ViewOperationsHandler.RunView({ EntityName: 'Customers', ExtraFilter: filter }, USER);
|
|
117
|
+
|
|
118
|
+
expect(mockRunViewFn).not.toHaveBeenCalled();
|
|
119
|
+
expect(outcome.success).toBe(false);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('RunViews refuses the whole batch when one entry carries a payload', async () => {
|
|
123
|
+
const outcome = await ViewOperationsHandler.RunViews(
|
|
124
|
+
[{ EntityName: 'Customers' }, { EntityName: 'Customers', ExtraFilter: BRACKET_STACKED }],
|
|
125
|
+
USER,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(mockRunViewsFn).not.toHaveBeenCalled();
|
|
129
|
+
expect(outcome.success).toBe(false);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('ListEntities refuses a payload in the query-string filter', async () => {
|
|
133
|
+
await expect(
|
|
134
|
+
ViewOperationsHandler.ListEntities({ EntityName: 'Customers', ExtraFilter: BRACKET_STACKED }, USER),
|
|
135
|
+
).rejects.toThrow(/multiple statements/);
|
|
136
|
+
expect(mockRunViewFn).not.toHaveBeenCalled();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('refuses a ViewEntity that was not loaded as an entity (its ID would be spliced into SQL)', async () => {
|
|
140
|
+
const forged = { ID: '0) ; SELECT 1 AS [x] ; SELECT 1 WHERE (1=1', EntityID: 'any' } as unknown as BaseEntity;
|
|
141
|
+
|
|
142
|
+
const outcome = await ViewOperationsHandler.RunView({ EntityName: 'Customers', ViewEntity: forged, ExcludeDataFromAllPriorViewRuns: true }, USER);
|
|
143
|
+
|
|
144
|
+
expect(mockRunViewFn).not.toHaveBeenCalled();
|
|
145
|
+
expect(outcome.success).toBe(false);
|
|
146
|
+
expect(outcome.error).toMatch(/ViewEntity is not accepted over REST/);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('passes a filter that only reads entity base views through to RunView', async () => {
|
|
150
|
+
const filter = `Name = 'O''Brien' AND ID IN (SELECT ID FROM sales.vwCustomers WHERE Region = 'West')`;
|
|
151
|
+
|
|
152
|
+
const outcome = await ViewOperationsHandler.RunView({ EntityName: 'Customers', ExtraFilter: filter, OrderBy: '[Name] DESC' }, USER);
|
|
153
|
+
|
|
154
|
+
expect(outcome.success).toBe(true);
|
|
155
|
+
expect((mockRunViewFn.mock.calls[0][0] as RunViewParams).ExtraFilter).toBe(filter);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The policy that decides who may run an action through a public entry point. It reads the
|
|
3
|
+
* `MJ: Action Authorizations` rows for the action, the caller's roles, one provider's authorizations
|
|
4
|
+
* and role grants, and whether the action's class authorizes its caller itself. It fails closed: an
|
|
5
|
+
* action that no authorization grants, and whose class does not authorize its caller, runs for
|
|
6
|
+
* Owners only.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
9
|
+
import { AuthorizationInfo, AuthorizationRoleInfo, Metadata, UserInfo, UserRoleInfo } from '@memberjunction/core';
|
|
10
|
+
import { EvaluateActionRunAuthorization, IsOwner, type AuthorizationMetadata } from '../auth/actionAuthorization.js';
|
|
11
|
+
|
|
12
|
+
const ANALYST_ROLE = 'A11C0000-0000-4000-8000-0000000000F1';
|
|
13
|
+
const AUDITOR_ROLE = 'A11C0000-0000-4000-8000-0000000000F2';
|
|
14
|
+
const BLOCKED_ROLE = 'A11C0000-0000-4000-8000-0000000000F3';
|
|
15
|
+
|
|
16
|
+
const DATA_TOOLS = new AuthorizationInfo({ ID: 'auth-data-tools', Name: 'Data Tools', IsActive: true });
|
|
17
|
+
const RUN_QUERIES = new AuthorizationInfo({ ID: 'auth-run-queries', Name: 'Run Queries', IsActive: true, ParentID: DATA_TOOLS.ID });
|
|
18
|
+
const RETIRED_TOOL = new AuthorizationInfo({ ID: 'auth-retired-tool', Name: 'Retired Tool', IsActive: false, ParentID: DATA_TOOLS.ID });
|
|
19
|
+
const SWITCHED_OFF = new AuthorizationInfo({ ID: 'auth-switched-off', Name: 'Switched Off', IsActive: false });
|
|
20
|
+
const AUTHORIZATIONS = [DATA_TOOLS, RUN_QUERIES, RETIRED_TOOL, SWITCHED_OFF];
|
|
21
|
+
const AUTHORIZATION_ROLES = [
|
|
22
|
+
new AuthorizationRoleInfo({ ID: 'ar-1', AuthorizationID: RUN_QUERIES.ID, RoleID: ANALYST_ROLE, Type: 'Allow' }),
|
|
23
|
+
new AuthorizationRoleInfo({ ID: 'ar-2', AuthorizationID: DATA_TOOLS.ID, RoleID: AUDITOR_ROLE, Type: 'Allow' }),
|
|
24
|
+
new AuthorizationRoleInfo({ ID: 'ar-3', AuthorizationID: RUN_QUERIES.ID, RoleID: BLOCKED_ROLE, Type: 'Deny' }),
|
|
25
|
+
new AuthorizationRoleInfo({ ID: 'ar-4', AuthorizationID: SWITCHED_OFF.ID, RoleID: ANALYST_ROLE, Type: 'Allow' }),
|
|
26
|
+
];
|
|
27
|
+
/** The provider metadata the decision reads. */
|
|
28
|
+
const METADATA: AuthorizationMetadata = { Authorizations: AUTHORIZATIONS, AuthorizationRoles: AUTHORIZATION_ROLES };
|
|
29
|
+
|
|
30
|
+
const user = (type: string, roleIDs: string[]): UserInfo =>
|
|
31
|
+
new UserInfo(null, { ID: `user-${type}-${roleIDs.join('-')}`, Type: type, UserRoles: roleIDs.map((RoleID) => new UserRoleInfo({ RoleID })) });
|
|
32
|
+
|
|
33
|
+
/** Decides for an action whose class does not authorize its caller. */
|
|
34
|
+
const decide = (caller: UserInfo, linkedAuthorizationIDs: string[]) =>
|
|
35
|
+
EvaluateActionRunAuthorization(caller, linkedAuthorizationIDs, METADATA, false);
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
vi.restoreAllMocks();
|
|
39
|
+
// The decision reads only the metadata it is given, so any read of the global provider fails the test.
|
|
40
|
+
vi.spyOn(Metadata, 'Provider', 'get').mockImplementation(() => {
|
|
41
|
+
throw new Error('The decision read the global metadata provider');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('IsOwner', () => {
|
|
46
|
+
it('recognises the Owner type, padded as the nchar column stores it', () => {
|
|
47
|
+
expect(IsOwner(user('Owner ', []))).toBe(true);
|
|
48
|
+
expect(IsOwner(user('User', [ANALYST_ROLE]))).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('EvaluateActionRunAuthorization', () => {
|
|
53
|
+
describe('Owners', () => {
|
|
54
|
+
it('lets an Owner run any action, linked or not', () => {
|
|
55
|
+
expect(decide(user('Owner ', []), []).Allowed).toBe(true);
|
|
56
|
+
expect(decide(user('Owner ', []), [RUN_QUERIES.ID]).Allowed).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('an action with no Action Authorization rows', () => {
|
|
61
|
+
it('is refused to any other user when its class does not authorize its caller', () => {
|
|
62
|
+
const decision = decide(user('User', [ANALYST_ROLE]), []);
|
|
63
|
+
|
|
64
|
+
expect(decision.Allowed).toBe(false);
|
|
65
|
+
expect(decision.Reason).toMatch(/no action authorization/i);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('runs for any user when its class authorizes its caller, so the action\'s own check decides', () => {
|
|
69
|
+
expect(EvaluateActionRunAuthorization(user('User', [ANALYST_ROLE]), [], METADATA, true).Allowed).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('an action with Action Authorization rows', () => {
|
|
74
|
+
it('is decided by its links, even when its class authorizes its caller', () => {
|
|
75
|
+
expect(EvaluateActionRunAuthorization(user('User', ['some-other-role']), [RUN_QUERIES.ID], METADATA, true).Allowed).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('runs for a user whose role holds a linked authorization', () => {
|
|
79
|
+
expect(decide(user('User', [ANALYST_ROLE]), [RUN_QUERIES.ID]).Allowed).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('runs for a user whose role holds an ancestor of a linked authorization', () => {
|
|
83
|
+
expect(decide(user('User', [AUDITOR_ROLE]), [RUN_QUERIES.ID]).Allowed).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('is refused to a user whose roles hold none of the linked authorizations', () => {
|
|
87
|
+
expect(decide(user('User', ['some-other-role']), [RUN_QUERIES.ID]).Allowed).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('is refused when the linked authorization is inactive', () => {
|
|
91
|
+
expect(decide(user('User', [ANALYST_ROLE]), [SWITCHED_OFF.ID]).Allowed).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('is refused when the linked authorization is inactive, even to a holder of its parent', () => {
|
|
95
|
+
expect(decide(user('User', [AUDITOR_ROLE]), [RETIRED_TOOL.ID]).Allowed).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('is refused when one of the user\'s roles is denied the linked authorization', () => {
|
|
99
|
+
expect(decide(user('User', [ANALYST_ROLE, BLOCKED_ROLE]), [RUN_QUERIES.ID]).Allowed).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('is refused when a role is denied the linked authorization, even if another role holds its parent', () => {
|
|
103
|
+
expect(decide(user('User', [AUDITOR_ROLE, BLOCKED_ROLE]), [RUN_QUERIES.ID]).Allowed).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('ignores a linked authorization that no longer exists in metadata', () => {
|
|
107
|
+
expect(decide(user('User', [ANALYST_ROLE]), ['auth-deleted']).Allowed).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('matches authorization IDs regardless of case', () => {
|
|
111
|
+
expect(decide(user('User', [ANALYST_ROLE]), [RUN_QUERIES.ID.toUpperCase()]).Allowed).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe('the metadata it reads', () => {
|
|
116
|
+
it('decides from the role grants it is given, never from the global provider', () => {
|
|
117
|
+
const sameTreeWithoutGrants: AuthorizationMetadata = { Authorizations: AUTHORIZATIONS, AuthorizationRoles: [] };
|
|
118
|
+
|
|
119
|
+
expect(decide(user('User', [ANALYST_ROLE]), [RUN_QUERIES.ID]).Allowed).toBe(true);
|
|
120
|
+
expect(EvaluateActionRunAuthorization(user('User', [ANALYST_ROLE]), [RUN_QUERIES.ID], sameTreeWithoutGrants, false).Allowed).toBe(false);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('walks to the parent within the authorizations it is given', () => {
|
|
124
|
+
const treeWithoutParent: AuthorizationMetadata = { Authorizations: [RUN_QUERIES], AuthorizationRoles: AUTHORIZATION_ROLES };
|
|
125
|
+
|
|
126
|
+
expect(decide(user('User', [AUDITOR_ROLE]), [RUN_QUERIES.ID]).Allowed).toBe(true);
|
|
127
|
+
expect(EvaluateActionRunAuthorization(user('User', [AUDITOR_ROLE]), [RUN_QUERIES.ID], treeWithoutParent, false).Allowed).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|