@memberjunction/server 6.2.0-edge.4 → 6.2.0-edge.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/agentSessions/SessionJanitor.d.ts +91 -30
- package/dist/agentSessions/SessionJanitor.d.ts.map +1 -1
- package/dist/agentSessions/SessionJanitor.js +311 -81
- package/dist/agentSessions/SessionJanitor.js.map +1 -1
- package/dist/agentSessions/SessionManager.d.ts +17 -3
- package/dist/agentSessions/SessionManager.d.ts.map +1 -1
- package/dist/agentSessions/SessionManager.js +44 -10
- package/dist/agentSessions/SessionManager.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 +27 -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/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +52 -10
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/initializeProviders.d.ts +10 -0
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +37 -1
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/auth/localDevAuth.d.ts +79 -0
- package/dist/auth/localDevAuth.d.ts.map +1 -0
- package/dist/auth/localDevAuth.js +439 -0
- package/dist/auth/localDevAuth.js.map +1 -0
- package/dist/auth/magicLink/MagicLinkKeys.d.ts +2 -2
- package/dist/auth/magicLink/MagicLinkKeys.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.js +1 -1
- package/dist/auth/magicLink/MagicLinkKeys.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/types.d.ts +12 -0
- package/dist/auth/magicLink/types.d.ts.map +1 -1
- package/dist/auth/sessionUserClone.d.ts +1 -1
- package/dist/auth/sessionUserClone.d.ts.map +1 -1
- package/dist/auth/sessionUserClone.js +3 -2
- package/dist/auth/sessionUserClone.js.map +1 -1
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts +292 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js +442 -0
- package/dist/broadcast/BaseCacheInvalidationBroadcastProvider.js.map +1 -0
- package/dist/broadcast/index.d.ts +6 -0
- package/dist/broadcast/index.d.ts.map +1 -0
- package/dist/broadcast/index.js +6 -0
- package/dist/broadcast/index.js.map +1 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts +43 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js +144 -0
- package/dist/broadcast/providers/AWSIoTCoreBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts +46 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js +149 -0
- package/dist/broadcast/providers/AzureWebPubSubBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts +49 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js +131 -0
- package/dist/broadcast/providers/GCPPubSubBroadcastProvider.js.map +1 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts +24 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.d.ts.map +1 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js +51 -0
- package/dist/broadcast/providers/SequencedRoleBucketBroadcastProvider.js.map +1 -0
- package/dist/cache.d.ts +3 -2
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js.map +1 -1
- package/dist/config.d.ts +632 -22
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +104 -4
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +3 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +29 -8
- 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 +156 -0
- package/dist/coreRoutes.js.map +1 -0
- package/dist/generated/graphql-schemas/__mj.d.ts +1016 -29
- package/dist/generated/graphql-schemas/__mj.d.ts.map +1 -1
- package/dist/generated/graphql-schemas/__mj.js +5125 -434
- package/dist/generated/graphql-schemas/__mj.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +85 -30
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +175 -60
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.d.ts.map +1 -1
- package/dist/generic/OrphanedConversationDetailReconciler.js +2 -0
- package/dist/generic/OrphanedConversationDetailReconciler.js.map +1 -1
- package/dist/generic/PubSubManager.d.ts +10 -0
- package/dist/generic/PubSubManager.d.ts.map +1 -1
- package/dist/generic/PubSubManager.js +44 -1
- package/dist/generic/PubSubManager.js.map +1 -1
- package/dist/generic/PushStatusResolver.d.ts +39 -6
- package/dist/generic/PushStatusResolver.d.ts.map +1 -1
- package/dist/generic/PushStatusResolver.js +94 -12
- package/dist/generic/PushStatusResolver.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +168 -69
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +464 -405
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/generic/RunViewResolver.d.ts +11 -3
- package/dist/generic/RunViewResolver.d.ts.map +1 -1
- package/dist/generic/RunViewResolver.js +91 -13
- package/dist/generic/RunViewResolver.js.map +1 -1
- package/dist/generic/actionOutputFieldSecurity.d.ts +26 -0
- package/dist/generic/actionOutputFieldSecurity.d.ts.map +1 -0
- package/dist/generic/actionOutputFieldSecurity.js +47 -0
- package/dist/generic/actionOutputFieldSecurity.js.map +1 -0
- package/dist/generic/fieldSecurityTransport.d.ts +30 -0
- package/dist/generic/fieldSecurityTransport.d.ts.map +1 -0
- package/dist/generic/fieldSecurityTransport.js +30 -0
- package/dist/generic/fieldSecurityTransport.js.map +1 -0
- 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 +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +230 -222
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -1
- package/dist/integration/CustomColumnPromoter.js +6 -1
- package/dist/integration/CustomColumnPromoter.js.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.d.ts +12 -0
- package/dist/logging/variablesLoggingMiddleware.d.ts.map +1 -1
- package/dist/logging/variablesLoggingMiddleware.js +50 -1
- package/dist/logging/variablesLoggingMiddleware.js.map +1 -1
- package/dist/multiTenancy/index.d.ts.map +1 -1
- package/dist/multiTenancy/index.js +22 -2
- package/dist/multiTenancy/index.js.map +1 -1
- package/dist/realtimeSessions/RealtimeSessionVerificationService.d.ts.map +1 -1
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js +2 -1
- package/dist/realtimeSessions/RealtimeSessionVerificationService.js.map +1 -1
- package/dist/realtimeSessions/RealtimeVerifyRouter.d.ts.map +1 -1
- package/dist/realtimeSessions/RealtimeVerifyRouter.js +2 -1
- package/dist/realtimeSessions/RealtimeVerifyRouter.js.map +1 -1
- 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.map +1 -1
- package/dist/realtimeSessions/SessionVerificationPortsBase.js +1 -0
- package/dist/realtimeSessions/SessionVerificationPortsBase.js.map +1 -1
- package/dist/realtimeSessions/index.d.ts +2 -0
- package/dist/realtimeSessions/index.d.ts.map +1 -1
- package/dist/realtimeSessions/index.js +2 -0
- package/dist/realtimeSessions/index.js.map +1 -1
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts +32 -0
- package/dist/realtimeSessions/ownedRealtimeSession.d.ts.map +1 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js +56 -0
- package/dist/realtimeSessions/ownedRealtimeSession.js.map +1 -0
- package/dist/realtimeSessions/sessionAccess.d.ts +91 -8
- package/dist/realtimeSessions/sessionAccess.d.ts.map +1 -1
- package/dist/realtimeSessions/sessionAccess.js +127 -20
- package/dist/realtimeSessions/sessionAccess.js.map +1 -1
- package/dist/realtimeSessions/sessionConfigStamps.js +1 -1
- package/dist/realtimeSessions/sessionConfigStamps.js.map +1 -1
- package/dist/realtimeSessions/subscriptionAuthorization.d.ts.map +1 -1
- package/dist/realtimeSessions/subscriptionAuthorization.js +5 -2
- package/dist/realtimeSessions/subscriptionAuthorization.js.map +1 -1
- package/dist/realtimeSessions/verificationWorkflow.d.ts +2 -0
- package/dist/realtimeSessions/verificationWorkflow.d.ts.map +1 -1
- package/dist/realtimeSessions/verificationWorkflow.js +1 -1
- package/dist/realtimeSessions/verificationWorkflow.js.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetRouter.js +19 -3
- package/dist/realtimeWidget/WidgetRouter.js.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.d.ts +67 -7
- package/dist/realtimeWidget/WidgetSessionService.d.ts.map +1 -1
- package/dist/realtimeWidget/WidgetSessionService.js +201 -19
- package/dist/realtimeWidget/WidgetSessionService.js.map +1 -1
- package/dist/realtimeWidget/host-identity.d.ts +1 -1
- package/dist/realtimeWidget/host-identity.js +1 -1
- package/dist/realtimeWidget/widgetCore.d.ts +20 -1
- package/dist/realtimeWidget/widgetCore.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetCore.js +17 -1
- package/dist/realtimeWidget/widgetCore.js.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.d.ts.map +1 -1
- package/dist/realtimeWidget/widgetGuestElevation.js +2 -1
- package/dist/realtimeWidget/widgetGuestElevation.js.map +1 -1
- package/dist/resolvers/ActionResolver.d.ts +19 -1
- package/dist/resolvers/ActionResolver.d.ts.map +1 -1
- package/dist/resolvers/ActionResolver.js +63 -12
- package/dist/resolvers/ActionResolver.js.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +1 -0
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.d.ts +18 -4
- package/dist/resolvers/AgentRunStatusPublisher.d.ts.map +1 -1
- package/dist/resolvers/AgentRunStatusPublisher.js +32 -8
- package/dist/resolvers/AgentRunStatusPublisher.js.map +1 -1
- package/dist/resolvers/AgentSessionResolver.d.ts +10 -4
- package/dist/resolvers/AgentSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/AgentSessionResolver.js +40 -17
- package/dist/resolvers/AgentSessionResolver.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/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/EntityCommunicationsResolver.d.ts +13 -1
- package/dist/resolvers/EntityCommunicationsResolver.d.ts.map +1 -1
- package/dist/resolvers/EntityCommunicationsResolver.js +50 -14
- package/dist/resolvers/EntityCommunicationsResolver.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 +46 -3
- package/dist/resolvers/FetchEntityVectorsResolver.d.ts.map +1 -1
- package/dist/resolvers/FetchEntityVectorsResolver.js +211 -50
- package/dist/resolvers/FetchEntityVectorsResolver.js.map +1 -1
- package/dist/resolvers/FileCategoryResolver.d.ts.map +1 -1
- package/dist/resolvers/FileCategoryResolver.js +1 -0
- package/dist/resolvers/FileCategoryResolver.js.map +1 -1
- package/dist/resolvers/FileResolver.d.ts +87 -11
- package/dist/resolvers/FileResolver.d.ts.map +1 -1
- package/dist/resolvers/FileResolver.js +292 -105
- 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/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +49 -9
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts +8 -1
- package/dist/resolvers/MergeRecordsResolver.d.ts.map +1 -1
- package/dist/resolvers/MergeRecordsResolver.js +31 -3
- package/dist/resolvers/MergeRecordsResolver.js.map +1 -1
- package/dist/resolvers/PipelineProgressResolver.d.ts +6 -0
- package/dist/resolvers/PipelineProgressResolver.d.ts.map +1 -1
- package/dist/resolvers/PipelineProgressResolver.js +5 -3
- package/dist/resolvers/PipelineProgressResolver.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 +33 -6
- package/dist/resolvers/QueryResolver.js.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.d.ts +2 -0
- package/dist/resolvers/QuerySystemUserResolver.d.ts.map +1 -1
- package/dist/resolvers/QuerySystemUserResolver.js +15 -6
- package/dist/resolvers/QuerySystemUserResolver.js.map +1 -1
- package/dist/resolvers/RSUResolver.d.ts.map +1 -1
- package/dist/resolvers/RSUResolver.js +12 -0
- package/dist/resolvers/RSUResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +18 -10
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +92 -54
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +11 -9
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts +56 -8
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +241 -67
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/RunTemplateResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTemplateResolver.js +4 -4
- package/dist/resolvers/RunTemplateResolver.js.map +1 -1
- package/dist/resolvers/RunTestResolver.d.ts.map +1 -1
- package/dist/resolvers/RunTestResolver.js +3 -1
- package/dist/resolvers/RunTestResolver.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 +4 -0
- package/dist/resolvers/SyncDataResolver.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 +66 -13
- package/dist/resolvers/TestQuerySQLResolver.js.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.d.ts +34 -2
- package/dist/resolvers/TransactionGroupResolver.d.ts.map +1 -1
- package/dist/resolvers/TransactionGroupResolver.js +150 -27
- package/dist/resolvers/TransactionGroupResolver.js.map +1 -1
- package/dist/resolvers/UserViewResolver.d.ts +1 -1
- package/dist/resolvers/UserViewResolver.d.ts.map +1 -1
- package/dist/resolvers/UserViewResolver.js +2 -1
- package/dist/resolvers/UserViewResolver.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/rest/EntityCRUDHandler.d.ts +6 -0
- package/dist/rest/EntityCRUDHandler.d.ts.map +1 -1
- package/dist/rest/EntityCRUDHandler.js +19 -5
- package/dist/rest/EntityCRUDHandler.js.map +1 -1
- package/dist/rest/RESTEndpointHandler.d.ts.map +1 -1
- package/dist/rest/RESTEndpointHandler.js +8 -5
- package/dist/rest/RESTEndpointHandler.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 +9 -1
- package/dist/rest/ViewOperationsHandler.d.ts.map +1 -1
- package/dist/rest/ViewOperationsHandler.js +43 -17
- 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/serverRoleConfigUnits.d.ts +29 -0
- package/dist/serverRoleConfigUnits.d.ts.map +1 -0
- package/dist/serverRoleConfigUnits.js +33 -0
- package/dist/serverRoleConfigUnits.js.map +1 -0
- 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 +59 -2
- package/dist/services/TaskGraphContinuationDeliverer.d.ts.map +1 -1
- package/dist/services/TaskGraphContinuationDeliverer.js +626 -16
- package/dist/services/TaskGraphContinuationDeliverer.js.map +1 -1
- package/dist/sharedCache.d.ts.map +1 -1
- package/dist/sharedCache.js +3 -0
- package/dist/sharedCache.js.map +1 -1
- package/dist/types.d.ts +23 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +99 -98
- package/src/__tests__/ActionResolver.fieldSecurity.test.ts +237 -0
- package/src/__tests__/AdhocQueryResolver.dialect.test.ts +137 -0
- package/src/__tests__/AgentRunStatusPublisher.test.ts +49 -0
- package/src/__tests__/AgentSessionResolver.ownership.test.ts +137 -0
- package/src/__tests__/CustomColumnPromoterOnePass.test.ts +15 -1
- package/src/__tests__/DatasetResolver.itemFilters.test.ts +201 -0
- package/src/__tests__/EntityCommunicationsResolver.actingUser.test.ts +151 -0
- package/src/__tests__/EntityRecordNameResolver.rowSecurity.test.ts +152 -0
- package/src/__tests__/EvolutionActsOnWhatItDetects.test.ts +113 -0
- package/src/__tests__/FetchEntityVectorsResolver.test.ts +369 -18
- package/src/__tests__/FileResolver.fieldSecurity.test.ts +82 -0
- package/src/__tests__/FileResolver.storageAccess.test.ts +562 -0
- package/src/__tests__/GetDataContextDataResolver.sqlItems.test.ts +416 -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__/PipelineProgressResolver.filter.test.ts +58 -0
- package/src/__tests__/QuerySystemUserResolver.fieldSecurity.test.ts +49 -0
- package/src/__tests__/RESTEndpointHandler.test.ts +25 -0
- package/src/__tests__/RealtimeClientSessionResolver.configTamper.test.ts +23 -3
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +324 -46
- package/src/__tests__/RecordingAssemblyScheduler.test.ts +178 -0
- package/src/__tests__/RemoteBrowserActionResolver.ownership.test.ts +98 -0
- package/src/__tests__/ResolverBase.adhocReadScope.test.ts +133 -2
- 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 +243 -7
- package/src/__tests__/ResolverBase.deleteReturnProjection.test.ts +52 -0
- package/src/__tests__/ResolverBase.entityMessageRelay.test.ts +176 -0
- package/src/__tests__/ResolverBase.filterEscaping.test.ts +5 -2
- package/src/__tests__/ResolverBase.includeBinaryFields.test.ts +23 -1
- package/src/__tests__/ResolverBase.publishStatusUpdate.test.ts +68 -0
- package/src/__tests__/ResolverBase.userViewCacheAndDedup.test.ts +130 -0
- package/src/__tests__/RunAIAgentResolver.branch.test.ts +69 -13
- package/src/__tests__/RunAIAgentResolver.conversationWriter.test.ts +210 -0
- package/src/__tests__/RunAIAgentResolver.fireAndForgetCompletion.test.ts +209 -0
- package/src/__tests__/RunAIAgentResolver.historyFrom.test.ts +20 -28
- package/src/__tests__/RunAIAgentResolver.runDataGuard.test.ts +264 -0
- package/src/__tests__/RunAIAgentResolver.serverReply.test.ts +358 -0
- package/src/__tests__/RunAIAgentResolver.stoppedPredecessor.test.ts +4 -1
- package/src/__tests__/RunTemplateResolver.test.ts +35 -2
- package/src/__tests__/RunViewResolver.cacheCheckForwarding.test.ts +11 -0
- package/src/__tests__/RunViewResolver.cacheCheckScreen.test.ts +165 -0
- package/src/__tests__/SearchKnowledgeStreamResolver.test.ts +161 -0
- package/src/__tests__/SessionJanitor.test.ts +365 -10
- package/src/__tests__/SessionManager.test.ts +122 -0
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +25 -0
- package/src/__tests__/TaskGraphAgentRunner.test.ts +266 -0
- package/src/__tests__/TaskGraphContinuationDeliverer.test.ts +468 -23
- package/src/__tests__/TaskGraphFrameResolver.test.ts +22 -0
- package/src/__tests__/TestQuerySQLResolver.test.ts +274 -0
- package/src/__tests__/TransactionGroupResolver.fieldSecurity.test.ts +199 -0
- package/src/__tests__/TransactionGroupResolver.refusals.test.ts +98 -1
- package/src/__tests__/UpdateMJDataContextItem.oldValues.test.ts +220 -0
- package/src/__tests__/UserViewResolver.updateWhereClause.test.ts +15 -0
- package/src/__tests__/ViewOperationsHandler.clauseScreen.test.ts +157 -0
- package/src/__tests__/ViewOperationsHandler.test.ts +7 -2
- package/src/__tests__/WidgetSessionService.test.ts +583 -4
- package/src/__tests__/agent-run-data-guard.test.ts +130 -0
- package/src/__tests__/apiRootBoot.test.ts +202 -0
- package/src/__tests__/buildMagicLinkSessionUser.test.ts +96 -0
- package/src/__tests__/cacheBroadcastProviders.test.ts +403 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +109 -0
- package/src/__tests__/conversationRunEvents.test.ts +56 -14
- package/src/__tests__/fieldSecurityFixtures.ts +91 -0
- package/src/__tests__/globalCors.test.ts +217 -0
- package/src/__tests__/localDevAuth.test.ts +196 -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__/orphanedConversationDetailReconciler.test.ts +10 -0
- package/src/__tests__/ownedRealtimeSession.test.ts +39 -0
- package/src/__tests__/pushStatusResolver.filter.test.ts +64 -1
- package/src/__tests__/realtimeRecordingHandler.test.ts +669 -0
- package/src/__tests__/realtimeSessions/RealtimeSessionEventsResolver.test.ts +29 -1
- package/src/__tests__/realtimeSessions/ownedRealtimeSession.anonymous.test.ts +31 -0
- package/src/__tests__/realtimeSessions/ownedRealtimeSession.namedScoped.test.ts +37 -0
- package/src/__tests__/realtimeSessions/sessionAccess.test.ts +146 -7
- package/src/__tests__/realtimeSessions/sessionConfigStamps.test.ts +1 -1
- package/src/__tests__/realtimeSessions/verificationWorkflow.test.ts +25 -4
- package/src/__tests__/resolverBase.cloudEventChangedFields.test.ts +157 -0
- package/src/__tests__/resolverBase.fieldSecurityTransport.test.ts +222 -0
- package/src/__tests__/resolverBase.fls.test.ts +146 -97
- package/src/__tests__/restTransport.fieldSecurity.test.ts +148 -0
- package/src/__tests__/runParamsTimeZone.test.ts +179 -0
- package/src/__tests__/runStreams.fieldSecurity.test.ts +100 -0
- package/src/__tests__/runViewProperties.test.ts +114 -0
- package/src/__tests__/scalabilityBenchmarks.test.ts +765 -0
- package/src/__tests__/scalabilityDatabaseIntegration.test.ts +207 -0
- package/src/__tests__/serverExtensionHostOverlay.test.ts +66 -0
- package/src/__tests__/serverExtensionReservedRoots.test.ts +11 -98
- package/src/__tests__/sessionUserClone.test.ts +5 -1
- package/src/__tests__/sharedCache.test.ts +5 -3
- package/src/__tests__/startupSweepOrder.test.ts +40 -0
- package/src/__tests__/unifiedAuth.test.ts +28 -0
- package/src/__tests__/widgetGuestElevation.test.ts +2 -0
- package/src/agentSessions/SessionJanitor.ts +369 -93
- package/src/agentSessions/SessionManager.ts +45 -10
- package/src/apiRoot.ts +78 -0
- package/src/auth/AuthProviderCatalogRouter.ts +29 -0
- package/src/auth/LoginScreenEngine.ts +50 -0
- package/src/auth/index.ts +57 -12
- package/src/auth/initializeProviders.ts +43 -1
- package/src/auth/localDevAuth.ts +528 -0
- package/src/auth/magicLink/MagicLinkKeys.ts +2 -2
- package/src/auth/magicLink/MagicLinkRouter.ts +3 -2
- package/src/auth/magicLink/types.ts +10 -0
- package/src/auth/sessionUserClone.ts +3 -2
- package/src/broadcast/BaseCacheInvalidationBroadcastProvider.ts +674 -0
- package/src/broadcast/index.ts +5 -0
- package/src/broadcast/providers/AWSIoTCoreBroadcastProvider.ts +180 -0
- package/src/broadcast/providers/AzureWebPubSubBroadcastProvider.ts +182 -0
- package/src/broadcast/providers/GCPPubSubBroadcastProvider.ts +169 -0
- package/src/broadcast/providers/SequencedRoleBucketBroadcastProvider.ts +57 -0
- package/src/cache.ts +2 -1
- package/src/config.ts +115 -5
- package/src/context.ts +32 -8
- package/src/coreRoutes.ts +181 -0
- package/src/generated/graphql-schemas/__mj.ts +3684 -284
- package/src/generic/CacheInvalidationResolver.ts +172 -60
- package/src/generic/OrphanedConversationDetailReconciler.ts +4 -2
- package/src/generic/PubSubManager.ts +54 -1
- package/src/generic/PushStatusResolver.ts +109 -12
- package/src/generic/ResolverBase.ts +540 -431
- package/src/generic/RunViewResolver.ts +97 -15
- package/src/generic/actionOutputFieldSecurity.ts +75 -0
- package/src/generic/fieldSecurityTransport.ts +62 -0
- package/src/globalCors.ts +71 -0
- package/src/index.ts +239 -238
- package/src/integration/CustomColumnPromoter.ts +6 -1
- package/src/logging/variablesLoggingMiddleware.ts +59 -3
- package/src/multiTenancy/index.ts +24 -2
- package/src/realtimeSessions/README.md +7 -3
- package/src/realtimeSessions/RealtimeSessionVerificationService.ts +2 -1
- package/src/realtimeSessions/RealtimeVerifyRouter.ts +2 -1
- package/src/realtimeSessions/RecordingAssemblyScheduler.ts +234 -0
- package/src/realtimeSessions/SessionVerificationPortsBase.ts +1 -0
- package/src/realtimeSessions/index.ts +2 -0
- package/src/realtimeSessions/ownedRealtimeSession.ts +69 -0
- package/src/realtimeSessions/sessionAccess.ts +183 -23
- package/src/realtimeSessions/sessionConfigStamps.ts +1 -1
- package/src/realtimeSessions/subscriptionAuthorization.ts +4 -2
- package/src/realtimeSessions/verificationWorkflow.ts +3 -1
- package/src/realtimeWidget/WidgetRouter.ts +22 -4
- package/src/realtimeWidget/WidgetSessionService.ts +252 -17
- package/src/realtimeWidget/host-identity.ts +1 -1
- package/src/realtimeWidget/widgetCore.ts +30 -1
- package/src/realtimeWidget/widgetGuestElevation.ts +2 -1
- package/src/resolvers/ActionResolver.ts +78 -15
- package/src/resolvers/AdhocQueryResolver.ts +1 -0
- package/src/resolvers/AgentRunStatusPublisher.ts +36 -8
- package/src/resolvers/AgentSessionResolver.ts +46 -18
- package/src/resolvers/ArtifactFileResolver.ts +9 -8
- package/src/resolvers/ConversationRunEventsResolver.ts +15 -11
- package/src/resolvers/DatasetResolver.ts +70 -10
- package/src/resolvers/EntityCommunicationsResolver.ts +56 -15
- package/src/resolvers/EntityRecordNameResolver.ts +3 -2
- package/src/resolvers/FetchEntityVectorsResolver.ts +248 -55
- package/src/resolvers/FileCategoryResolver.ts +1 -0
- package/src/resolvers/FileResolver.ts +334 -109
- package/src/resolvers/GetDataContextDataResolver.ts +139 -41
- package/src/resolvers/GetDataResolver.ts +6 -3
- package/src/resolvers/IntegrationDiscoveryResolver.ts +53 -9
- package/src/resolvers/MergeRecordsResolver.ts +23 -2
- package/src/resolvers/PipelineProgressResolver.ts +12 -3
- package/src/resolvers/QueryResolver.ts +31 -6
- package/src/resolvers/QuerySystemUserResolver.ts +15 -6
- package/src/resolvers/RSUResolver.ts +12 -0
- package/src/resolvers/RealtimeClientSessionResolver.ts +114 -66
- package/src/resolvers/RemoteBrowserActionResolver.ts +15 -10
- package/src/resolvers/RunAIAgentResolver.ts +296 -85
- package/src/resolvers/RunTemplateResolver.ts +6 -4
- package/src/resolvers/RunTestResolver.ts +3 -1
- package/src/resolvers/SearchKnowledgeStreamResolver.ts +19 -3
- package/src/resolvers/SqlLoggingConfigResolver.ts +2 -4
- package/src/resolvers/SyncDataResolver.ts +4 -0
- package/src/resolvers/TaskGraphFrameResolver.ts +33 -1
- package/src/resolvers/TestQuerySQLResolver.ts +69 -13
- package/src/resolvers/TransactionGroupResolver.ts +185 -30
- package/src/resolvers/UserViewResolver.ts +2 -1
- package/src/resolvers/__tests__/GetDataResolver.validation.test.ts +95 -0
- package/src/resolvers/__tests__/MergeRecordsResolver.test.ts +43 -0
- package/src/resolvers/agent-run-data-guard.ts +89 -0
- package/src/rest/EntityCRUDHandler.ts +22 -5
- package/src/rest/RESTEndpointHandler.ts +8 -5
- package/src/rest/RealtimeRecordingHandler.ts +467 -0
- package/src/rest/ViewOperationsHandler.ts +52 -18
- package/src/rest/setupRESTEndpoints.ts +6 -0
- package/src/serverExtensionReservedRoots.ts +14 -17
- package/src/serverRoleConfigUnits.ts +48 -0
- 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 +738 -16
- package/src/sharedCache.ts +3 -0
- package/src/types.ts +23 -1
|
@@ -10,18 +10,18 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
11
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
12
|
};
|
|
13
|
-
import { EntityPermissionType, FieldValueCollection, LogError } from '@memberjunction/core';
|
|
13
|
+
import { AssertClientInputWritableForUser, EntityPermissionType, FieldValueCollection, LogError } from '@memberjunction/core';
|
|
14
14
|
import { NormalizeUUID } from '@memberjunction/global';
|
|
15
|
-
import { readRealtimeRecordingFile } from '@memberjunction/ai-agents';
|
|
16
15
|
import { Arg, Ctx, DeleteOptionsInput, Field, FieldResolver, Float, InputType, Int, Mutation, ObjectType, PubSub, PubSubEngine, Query, Resolver, Root, } from '@memberjunction/server';
|
|
17
|
-
import { createDownloadUrl, createUploadUrl, deleteObject, moveObject, copyObject, listObjects, copyObjectBetweenProviders, searchAcrossAccounts, FileStorageEngine, } from '@memberjunction/storage';
|
|
16
|
+
import { createDownloadUrl, createUploadUrl, deleteObject, moveObject, copyObject, listObjects, copyObjectBetweenProviders, searchAcrossAccounts, FileStorageEngine, StorageAccessEvaluator, StorageAccountAccessDeniedError, StorageObjectKeyOf, } from '@memberjunction/storage';
|
|
18
17
|
import { CreateMJFileInput, MJFileResolver as FileResolverBase, MJFile_, UpdateMJFileInput } from '../generated/generated.js';
|
|
19
18
|
import { FieldMapper } from '@memberjunction/graphql-dataprovider';
|
|
20
19
|
import { GetReadOnlyProvider } from '../util.js';
|
|
21
|
-
import {
|
|
20
|
+
import { ProjectEntityObjectForCaller } from '../generic/fieldSecurityTransport.js';
|
|
22
21
|
import { MediaAccessKeyManager } from '../rest/MediaAccessKeys.js';
|
|
23
22
|
import { UploadTokenManager } from '../rest/UploadTokenManager.js';
|
|
24
23
|
import { DeriveSidecarPath, ParsePeaksSidecar } from './peaksSidecar.js';
|
|
24
|
+
import { ApiRoute } from '../apiRoot.js';
|
|
25
25
|
let CreateUploadURLInput = class CreateUploadURLInput {
|
|
26
26
|
};
|
|
27
27
|
__decorate([
|
|
@@ -576,16 +576,9 @@ MediaAccessTokenResult = __decorate([
|
|
|
576
576
|
ObjectType()
|
|
577
577
|
], MediaAccessTokenResult);
|
|
578
578
|
export { MediaAccessTokenResult };
|
|
579
|
+
/** The failure reported for an account `SearchAcrossAccounts` refuses — the same whether it is restricted or does not exist. */
|
|
580
|
+
const SEARCH_ACCOUNT_REFUSED_MESSAGE = 'Access denied: you do not have access to this storage account or it does not exist.';
|
|
579
581
|
let FileResolver = class FileResolver extends FileResolverBase {
|
|
580
|
-
/**
|
|
581
|
-
* Resolves the server's public base URL the same way callbacks/magic-link do:
|
|
582
|
-
* `publicUrl` if configured (e.g. an ngrok URL), else `baseUrl:graphqlPort`. Trailing
|
|
583
|
-
* slash stripped so callers can append `/media/...`.
|
|
584
|
-
*/
|
|
585
|
-
resolvePublicBaseUrl() {
|
|
586
|
-
const base = configInfo.publicUrl || `${configInfo.baseUrl}:${configInfo.graphqlPort}${configInfo.graphqlRootPath || ''}`;
|
|
587
|
-
return base.replace(/\/+$/, '');
|
|
588
|
-
}
|
|
589
582
|
/**
|
|
590
583
|
* Mints a short-lived signed media-access token for an `MJ: Files` record and returns the
|
|
591
584
|
* authenticated streaming URL (`<publicBase>/media/<fileId>?token=<token>`). Permission-gated
|
|
@@ -607,14 +600,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
607
600
|
if (!loaded) {
|
|
608
601
|
return { Success: false, ErrorMessage: 'You do not have access to this file or it does not exist.' };
|
|
609
602
|
}
|
|
603
|
+
// Storage gate: the token is the capability the /media route honours, so it is minted only when the caller may read
|
|
604
|
+
// the account the row's provider resolves to and every row tracking the object (throws — caught below).
|
|
605
|
+
const resolved = await this.assertFileObjectAccess(fileEntity, 'Read', contextUser, provider);
|
|
610
606
|
// Access authorized — mint the capability token.
|
|
611
607
|
const { Token, ExpiresAt } = MediaAccessKeyManager.Instance.Sign(fileId, contextUser.ID);
|
|
612
608
|
const fileNameSuffix = fileEntity.Name ? `/${encodeURIComponent(fileEntity.Name)}` : '';
|
|
613
|
-
const url =
|
|
609
|
+
const url = ApiRoute(`/media/${encodeURIComponent(fileId)}${fileNameSuffix}?token=${encodeURIComponent(Token)}`, { absolute: true });
|
|
614
610
|
// Best-effort: surface precomputed waveform peaks from a peaks.json sidecar beside the file, so
|
|
615
611
|
// the player renders the real waveform instantly without fetching/decoding the audio. Never
|
|
616
612
|
// blocks token minting — any failure just omits Peaks.
|
|
617
|
-
const peaks = await this.tryReadPeaksSidecar(fileEntity,
|
|
613
|
+
const peaks = await this.tryReadPeaksSidecar(fileEntity, resolved?.Driver);
|
|
618
614
|
return { Success: true, Token, Url: url, ExpiresAt, MimeType: fileEntity.ContentType ?? undefined, Peaks: peaks };
|
|
619
615
|
}
|
|
620
616
|
catch (error) {
|
|
@@ -627,33 +623,22 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
627
623
|
* Best-effort read of a `peaks.json` waveform sidecar that sits in the SAME storage folder as the
|
|
628
624
|
* given file (a JSON array of normalized `0..1` numbers, written at capture time). Derives the
|
|
629
625
|
* folder from the file's `ProviderKey` (strips the final path segment), reads `<folder>/peaks.json`
|
|
630
|
-
*
|
|
631
|
-
* `undefined` on ANY failure (no ProviderKey, no
|
|
632
|
-
* peaks as a pure optimization and must never let a
|
|
626
|
+
* through the file's own storage driver — the one {@link assertFileObjectAccess} resolved, so the account is already
|
|
627
|
+
* gated — parses + validates it, and returns sanitized peaks. Returns `undefined` on ANY failure (no ProviderKey, no
|
|
628
|
+
* driver, no sidecar, parse error, garbage) — the caller treats peaks as a pure optimization and must never let a
|
|
629
|
+
* sidecar problem affect token minting.
|
|
633
630
|
*
|
|
634
631
|
* @param file The already-loaded (under the user's context) `MJ: Files` row.
|
|
635
|
-
* @param
|
|
632
|
+
* @param driver The gated driver for the file's account, or undefined when the provider has none.
|
|
636
633
|
* @returns Sanitized `0..1` peaks (length-capped), or `undefined`.
|
|
637
634
|
*/
|
|
638
|
-
async tryReadPeaksSidecar(file,
|
|
635
|
+
async tryReadPeaksSidecar(file, driver) {
|
|
639
636
|
try {
|
|
640
637
|
// The sidecar lives next to the recording: replace the final path segment with peaks.json.
|
|
641
638
|
const sidecarPath = DeriveSidecarPath(file.ProviderKey);
|
|
642
|
-
if (!sidecarPath) {
|
|
643
|
-
return undefined;
|
|
644
|
-
}
|
|
645
|
-
// Resolve the file's storage account → driver (mirror of readRealtimeRecordingFile's pattern).
|
|
646
|
-
await FileStorageEngine.Instance.Config(false, contextUser);
|
|
647
|
-
let accounts = FileStorageEngine.Instance.GetAccountsByProviderID(file.ProviderID);
|
|
648
|
-
if (accounts.length === 0) {
|
|
649
|
-
await FileStorageEngine.Instance.Config(true, contextUser);
|
|
650
|
-
accounts = FileStorageEngine.Instance.GetAccountsByProviderID(file.ProviderID);
|
|
651
|
-
}
|
|
652
|
-
const account = accounts[0];
|
|
653
|
-
if (!account) {
|
|
639
|
+
if (!sidecarPath || !driver) {
|
|
654
640
|
return undefined;
|
|
655
641
|
}
|
|
656
|
-
const driver = await FileStorageEngine.Instance.GetDriver(account.ID, contextUser);
|
|
657
642
|
const bytes = await driver.GetObject({ fullPath: sidecarPath });
|
|
658
643
|
if (!bytes || bytes.length === 0) {
|
|
659
644
|
return undefined;
|
|
@@ -689,6 +674,65 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
689
674
|
accountEntity,
|
|
690
675
|
};
|
|
691
676
|
}
|
|
677
|
+
/**
|
|
678
|
+
* The storage-account gate. Every route keyed by a client-supplied account ID runs it FIRST — before the account is
|
|
679
|
+
* loaded and before any driver call. Throws `StorageAccountAccessDeniedError` ("You do not have access to this storage
|
|
680
|
+
* account or it does not exist.") unless the caller may use the account for `access`, evaluated per call against
|
|
681
|
+
* `MJ: File Storage Account Permissions` by `StorageAccessEvaluator` (`@memberjunction/storage`). An unknown account
|
|
682
|
+
* gets the same refusal, so the error never reveals whether an account ID is real.
|
|
683
|
+
*/
|
|
684
|
+
async assertAccountAccess(accountId, access, context) {
|
|
685
|
+
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
686
|
+
const user = this.GetUserFromPayload(context.userPayload);
|
|
687
|
+
await StorageAccessEvaluator.Instance.AssertAccountAccess(accountId, user, access, md);
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* The tracked-file rule for an account-keyed route: when any of `objectNames` in the account backs an `MJ: Files` row,
|
|
691
|
+
* the caller must be able to read that row — the rule {@link GetFileContents} applies to a file ID — else this throws
|
|
692
|
+
* `TrackedFileAccessDeniedError` ("You do not have access to this file or it does not exist."). Keys are compared the
|
|
693
|
+
* way the account's driver addresses them (`FileStorageBase.NormalizeObjectKey`) and case-insensitively; a key whose
|
|
694
|
+
* meaning depends on the provider (`..`, `\`, `%2F`, ...) is refused outright. Applied to every object a route reads
|
|
695
|
+
* or signs, and to every object it overwrites, moves or deletes — the minimum for a write is that the caller may read
|
|
696
|
+
* what it replaces. An object no row tracks is governed by the account gate, which the caller has already passed.
|
|
697
|
+
*/
|
|
698
|
+
async assertTrackedObjectReadable(accountId, objectNames, user, md) {
|
|
699
|
+
const providerID = await this.accountProviderID(accountId, user, md);
|
|
700
|
+
const driver = await FileStorageEngine.Instance.GetDriver(accountId, user);
|
|
701
|
+
await StorageAccessEvaluator.Instance.AssertTrackedObjectsReadable(providerID, objectNames, user, md, driver);
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* The storage gate for a route that goes from an `MJ: Files` row (already loaded as the caller) to its object: the
|
|
705
|
+
* account the row's provider resolves to must allow `access`, and every row tracking the object must be readable by
|
|
706
|
+
* the caller (`FileStorageEngine.ResolveFileObject`) — so a row whose client-written `ProviderID`/`ProviderKey`/`Name`
|
|
707
|
+
* aliases someone else's object is refused. `extraKeys` (a rename target) get the tracked-file rule too. When the
|
|
708
|
+
* provider has no storage account the legacy env-configured driver serves the row; there is no account to gate, so the
|
|
709
|
+
* tracked-file rule alone applies. Returns the resolved object, or `null` in that legacy case.
|
|
710
|
+
*/
|
|
711
|
+
async assertFileObjectAccess(file, access, user, md, extraKeys = []) {
|
|
712
|
+
const location = { ProviderID: file.ProviderID, ProviderKey: file.ProviderKey ?? null, Name: file.Name };
|
|
713
|
+
const resolved = await FileStorageEngine.Instance.ResolveFileObject(location, user, access, md);
|
|
714
|
+
const keys = resolved ? extraKeys : [StorageObjectKeyOf(location), ...extraKeys];
|
|
715
|
+
if (keys.length > 0) {
|
|
716
|
+
await StorageAccessEvaluator.Instance.AssertTrackedObjectsReadable(file.ProviderID, keys, user, md, resolved?.Driver);
|
|
717
|
+
}
|
|
718
|
+
return resolved;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* The storage provider ID of an account the caller has already passed the gate for, from the engine's cache (refreshed
|
|
722
|
+
* once on a miss). Refuses like the gate when the account still cannot be found.
|
|
723
|
+
*/
|
|
724
|
+
async accountProviderID(accountId, user, md) {
|
|
725
|
+
await FileStorageEngine.Instance.Config(false, user, md);
|
|
726
|
+
let account = FileStorageEngine.Instance.GetAccountById(accountId);
|
|
727
|
+
if (!account) {
|
|
728
|
+
await FileStorageEngine.Instance.RefreshAfterMiss(md);
|
|
729
|
+
account = FileStorageEngine.Instance.GetAccountById(accountId);
|
|
730
|
+
}
|
|
731
|
+
if (!account) {
|
|
732
|
+
throw new StorageAccountAccessDeniedError();
|
|
733
|
+
}
|
|
734
|
+
return account.ProviderID;
|
|
735
|
+
}
|
|
692
736
|
/**
|
|
693
737
|
* Loads a FileStorageAccount and its associated FileStorageProvider.
|
|
694
738
|
* This is the standard way to get provider information in the enterprise model.
|
|
@@ -729,6 +773,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
729
773
|
const fileEntity = await provider.GetEntityObject('MJ: Files', user);
|
|
730
774
|
const providerEntity = await provider.GetEntityObject('MJ: File Storage Providers', user);
|
|
731
775
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
776
|
+
// Field-level security: refuse a key the caller may not read, or that names no field, before anything else
|
|
777
|
+
AssertClientInputWritableForUser(fileEntity.EntityInfo, { ...input }, user);
|
|
778
|
+
// The upload URL signs an overwrite of `Name` (and the row will point at `ProviderKey`): Write on the account the
|
|
779
|
+
// provider resolves to, and the tracked-file rule on both keys, as for every other route that writes an object.
|
|
780
|
+
const location = { ProviderID: input.ProviderID ?? '', ProviderKey: null, Name: input.Name ?? '' };
|
|
781
|
+
await this.assertFileObjectAccess(location, 'Write', user, provider, input.ProviderKey ? [input.ProviderKey] : []);
|
|
732
782
|
const [sameName] = await this.findBy(provider, 'MJ: Files', { Name: input.Name, ProviderID: input.ProviderID }, context.userPayload.userRecord);
|
|
733
783
|
const NameExists = Boolean(sameName);
|
|
734
784
|
const success = fileEntity.NewRecord(FieldValueCollection.FromObject({ ...input, Status: 'Pending' }));
|
|
@@ -746,7 +796,7 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
746
796
|
if (!saved) {
|
|
747
797
|
console.error('[CreateFile] File save failed:', fileEntity.LatestResult?.CompleteMessage);
|
|
748
798
|
}
|
|
749
|
-
const File =
|
|
799
|
+
const File = this.fileResponse(fileEntity, user, provider);
|
|
750
800
|
return { File, UploadUrl, NameExists };
|
|
751
801
|
}
|
|
752
802
|
/**
|
|
@@ -761,6 +811,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
761
811
|
// Permission check on MJ: Files
|
|
762
812
|
const fileEntity = await provider.GetEntityObject('MJ: Files', user);
|
|
763
813
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
814
|
+
// Write gate on the account the upload lands in — before the staged upload is consumed or any driver is touched.
|
|
815
|
+
// With no account at all there is nothing to gate; UploadFile then refuses with its documented error.
|
|
816
|
+
const targetAccountId = await this.resolveUploadAccountId(input.AccountID, user, provider);
|
|
817
|
+
if (targetAccountId) {
|
|
818
|
+
await this.assertAccountAccess(targetAccountId, 'Write', context);
|
|
819
|
+
}
|
|
764
820
|
let buffer;
|
|
765
821
|
let fileName = input.FileName || 'file';
|
|
766
822
|
let mimeType = input.MimeType || 'application/octet-stream';
|
|
@@ -785,25 +841,19 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
785
841
|
ErrorMessage: 'Either UploadToken or Base64Data must be provided.',
|
|
786
842
|
};
|
|
787
843
|
}
|
|
788
|
-
// Ensure FileStorageEngine is configured under the user context
|
|
789
|
-
await FileStorageEngine.Instance.Config(false, user, provider);
|
|
790
|
-
if (input.AccountID && !FileStorageEngine.Instance.GetAccountById(input.AccountID)) {
|
|
791
|
-
await FileStorageEngine.Instance.Config(true, user, provider);
|
|
792
|
-
}
|
|
793
844
|
const uploadResult = await FileStorageEngine.Instance.UploadFile({
|
|
794
845
|
content: buffer,
|
|
795
846
|
fileName,
|
|
796
847
|
mimeType,
|
|
797
848
|
contextUser: user,
|
|
798
|
-
storageAccountId:
|
|
849
|
+
storageAccountId: targetAccountId,
|
|
799
850
|
categoryID: input.CategoryID,
|
|
800
851
|
description: input.Description,
|
|
801
852
|
pathPrefix: input.PathPrefix,
|
|
802
853
|
provider,
|
|
803
854
|
});
|
|
804
855
|
const loaded = await fileEntity.Load(uploadResult.FileID);
|
|
805
|
-
const
|
|
806
|
-
const File = loaded ? mapper.MapFields({ ...fileEntity.GetAll() }) : undefined;
|
|
856
|
+
const File = loaded ? this.fileResponse(fileEntity, user, provider) : undefined;
|
|
807
857
|
return {
|
|
808
858
|
Success: true,
|
|
809
859
|
FileID: uploadResult.FileID,
|
|
@@ -819,32 +869,49 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
819
869
|
};
|
|
820
870
|
}
|
|
821
871
|
}
|
|
872
|
+
/**
|
|
873
|
+
* The file record for a response: its field values as the caller may read them, in GraphQL field
|
|
874
|
+
* names. The record was just saved or reloaded, and a save re-reads the whole row.
|
|
875
|
+
*/
|
|
876
|
+
fileResponse(fileEntity, user, provider) {
|
|
877
|
+
return new FieldMapper().MapFields({ ...ProjectEntityObjectForCaller(fileEntity, user, provider) });
|
|
878
|
+
}
|
|
879
|
+
/**
|
|
880
|
+
* The account an upload will land in: the requested one, else the engine's default (the first active account) —
|
|
881
|
+
* returned as an explicit ID so the account the gate checks is the account `UploadFile` uses. A requested ID is
|
|
882
|
+
* returned as-is: the gate refuses an unknown one without revealing that it does not exist, and `UploadFile` refreshes
|
|
883
|
+
* the engine's cache itself when a permitted account is missing from it.
|
|
884
|
+
*/
|
|
885
|
+
async resolveUploadAccountId(accountId, user, md) {
|
|
886
|
+
if (accountId) {
|
|
887
|
+
return accountId;
|
|
888
|
+
}
|
|
889
|
+
await FileStorageEngine.Instance.Config(false, user, md);
|
|
890
|
+
return FileStorageEngine.Instance.ResolveStorageAccount()?.account.ID;
|
|
891
|
+
}
|
|
822
892
|
async DownloadUrl(file, context) {
|
|
823
893
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
824
894
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
825
895
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
826
896
|
fileEntity.CheckPermissions(EntityPermissionType.Read, true);
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
}
|
|
833
|
-
if (accounts.length > 0) {
|
|
834
|
-
const account = accounts[0];
|
|
835
|
-
const driver = await FileStorageEngine.Instance.GetDriver(account.ID, user);
|
|
836
|
-
return await driver.CreatePreAuthDownloadUrl(file.ProviderKey ?? file.Name);
|
|
897
|
+
// Storage gate: Read on the account the row's provider resolves to, and the tracked-file rule on its object
|
|
898
|
+
const location = { ProviderID: file.ProviderID, ProviderKey: file.ProviderKey ?? null, Name: file.Name ?? '' };
|
|
899
|
+
const resolved = await this.assertFileObjectAccess(location, 'Read', user, md);
|
|
900
|
+
if (resolved) {
|
|
901
|
+
return await resolved.Driver.CreatePreAuthDownloadUrl(resolved.ObjectKey);
|
|
837
902
|
}
|
|
838
903
|
const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', user);
|
|
839
904
|
await providerEntity.Load(file.ProviderID);
|
|
840
905
|
const userContext = this.buildUserContext(context);
|
|
841
|
-
const url = await createDownloadUrl(providerEntity, file
|
|
906
|
+
const url = await createDownloadUrl(providerEntity, StorageObjectKeyOf(file), userContext);
|
|
842
907
|
return url;
|
|
843
908
|
}
|
|
844
909
|
/**
|
|
845
910
|
* Returns an `MJ: Files` record's bytes as base64, read server-side through authenticated MJStorage
|
|
846
911
|
* (`GetObject`) — NOT a public pre-signed link. Permission-gated: the file is first loaded under the
|
|
847
|
-
* calling user's context, so MJ row-level security determines access
|
|
912
|
+
* calling user's context, so MJ row-level security determines access; then the storage gate
|
|
913
|
+
* ({@link assertFileObjectAccess}) requires Read on the account the row's provider resolves to and refuses an object
|
|
914
|
+
* another row tracks when the caller cannot read that row. Never throws to the client.
|
|
848
915
|
*
|
|
849
916
|
* @param fileId The `MJ: Files` id whose bytes to return.
|
|
850
917
|
* @returns `{ Success, Base64?, MimeType?, ErrorMessage? }`.
|
|
@@ -860,15 +927,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
860
927
|
if (!loaded) {
|
|
861
928
|
return { Success: false, ErrorMessage: 'You do not have access to this file or it does not exist.' };
|
|
862
929
|
}
|
|
863
|
-
//
|
|
864
|
-
|
|
865
|
-
|
|
930
|
+
// Storage gate, then read the bytes via authenticated MJStorage (server-side GetObject on the file's own account).
|
|
931
|
+
// A refusal throws and is reported below; a row whose provider has no account has no bytes to read.
|
|
932
|
+
const resolved = await this.assertFileObjectAccess(fileEntity, 'Read', contextUser, provider);
|
|
933
|
+
const bytes = resolved ? await resolved.Driver.GetObject({ fullPath: resolved.ObjectKey }) : null;
|
|
934
|
+
if (!bytes || bytes.length === 0) {
|
|
866
935
|
return { Success: false, ErrorMessage: 'The file could not be read from storage.' };
|
|
867
936
|
}
|
|
868
937
|
return {
|
|
869
938
|
Success: true,
|
|
870
|
-
Base64:
|
|
871
|
-
MimeType:
|
|
939
|
+
Base64: bytes.toString('base64'),
|
|
940
|
+
MimeType: fileEntity.ContentType ?? undefined,
|
|
872
941
|
};
|
|
873
942
|
}
|
|
874
943
|
catch (error) {
|
|
@@ -885,6 +954,8 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
885
954
|
fileEntity.CheckPermissions(EntityPermissionType.Update, true);
|
|
886
955
|
await fileEntity.Load(input.ID);
|
|
887
956
|
if (fileEntity.Name !== input.Name) {
|
|
957
|
+
// Renaming moves the object: Write on its account, and the tracked-file rule on the object and the new name
|
|
958
|
+
await this.assertFileObjectAccess(fileEntity, 'Write', user, md, [fileEntity.Name, input.Name]);
|
|
888
959
|
const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', user);
|
|
889
960
|
await providerEntity.Load(fileEntity.ProviderID);
|
|
890
961
|
const userContext = this.buildUserContext(context);
|
|
@@ -905,12 +976,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
905
976
|
return null;
|
|
906
977
|
}
|
|
907
978
|
fileEntity.CheckPermissions(EntityPermissionType.Delete, true);
|
|
908
|
-
// Only delete the object from the provider if it's actually been uploaded
|
|
979
|
+
// Only delete the object from the provider if it's actually been uploaded — after the storage gate: Write on its
|
|
980
|
+
// account, and no other row the caller cannot read may track the object (a row can alias another row's object)
|
|
909
981
|
if (fileEntity.Status === 'Uploaded') {
|
|
982
|
+
await this.assertFileObjectAccess(fileEntity, 'Write', userInfo, md);
|
|
910
983
|
const providerEntity = await md.GetEntityObject('MJ: File Storage Providers', userInfo);
|
|
911
984
|
await providerEntity.Load(fileEntity.ProviderID);
|
|
912
985
|
const userContext = this.buildUserContext(context);
|
|
913
|
-
await deleteObject(providerEntity, fileEntity
|
|
986
|
+
await deleteObject(providerEntity, StorageObjectKeyOf(fileEntity), userContext);
|
|
914
987
|
}
|
|
915
988
|
return super.DeleteMJFile(ID, options, context, pubSub);
|
|
916
989
|
}
|
|
@@ -922,12 +995,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
922
995
|
});
|
|
923
996
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
924
997
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
998
|
+
// Account gate first — before the account is loaded or any driver is touched
|
|
999
|
+
await this.assertAccountAccess(input.AccountID, 'Read', context);
|
|
925
1000
|
// Load the account and its provider
|
|
926
1001
|
const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
|
|
927
1002
|
console.log('[FileResolver] Provider loaded:', {
|
|
928
1003
|
Name: providerEntity.Name,
|
|
929
1004
|
ServerDriverKey: providerEntity.ServerDriverKey,
|
|
930
|
-
HasConfiguration: !!providerEntity.
|
|
1005
|
+
HasConfiguration: !!providerEntity.Configuration,
|
|
931
1006
|
});
|
|
932
1007
|
// Check permissions - user must have read access to Files entity
|
|
933
1008
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
@@ -941,8 +1016,10 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
941
1016
|
objects: result.objects.map((o) => ({ name: o.name, isDirectory: o.isDirectory })),
|
|
942
1017
|
prefixes: result.prefixes,
|
|
943
1018
|
});
|
|
944
|
-
//
|
|
945
|
-
|
|
1019
|
+
// Drop objects behind an MJ: Files row the caller cannot read — a listing never names a tracked object the caller
|
|
1020
|
+
// is refused — then convert Date objects to ISO strings for GraphQL
|
|
1021
|
+
const visible = await this.dropUnreadableListedObjects(input.AccountID, result.objects, user, md);
|
|
1022
|
+
const objects = visible.map((obj) => ({
|
|
946
1023
|
...obj,
|
|
947
1024
|
lastModified: obj.lastModified.toISOString(),
|
|
948
1025
|
}));
|
|
@@ -957,6 +1034,9 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
957
1034
|
// Check permissions
|
|
958
1035
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
959
1036
|
fileEntity.CheckPermissions(EntityPermissionType.Read, true);
|
|
1037
|
+
// Account gate, then — when the object backs an MJ: Files row — that row must be readable by the caller
|
|
1038
|
+
await this.assertAccountAccess(input.AccountID, 'Read', context);
|
|
1039
|
+
await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
|
|
960
1040
|
await FileStorageEngine.Instance.Config(false, user, md);
|
|
961
1041
|
const driver = await FileStorageEngine.Instance.GetDriver(input.AccountID, user);
|
|
962
1042
|
const downloadUrl = await driver.CreatePreAuthDownloadUrl(input.ObjectName);
|
|
@@ -982,6 +1062,9 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
982
1062
|
// Check permissions
|
|
983
1063
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
984
1064
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
1065
|
+
await this.assertAccountAccess(input.AccountID, 'Write', context);
|
|
1066
|
+
// An upload URL can overwrite: the caller must be able to read any MJ: Files row that tracks the target
|
|
1067
|
+
await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
|
|
985
1068
|
await FileStorageEngine.Instance.Config(false, user, md);
|
|
986
1069
|
const driver = await FileStorageEngine.Instance.GetDriver(input.AccountID, user);
|
|
987
1070
|
if (!driver.SupportsPreAuthUpload) {
|
|
@@ -1008,7 +1091,7 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1008
1091
|
return {
|
|
1009
1092
|
Success: true,
|
|
1010
1093
|
Token: mint.Token,
|
|
1011
|
-
Url: '/media/upload-stage',
|
|
1094
|
+
Url: ApiRoute('/media/upload-stage'),
|
|
1012
1095
|
};
|
|
1013
1096
|
}
|
|
1014
1097
|
catch (err) {
|
|
@@ -1022,11 +1105,14 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1022
1105
|
async DeleteStorageObject(input, context) {
|
|
1023
1106
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
1024
1107
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
1108
|
+
// Account gate first — before the account is loaded or any driver is touched
|
|
1109
|
+
await this.assertAccountAccess(input.AccountID, 'Write', context);
|
|
1025
1110
|
// Load the account and its provider
|
|
1026
1111
|
const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
|
|
1027
1112
|
// Check permissions
|
|
1028
1113
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
1029
1114
|
fileEntity.CheckPermissions(EntityPermissionType.Delete, true);
|
|
1115
|
+
await this.assertTrackedObjectReadable(input.AccountID, [input.ObjectName], user, md);
|
|
1030
1116
|
// Delete the object with extended user context (includes account for credential lookup)
|
|
1031
1117
|
const userContext = this.buildExtendedUserContext(context, account);
|
|
1032
1118
|
const success = await deleteObject(providerEntity, input.ObjectName, userContext);
|
|
@@ -1035,11 +1121,15 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1035
1121
|
async MoveStorageObject(input, context) {
|
|
1036
1122
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
1037
1123
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
1124
|
+
// Account gate first — before the account is loaded or any driver is touched
|
|
1125
|
+
await this.assertAccountAccess(input.AccountID, 'Write', context);
|
|
1038
1126
|
// Load the account and its provider
|
|
1039
1127
|
const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
|
|
1040
1128
|
// Check permissions
|
|
1041
1129
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
1042
1130
|
fileEntity.CheckPermissions(EntityPermissionType.Update, true);
|
|
1131
|
+
// A move reads and removes the source and may overwrite the destination: both get the tracked-file rule
|
|
1132
|
+
await this.assertTrackedObjectReadable(input.AccountID, [input.OldName, input.NewName], user, md);
|
|
1043
1133
|
// Move the object with extended user context (includes account for credential lookup)
|
|
1044
1134
|
const userContext = this.buildExtendedUserContext(context, account);
|
|
1045
1135
|
const success = await moveObject(providerEntity, input.OldName, input.NewName, userContext);
|
|
@@ -1048,12 +1138,17 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1048
1138
|
async CopyStorageObject(input, context) {
|
|
1049
1139
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
1050
1140
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
1141
|
+
// Account gate first — a copy within one account writes to it — before the account is loaded or any driver is touched
|
|
1142
|
+
await this.assertAccountAccess(input.AccountID, 'Write', context);
|
|
1051
1143
|
// Load the account and its provider
|
|
1052
1144
|
const { account, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
|
|
1053
1145
|
// Check permissions - copying requires both read (source) and create (destination)
|
|
1054
1146
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
1055
1147
|
fileEntity.CheckPermissions(EntityPermissionType.Read, true);
|
|
1056
1148
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
1149
|
+
// A copy of a tracked object is an untracked one the caller could then download, and the destination may be
|
|
1150
|
+
// overwritten: both get the tracked-file rule
|
|
1151
|
+
await this.assertTrackedObjectReadable(input.AccountID, [input.SourceName, input.DestinationName], user, md);
|
|
1057
1152
|
// Copy the object with extended user context (includes account for credential lookup)
|
|
1058
1153
|
const userContext = this.buildExtendedUserContext(context, account);
|
|
1059
1154
|
const success = await copyObject(providerEntity, input.SourceName, input.DestinationName, userContext);
|
|
@@ -1062,6 +1157,8 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1062
1157
|
async CreateDirectory(input, context) {
|
|
1063
1158
|
const md = GetReadOnlyProvider(context.providers, { allowFallbackToReadWrite: true });
|
|
1064
1159
|
const user = this.GetUserFromPayload(context.userPayload);
|
|
1160
|
+
// Account gate first — before the account is loaded or any driver is touched
|
|
1161
|
+
await this.assertAccountAccess(input.AccountID, 'Write', context);
|
|
1065
1162
|
// Load the account and its provider
|
|
1066
1163
|
const { account: accountEntity, provider: providerEntity } = await this.loadAccountAndProvider(input.AccountID, context);
|
|
1067
1164
|
// Check permissions
|
|
@@ -1086,6 +1183,12 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1086
1183
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
1087
1184
|
fileEntity.CheckPermissions(EntityPermissionType.Read, true);
|
|
1088
1185
|
fileEntity.CheckPermissions(EntityPermissionType.Create, true);
|
|
1186
|
+
// Account gates first: read the source, write the destination — before either account is loaded or touched
|
|
1187
|
+
await this.assertAccountAccess(input.SourceAccountID, 'Read', context);
|
|
1188
|
+
await this.assertAccountAccess(input.DestinationAccountID, 'Write', context);
|
|
1189
|
+
// Tracked-file rule on both ends: the source (its copy would be untracked), the destination (it may be overwritten)
|
|
1190
|
+
await this.assertTrackedObjectReadable(input.SourceAccountID, [input.SourcePath], user, md);
|
|
1191
|
+
await this.assertTrackedObjectReadable(input.DestinationAccountID, [input.DestinationPath], user, md);
|
|
1089
1192
|
// Load the source account and its provider
|
|
1090
1193
|
const { account: sourceAccount, provider: sourceProviderEntity } = await this.loadAccountAndProvider(input.SourceAccountID, context);
|
|
1091
1194
|
// Load the destination account and its provider
|
|
@@ -1121,47 +1224,131 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1121
1224
|
// Check permissions - searching requires read access
|
|
1122
1225
|
const fileEntity = await md.GetEntityObject('MJ: Files', user);
|
|
1123
1226
|
fileEntity.CheckPermissions(EntityPermissionType.Read, true);
|
|
1124
|
-
//
|
|
1125
|
-
await
|
|
1126
|
-
const
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
const foundIDs = new Set(accountEntities.map((a) => NormalizeUUID(a.ID)));
|
|
1135
|
-
const missingIDs = input.AccountIDs.filter((id) => !foundIDs.has(NormalizeUUID(id)));
|
|
1136
|
-
console.warn(`[FileResolver] Accounts not found: ${missingIDs.join(', ')}`);
|
|
1137
|
-
}
|
|
1138
|
-
// Load providers from cached metadata
|
|
1139
|
-
const providerMap = new Map();
|
|
1140
|
-
for (const provider of FileStorageEngine.Instance.Providers) {
|
|
1141
|
-
providerMap.set(provider.ID, provider);
|
|
1142
|
-
}
|
|
1143
|
-
// Build account/provider pairs for the search
|
|
1144
|
-
const accountInputs = [];
|
|
1145
|
-
for (const account of accountEntities) {
|
|
1146
|
-
const provider = providerMap.get(account.ProviderID);
|
|
1147
|
-
if (provider) {
|
|
1148
|
-
accountInputs.push({ accountEntity: account, providerEntity: provider });
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
// Execute the search across all accounts with account-based credentials
|
|
1152
|
-
const result = await searchAcrossAccounts(accountInputs, input.Query, {
|
|
1153
|
-
maxResultsPerAccount: input.MaxResultsPerAccount,
|
|
1154
|
-
fileTypes: input.FileTypes,
|
|
1155
|
-
searchContent: input.SearchContent,
|
|
1156
|
-
contextUser: user,
|
|
1157
|
-
});
|
|
1227
|
+
// Account gate BEFORE any driver call: refused accounts are reported as failed and never searched
|
|
1228
|
+
const { searchable, refused } = await this.partitionSearchAccounts(input.AccountIDs, user, md);
|
|
1229
|
+
const result = searchable.length > 0
|
|
1230
|
+
? await searchAcrossAccounts(searchable, input.Query, {
|
|
1231
|
+
maxResultsPerAccount: input.MaxResultsPerAccount,
|
|
1232
|
+
fileTypes: input.FileTypes,
|
|
1233
|
+
searchContent: input.SearchContent,
|
|
1234
|
+
contextUser: user,
|
|
1235
|
+
})
|
|
1236
|
+
: { accountResults: [], totalResultsReturned: 0, successfulAccounts: 0, failedAccounts: 0 };
|
|
1158
1237
|
console.log('[FileResolver] SearchAcrossAccounts result:', {
|
|
1159
1238
|
totalResultsReturned: result.totalResultsReturned,
|
|
1160
1239
|
successfulAccounts: result.successfulAccounts,
|
|
1161
1240
|
failedAccounts: result.failedAccounts,
|
|
1241
|
+
refusedAccounts: refused.length,
|
|
1162
1242
|
});
|
|
1163
|
-
|
|
1164
|
-
|
|
1243
|
+
const kept = await this.dropUnreadableTrackedHits(result.accountResults, user, md);
|
|
1244
|
+
return this.toSearchAcrossAccountsPayload(this.inRequestOrder(input.AccountIDs, [...kept, ...refused]));
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Per-account results in the order the accounts were requested (each requested ID once), whether searched or refused —
|
|
1248
|
+
* so a refused account sits where the caller put it rather than after every searched one.
|
|
1249
|
+
*/
|
|
1250
|
+
inRequestOrder(requestedIDs, results) {
|
|
1251
|
+
const byID = new Map(results.map((r) => [NormalizeUUID(r.accountID), r]));
|
|
1252
|
+
const ordered = [];
|
|
1253
|
+
for (const id of requestedIDs) {
|
|
1254
|
+
const result = byID.get(NormalizeUUID(id));
|
|
1255
|
+
if (result) {
|
|
1256
|
+
ordered.push(result);
|
|
1257
|
+
byID.delete(NormalizeUUID(id));
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
return [...ordered, ...byID.values()];
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Splits the requested accounts into those to search and those refused, before any driver call. An account the
|
|
1264
|
+
* caller may not read is refused with {@link SEARCH_ACCOUNT_REFUSED_MESSAGE} — and so is one that does not exist,
|
|
1265
|
+
* identically, so the response never reveals which IDs are real. Readable accounts are paired with their provider
|
|
1266
|
+
* from the engine's cache (refreshed once if a readable account is missing from it). Duplicate IDs are searched once.
|
|
1267
|
+
*/
|
|
1268
|
+
async partitionSearchAccounts(accountIDs, user, md) {
|
|
1269
|
+
const readable = await StorageAccessEvaluator.Instance.AccessibleAccountIDs(accountIDs, user, 'Read', md);
|
|
1270
|
+
await FileStorageEngine.Instance.Config(false, user, md);
|
|
1271
|
+
if ([...readable].some((id) => !FileStorageEngine.Instance.GetAccountWithProvider(id))) {
|
|
1272
|
+
await FileStorageEngine.Instance.RefreshAfterMiss(md);
|
|
1273
|
+
}
|
|
1274
|
+
const searchable = [];
|
|
1275
|
+
const refused = [];
|
|
1276
|
+
const seen = new Set();
|
|
1277
|
+
for (const requestedID of accountIDs) {
|
|
1278
|
+
const key = NormalizeUUID(requestedID);
|
|
1279
|
+
if (seen.has(key))
|
|
1280
|
+
continue;
|
|
1281
|
+
seen.add(key);
|
|
1282
|
+
const pair = readable.has(key) ? FileStorageEngine.Instance.GetAccountWithProvider(requestedID) : null;
|
|
1283
|
+
if (pair) {
|
|
1284
|
+
searchable.push({ accountEntity: pair.account, providerEntity: pair.provider });
|
|
1285
|
+
}
|
|
1286
|
+
else {
|
|
1287
|
+
refused.push(this.refusedAccountResult(requestedID));
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
return { searchable, refused };
|
|
1291
|
+
}
|
|
1292
|
+
/** A failed per-account entry for an account the caller may not search; it names nothing beyond the requested ID. */
|
|
1293
|
+
refusedAccountResult(accountID) {
|
|
1294
|
+
return {
|
|
1295
|
+
accountID,
|
|
1296
|
+
accountName: '',
|
|
1297
|
+
providerID: '',
|
|
1298
|
+
providerName: '',
|
|
1299
|
+
success: false,
|
|
1300
|
+
errorMessage: SEARCH_ACCOUNT_REFUSED_MESSAGE,
|
|
1301
|
+
results: [],
|
|
1302
|
+
hasMore: false,
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Drops search hits that map to an `MJ: Files` row the caller may not read — one batched lookup per account, keyed on
|
|
1307
|
+
* each hit's path and provider object ID (`StorageAccessEvaluator.UnreadableTrackedObjectKeys`, compared the way the
|
|
1308
|
+
* account's driver addresses keys). Hits no row tracks stay; the account gate governs them.
|
|
1309
|
+
*
|
|
1310
|
+
* When any hit is dropped, the provider's `totalMatches` (which counted it) and `nextPageToken` (this route takes no
|
|
1311
|
+
* page token back) are withheld; `hasMore` is kept. How reliable the mapping is depends on the provider: for
|
|
1312
|
+
* object-key stores (S3, Azure Blob, GCS) a hit's path IS the row's `ProviderKey`; for hierarchical providers (Box,
|
|
1313
|
+
* Google Drive, Dropbox, SharePoint) a search path may not match the key a row was written with, and such a miss falls
|
|
1314
|
+
* back to the account gate. `MJ: Files` rows are keyed by provider, not account, so the lookup errs toward dropping.
|
|
1315
|
+
*/
|
|
1316
|
+
async dropUnreadableTrackedHits(accountResults, user, md) {
|
|
1317
|
+
return Promise.all(accountResults.map((ar) => this.dropUnreadableAccountHits(ar, user, md)));
|
|
1318
|
+
}
|
|
1319
|
+
/** {@link dropUnreadableTrackedHits} for one account's results; if the check cannot run, the account's hits are dropped. */
|
|
1320
|
+
async dropUnreadableAccountHits(ar, user, md) {
|
|
1321
|
+
if (!ar.success || ar.results.length === 0)
|
|
1322
|
+
return ar;
|
|
1323
|
+
try {
|
|
1324
|
+
const driver = await FileStorageEngine.Instance.GetDriver(ar.accountID, user);
|
|
1325
|
+
const keys = ar.results.flatMap((r) => [r.path, r.objectId].filter((k) => !!k));
|
|
1326
|
+
const unreadable = await StorageAccessEvaluator.Instance.UnreadableTrackedObjectKeys(ar.providerID, keys, user, md, driver);
|
|
1327
|
+
const results = ar.results.filter((r) => !unreadable.has(r.path) && !(r.objectId && unreadable.has(r.objectId)));
|
|
1328
|
+
return results.length === ar.results.length ? ar : { ...ar, results, totalMatches: undefined, nextPageToken: undefined };
|
|
1329
|
+
}
|
|
1330
|
+
catch (error) {
|
|
1331
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1332
|
+
LogError(`SearchAcrossAccounts: tracked-file check failed for account ${ar.accountID} — its hits are withheld: ${reason}`);
|
|
1333
|
+
return { ...ar, results: [], totalMatches: undefined, nextPageToken: undefined };
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
/**
|
|
1337
|
+
* The listed objects minus those behind an `MJ: Files` row the caller may not read (directories are prefixes, never
|
|
1338
|
+
* tracked objects, and are kept). Keys are compared the way the account's driver addresses them.
|
|
1339
|
+
*/
|
|
1340
|
+
async dropUnreadableListedObjects(accountId, objects, user, md) {
|
|
1341
|
+
const files = objects.filter((o) => !o.isDirectory);
|
|
1342
|
+
if (files.length === 0)
|
|
1343
|
+
return objects;
|
|
1344
|
+
const providerID = await this.accountProviderID(accountId, user, md);
|
|
1345
|
+
const driver = await FileStorageEngine.Instance.GetDriver(accountId, user);
|
|
1346
|
+
const unreadable = await StorageAccessEvaluator.Instance.UnreadableTrackedObjectKeys(providerID, files.map((o) => o.fullPath), user, md, driver);
|
|
1347
|
+
return objects.filter((o) => o.isDirectory || !unreadable.has(o.fullPath));
|
|
1348
|
+
}
|
|
1349
|
+
/** Converts per-account results to the GraphQL payload, recomputing the totals from what is actually returned. */
|
|
1350
|
+
toSearchAcrossAccountsPayload(accountResults) {
|
|
1351
|
+
const payloadResults = accountResults.map((ar) => ({
|
|
1165
1352
|
accountID: ar.accountID,
|
|
1166
1353
|
accountName: ar.accountName,
|
|
1167
1354
|
success: ar.success,
|
|
@@ -1182,10 +1369,10 @@ let FileResolver = class FileResolver extends FileResolverBase {
|
|
|
1182
1369
|
nextPageToken: ar.nextPageToken,
|
|
1183
1370
|
}));
|
|
1184
1371
|
return {
|
|
1185
|
-
accountResults,
|
|
1186
|
-
totalResultsReturned:
|
|
1187
|
-
successfulAccounts:
|
|
1188
|
-
failedAccounts:
|
|
1372
|
+
accountResults: payloadResults,
|
|
1373
|
+
totalResultsReturned: accountResults.reduce((sum, ar) => sum + (ar.success ? ar.results.length : 0), 0),
|
|
1374
|
+
successfulAccounts: accountResults.filter((ar) => ar.success).length,
|
|
1375
|
+
failedAccounts: accountResults.filter((ar) => !ar.success).length,
|
|
1189
1376
|
};
|
|
1190
1377
|
}
|
|
1191
1378
|
};
|