@xenon-device-management/xenon 2.0.0 → 2.1.0
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 +2 -1
- package/lib/package.json +1 -2
- package/lib/public/assets/{AnnotationOverlay-BmTPCAds.js → AnnotationOverlay-C-Pw_hvw.js} +1 -1
- package/lib/public/assets/{ApiKeyGate-B5eB7f8B.js → ApiKeyGate-DmYQ0oSk.js} +1 -1
- package/lib/public/assets/BugReportButton-n8T1dFUp.js +10 -0
- package/lib/public/assets/{DeviceMosaicView-9DUoZnQi.js → DeviceMosaicView-D-rGOBbJ.js} +1 -1
- package/lib/public/assets/{EmptyState-lvyNiXev.js → EmptyState-DExETevp.js} +1 -1
- package/lib/public/assets/{FieldGroup-BduMPM9K.js → FieldGroup-CCdFlZm6.js} +1 -1
- package/lib/public/assets/{FilterMenu-BSqX6I94.js → FilterMenu-DiUSOSL-.js} +1 -1
- package/lib/public/assets/{Menu-Ba5cT7jZ.js → Menu-ZOfpH6gh.js} +1 -1
- package/lib/public/assets/{Modal-CceoMY7D.js → Modal-CZNtoB-1.js} +1 -1
- package/lib/public/assets/{RecordingPage-6T9dOidc.js → RecordingPage-BKp6h8Jl.js} +1 -1
- package/lib/public/assets/{RecordingsPage-qj6FdZXR.js → RecordingsPage-Bf881FX4.js} +1 -1
- package/lib/public/assets/{SegmentedControl-CSRuZ8TR.js → SegmentedControl-EUoyizvW.js} +1 -1
- package/lib/public/assets/{SettingCard-Bp1kz4By.js → SettingCard-ClT4hf4_.js} +1 -1
- package/lib/public/assets/{Table-LV7zFyZg.js → Table-ucR4bnmc.js} +1 -1
- package/lib/public/assets/{activity-D17TUloi.js → activity-K-hdylwq.js} +1 -1
- package/lib/public/assets/{ai-settings-qvbhY89P.js → ai-settings-BLhdwI3c.js} +1 -1
- package/lib/public/assets/{api-keys-BfpvdG7G.js → api-keys-BbiN7rBI.js} +1 -1
- package/lib/public/assets/{apps-Dbnzd0fW.js → apps-Dez1petz.js} +1 -1
- package/lib/public/assets/{arrow-left-DD1VjtC7.js → arrow-left-UjmFHLAJ.js} +1 -1
- package/lib/public/assets/{arrow-right-D0pnM3ma.js → arrow-right-BVyEigbu.js} +1 -1
- package/lib/public/assets/{arrow-up-right-BIC8JOV6.js → arrow-up-right-B-ZlBof3.js} +1 -1
- package/lib/public/assets/{auth-shell-DWHPXSnI.js → auth-shell-DAQjAgUv.js} +2 -2
- package/lib/public/assets/{builds-page-ClCNBUv_.js → builds-page-CmIPa5JK.js} +1 -1
- package/lib/public/assets/{button-xtAhYR-f.js → button-Dd-IhMC1.js} +1 -1
- package/lib/public/assets/{calendar-CupwJ7Bm.js → calendar-CAIeJwa5.js} +1 -1
- package/lib/public/assets/{check-CyuEoKkd.js → check-C4Jrjg8f.js} +1 -1
- package/lib/public/assets/{chevron-left-DgX-F9xK.js → chevron-left-BGzlf-4i.js} +1 -1
- package/lib/public/assets/{chevron-right-BKJgJnU9.js → chevron-right-_wYqDjDC.js} +1 -1
- package/lib/public/assets/{circle-check-CZdg8mMa.js → circle-check-Ckn00yMB.js} +1 -1
- package/lib/public/assets/{circle-x-CZH6t9z-.js → circle-x-pdVkomLa.js} +1 -1
- package/lib/public/assets/{clock-Cd-t7xpe.js → clock-B8vmwkHj.js} +1 -1
- package/lib/public/assets/{copy-C6zerX3I.js → copy-BAqnFI-F.js} +1 -1
- package/lib/public/assets/{device-explorer-B1pI6b_M.js → device-explorer-CYq_4F_N.js} +3 -3
- package/lib/public/assets/device-explorer-D2AqrrLN.css +1 -0
- package/lib/public/assets/{download-x9WP7mcL.js → download-B6UEAp_1.js} +1 -1
- package/lib/public/assets/{forgot-password-B0TEH4N-.js → forgot-password-CLqNvmrN.js} +1 -1
- package/lib/public/assets/{funnel-Cb40MfFN.js → funnel-Da90BBbT.js} +1 -1
- package/lib/public/assets/{index-BR1wTIPC.js → index-BuIIcmGp.js} +2 -2
- package/lib/public/assets/{index-BvH_ekRy.js → index-MX7IzqyW.js} +1 -1
- package/lib/public/assets/{input-DejcCiTI.js → input-Bq9aY-We.js} +1 -1
- package/lib/public/assets/{keycodes-Mavx5iGg.js → keycodes-CIhXIJyP.js} +1 -1
- package/lib/public/assets/{layers-BKqYLiHE.js → layers-bXjpswQI.js} +1 -1
- package/lib/public/assets/{lock-LFsFhc17.js → lock-pJ72y5Fy.js} +1 -1
- package/lib/public/assets/{login-x2pkzt-t.js → login-UOcHALV2.js} +1 -1
- package/lib/public/assets/{maintenance-settings-CYafcoV-.js → maintenance-settings-XnJgp5yb.js} +1 -1
- package/lib/public/assets/{monitor-_7_uJ8d9.js → monitor-QkAhI1Jx.js} +1 -1
- package/lib/public/assets/{mouse-pointer-2-BfLuURKN.js → mouse-pointer-2-B42Da0m4.js} +1 -1
- package/lib/public/assets/{network-Z_zBhpsD.js → network-CKVqcyp6.js} +1 -1
- package/lib/public/assets/{overview-BY5uv08S.js → overview-BORGkN3N.js} +1 -1
- package/lib/public/assets/{page-header-CP2UkykG.js → page-header-CKWFOjOI.js} +1 -1
- package/lib/public/assets/{plus-BTD0dnQ0.js → plus-BpFKVdC8.js} +1 -1
- package/lib/public/assets/{profile-page-BhJZuCS7.js → profile-page-CcbKBicY.js} +1 -1
- package/lib/public/assets/{reset-password-CMN0SO81.js → reset-password-BA0W9bxl.js} +1 -1
- package/lib/public/assets/{runbook-page-CBpPX9_5.js → runbook-page-Cp7ZBq3s.js} +1 -1
- package/lib/public/assets/{select-Uc9E5pye.js → select-DIZWIoVR.js} +1 -1
- package/lib/public/assets/{selector-detail-page-BSyBnRuU.js → selector-detail-page-C_4YuvAp.js} +1 -1
- package/lib/public/assets/{selector-health-DzqxNkzm.js → selector-health-CXjD_Xth.js} +1 -1
- package/lib/public/assets/{selector-health-page-CyPLjoD1.js → selector-health-page-CTqIHeoG.js} +1 -1
- package/lib/public/assets/{session-detail-page-BZQZN1P-.js → session-detail-page-B0KYkk8z.js} +1 -1
- package/lib/public/assets/{settings-CkSWiZq8.js → settings-BcRBKUXd.js} +1 -1
- package/lib/public/assets/{status-pill-outline-BFYni2qo.js → status-pill-outline-Dh2PXSf_.js} +1 -1
- package/lib/public/assets/{tablet-BSd-xSZK.js → tablet-_sXqZztt.js} +1 -1
- package/lib/public/assets/{teams-PpoEQz31.js → teams-DGHT8MKm.js} +1 -1
- package/lib/public/assets/{trash-2-BorJv5_u.js → trash-2-BV88cV3Z.js} +1 -1
- package/lib/public/assets/{upload-C3i7sAZl.js → upload-CIDH9wkj.js} +1 -1
- package/lib/public/assets/{use-password-reset-mode-CeZQ3EsH.js → use-password-reset-mode-DYGuSNwl.js} +1 -1
- package/lib/public/assets/{users-CEJ3fC78.js → users-7yviqflc.js} +1 -1
- package/lib/public/assets/{users-Drd7ifJx.js → users-BUE7MoTq.js} +1 -1
- package/lib/public/assets/{video-off-CNzn6bPE.js → video-off-BTRxtlGo.js} +1 -1
- package/lib/public/assets/{webhook-settings-BFqX6QXV.js → webhook-settings-DMKD9Pgm.js} +1 -1
- package/lib/public/assets/{zap-4yN8Jdfw.js → zap-CWE94qcK.js} +1 -1
- package/lib/public/index.html +1 -1
- package/lib/src/InternalHttpClient.js +5 -1
- package/lib/src/XenonCapabilityManager.js +15 -1
- package/lib/src/app/appiumBasePath.js +32 -0
- package/lib/src/app/index.js +12 -0
- package/lib/src/app/insertBeforeRoutes.js +24 -4
- package/lib/src/app/registerCommandAuth.js +152 -33
- package/lib/src/app/routers/control.js +57 -13
- package/lib/src/app/routers/grid.js +28 -8
- package/lib/src/app/ws/h264StreamWs.js +13 -8
- package/lib/src/app/ws/logcatWs.js +12 -7
- package/lib/src/app/ws/upgradeRouter.js +215 -0
- package/lib/src/dashboard/event-manager.js +7 -6
- package/lib/src/data-service/device-service.js +152 -49
- package/lib/src/data-service/device-store.js +81 -10
- package/lib/src/data-service/deviceClaims.js +98 -0
- package/lib/src/data-service/deviceFieldOwners.js +32 -10
- package/lib/src/data-service/prisma-store.js +94 -31
- package/lib/src/device-managers/AndroidDeviceManager.js +13 -15
- package/lib/src/device-managers/HealthMonitorService.js +17 -2
- package/lib/src/device-managers/IOSDeviceManager.js +5 -1
- package/lib/src/device-managers/NodeDevices.js +7 -2
- package/lib/src/device-managers/android/AndroidH264StreamService.js +3 -3
- package/lib/src/device-managers/android/AndroidStreamService.js +6 -5
- package/lib/src/device-managers/android/previewHold.js +2 -2
- package/lib/src/device-managers/ios/IOSDiscoveryService.js +19 -8
- package/lib/src/device-managers/ios/IOSStreamService.js +158 -38
- package/lib/src/device-managers/localDeviceHosts.js +86 -0
- package/lib/src/device-managers/ownDeviceRow.js +38 -0
- package/lib/src/device-utils.js +78 -24
- package/lib/src/gateway/createHandoff.js +73 -0
- package/lib/src/gateway/defaultGateway.js +80 -0
- package/lib/src/gateway/forwardToNode.js +147 -0
- package/lib/src/gateway/hubSessionToken.js +356 -0
- package/lib/src/gateway/internalCall.js +90 -0
- package/lib/src/gateway/nodeSessionStatus.js +140 -0
- package/lib/src/gateway/nodeWebDriverUrl.js +139 -0
- package/lib/src/gateway/sessionCreate.js +136 -0
- package/lib/src/gateway/sessionGateway.js +201 -0
- package/lib/src/gateway/sessionLocator.js +100 -0
- package/lib/src/generated/client/edge.js +7 -4
- package/lib/src/generated/client/index-browser.js +4 -1
- package/lib/src/generated/client/index.d.ts +112 -1
- package/lib/src/generated/client/index.js +7 -4
- package/lib/src/generated/client/package.json +1 -1
- package/lib/src/generated/client/schema.prisma +9 -0
- package/lib/src/generated/client/wasm.js +4 -1
- package/lib/src/helpers/index.js +5 -1
- package/lib/src/logger.js +5 -0
- package/lib/src/middleware/authMiddleware.js +80 -0
- package/lib/src/middleware/commandAuth.js +31 -17
- package/lib/src/middleware/csrfMiddleware.js +3 -2
- package/lib/src/middleware/sessionListingFilter.js +153 -0
- package/lib/src/middleware/sessionUpgradeGuard.js +203 -0
- package/lib/src/plugin.js +27 -1
- package/lib/src/prisma.js +2 -1
- package/lib/src/services/DeviceReconciler.js +5 -2
- package/lib/src/services/OrphanSweeper.js +12 -19
- package/lib/src/services/PortAllocator.js +75 -16
- package/lib/src/services/ServerManager.js +50 -30
- package/lib/src/services/SessionLifecycleService.js +335 -76
- package/lib/src/services/ShutdownCoordinator.js +7 -1
- package/lib/src/services/SocketServer.js +11 -2
- package/lib/src/services/device-access/LiveSessionOwners.js +53 -0
- package/lib/src/services/device-access/SessionOwnerResolver.js +74 -0
- package/lib/src/services/lease/LeaseOrphanSweeper.js +32 -6
- package/lib/src/services/lease/LeaseService.js +19 -5
- package/lib/src/services/session/nodeCreateCaps.js +55 -0
- package/lib/src/services/session/sessionCredentials.js +7 -59
- package/lib/src/sessions/CloudSession.js +12 -0
- package/lib/src/sessions/LocalSession.js +52 -26
- package/lib/src/sessions/RemoteSession.js +116 -9
- package/lib/src/sessions/SessionManager.js +49 -6
- package/lib/src/sessions/appiumUmbrella.js +32 -0
- package/lib/test/helpers/appium-umbrella.js +84 -0
- package/lib/test/helpers/fake-device-table.js +97 -0
- package/lib/test/helpers/loki-stores.js +41 -0
- package/lib/test/helpers/loopbackRequest.js +77 -0
- package/lib/test/helpers/scratch-database.js +100 -0
- package/lib/test/helpers/scratch-port-leases.js +119 -0
- package/lib/test/helpers/test-container.js +35 -27
- package/lib/test/integration/androidDevices.spec.js +5 -4
- package/lib/test/integration/ios/01iOSSimulator.spec.js +6 -5
- package/lib/test/integration/ios/02iOSDevices.spec.js +3 -2
- package/lib/test/integration/prisma-device-store-add-devices.spec.js +14 -4
- package/lib/test/unit/AndroidDeviceManager.spec.js +8 -1
- package/lib/test/unit/DeviceModel.spec.js +9 -9
- package/lib/test/unit/HealingCascade.test.js +5 -1
- package/lib/test/unit/OrphanSweeper.test.js +17 -13
- package/lib/test/unit/SessionHeartbeatWrite.test.js +6 -7
- package/lib/test/unit/StreamTicketService.spec.js +4 -1
- package/lib/test/unit/TokenEndpoint.spec.js +17 -3
- package/lib/test/unit/aggregate-hotspots-session-scope.spec.js +5 -5
- package/lib/test/unit/android-h264-service.spec.js +6 -0
- package/lib/test/unit/android-stream-stall-eof.spec.js +10 -0
- package/lib/test/unit/apps-team-visibility.spec.js +16 -16
- package/lib/test/unit/auth-me-auth-disabled.test.js +7 -4
- package/lib/test/unit/authMiddleware.appTicket.spec.js +5 -5
- package/lib/test/unit/authMiddleware.bearer.spec.js +4 -1
- package/lib/test/unit/authMiddleware.mcpAudience.spec.js +4 -1
- package/lib/test/unit/authMiddleware.streamTicket.spec.js +19 -7
- package/lib/test/unit/authMiddleware.test.js +3 -0
- package/lib/test/unit/bug-report/route.spec.js +9 -9
- package/lib/test/unit/cliargs-admin-only.spec.js +3 -3
- package/lib/test/unit/clipboard-route.spec.js +18 -8
- package/lib/test/unit/command-auth-appium-server.spec.js +187 -32
- package/lib/test/unit/command-auth.spec.js +31 -31
- package/lib/test/unit/control-device-lookup.spec.js +9 -9
- package/lib/test/unit/control-forward-decision.spec.js +115 -0
- package/lib/test/unit/control-lookup-failure.spec.js +3 -3
- package/lib/test/unit/create-error-message.spec.js +43 -0
- package/lib/test/unit/create-handoff.spec.js +86 -0
- package/lib/test/unit/dashboard-events-team-scope.spec.js +6 -3
- package/lib/test/unit/device-access-guard.spec.js +28 -28
- package/lib/test/unit/device-service.spec.js +4 -0
- package/lib/test/unit/device-store-clear-at-boot.spec.js +48 -0
- package/lib/test/unit/device-sync-lost-update.spec.js +17 -89
- package/lib/test/unit/device-team-guard.spec.js +20 -20
- package/lib/test/unit/device-utils.spec.js +8 -0
- package/lib/test/unit/extract-team-cap.spec.js +65 -0
- package/lib/test/unit/grid-admin-only.spec.js +3 -3
- package/lib/test/unit/healing-tier-gate.spec.js +5 -1
- package/lib/test/unit/health-monitor-own-phones.spec.js +102 -0
- package/lib/test/unit/health-monitor-stream-lookup.spec.js +11 -1
- package/lib/test/unit/helpers.spec.js +27 -0
- package/lib/test/unit/hub-boot-sweep-recovery.spec.js +192 -0
- package/lib/test/unit/hub-create-once.spec.js +190 -0
- package/lib/test/unit/hub-create-session-umbrella.spec.js +160 -0
- package/lib/test/unit/hub-gateway-create-appium-server.spec.js +591 -0
- package/lib/test/unit/hub-node-control-forward.spec.js +291 -0
- package/lib/test/unit/hub-node-gateway-appium-server.spec.js +594 -0
- package/lib/test/unit/hub-node-race.spec.js +401 -0
- package/lib/test/unit/hub-node-same-host.spec.js +356 -0
- package/lib/test/unit/hub-register-removal.spec.js +165 -0
- package/lib/test/unit/hub-remote-session-heartbeat.spec.js +277 -0
- package/lib/test/unit/hub-routed-session-recovery.spec.js +151 -0
- package/lib/test/unit/hub-session-token.spec.js +265 -0
- package/lib/test/unit/insert-before-routes.spec.js +5 -5
- package/lib/test/unit/install-repository-app-team.spec.js +2 -2
- package/lib/test/unit/interceptor/MitmProxyHost.spec.js +23 -10
- package/lib/test/unit/internal-call.spec.js +181 -0
- package/lib/test/unit/ios-discovery-same-host.spec.js +193 -0
- package/lib/test/unit/ios-stream-appium-tunnel.spec.js +317 -0
- package/lib/test/unit/ios-stream-port-leases.spec.js +299 -0
- package/lib/test/unit/ios-wda-status-identity.spec.js +84 -0
- package/lib/test/unit/is-xenon-running.spec.js +69 -0
- package/lib/test/unit/lease/LeaseOrphanSweeper.spec.js +2 -2
- package/lib/test/unit/lease/LeaseService.spec.js +28 -1
- package/lib/test/unit/lease/lease-reap-held-phone.spec.js +154 -0
- package/lib/test/unit/lease/lease-sweeper-expiry.spec.js +92 -0
- package/lib/test/unit/lease/lease-team-visibility.spec.js +2 -2
- package/lib/test/unit/lease/lease-token-log-filter.spec.js +242 -64
- package/lib/test/unit/lease/lease-token-oracle.spec.js +20 -7
- package/lib/test/unit/lease/session-lease-proof.spec.js +8 -3
- package/lib/test/unit/logcat-ws.spec.js +4 -1
- package/lib/test/unit/loopback-request.spec.js +81 -0
- package/lib/test/unit/node-create-caps.spec.js +54 -0
- package/lib/test/unit/node-gateway-create-appium-server.spec.js +354 -0
- package/lib/test/unit/node-hub-session-owner.spec.js +223 -0
- package/lib/test/unit/omni-vision.spec.js +7 -3
- package/lib/test/unit/plugin.spec.js +45 -4
- package/lib/test/unit/port-allocator-os-probe.spec.js +151 -0
- package/lib/test/unit/profile-router.test.js +11 -11
- package/lib/test/unit/queue-requester.spec.js +5 -0
- package/lib/test/unit/queue-team-scope-routes.spec.js +17 -16
- package/lib/test/unit/queue-visibility.spec.js +1 -1
- package/lib/test/unit/recording-downloads-visibility.spec.js +2 -2
- package/lib/test/unit/recording-instrumentation.spec.js +5 -0
- package/lib/test/unit/recording-source-ended.spec.js +5 -0
- package/lib/test/unit/recording-store.spec.js +9 -3
- package/lib/test/unit/recordings-library-routes.spec.js +51 -51
- package/lib/test/unit/recordings-write-visibility.spec.js +2 -2
- package/lib/test/unit/remote-session-create.spec.js +130 -0
- package/lib/test/unit/reservation-team-visibility.spec.js +5 -5
- package/lib/test/unit/reset-link.test.js +14 -14
- package/lib/test/unit/session-app-resolution.spec.js +14 -10
- package/lib/test/unit/session-assets.spec.js +9 -9
- package/lib/test/unit/session-attribution-wiring.spec.js +29 -2
- package/lib/test/unit/session-create-layer.spec.js +255 -0
- package/lib/test/unit/session-credential-strip.spec.js +25 -9
- package/lib/test/unit/session-credentials.spec.js +0 -47
- package/lib/test/unit/session-listing-auth.spec.js +345 -0
- package/lib/test/unit/session-locator.spec.js +250 -0
- package/lib/test/unit/session-node-calls.spec.js +174 -0
- package/lib/test/unit/session-owner-resolver.spec.js +107 -0
- package/lib/test/unit/session-recovery-routing.spec.js +118 -0
- package/lib/test/unit/session-upgrade-guard.spec.js +571 -0
- package/lib/test/unit/shutdown-drain-remote.spec.js +58 -0
- package/lib/test/unit/socketServer.bearer.spec.js +3 -1
- package/lib/test/unit/stream-leave-route.spec.js +15 -7
- package/lib/test/unit/stream-own-device-row.spec.js +164 -0
- package/lib/test/unit/stream-stop-ownership.spec.js +13 -5
- package/lib/test/unit/stream-stop-recording.spec.js +13 -5
- package/lib/test/unit/stream-ticket-identity.spec.js +15 -5
- package/lib/test/unit/upgrade-router.spec.js +446 -0
- package/lib/test/unit/users-router.test.js +11 -8
- package/lib/test/unit/verify-credential.spec.js +4 -1
- package/lib/test/unit/webdriver-info-route.spec.js +64 -0
- package/lib/test/unit/ws-upgrade-routing-appium-server.spec.js +500 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -2
- package/prisma/migrations/20260929120000_device_claims/migration.sql +8 -0
- package/prisma/schema.prisma +9 -0
- package/lib/public/assets/BugReportButton-DKYsOdGV.js +0 -10
- package/lib/public/assets/device-explorer-DV8rWRxL.css +0 -1
- package/lib/src/proxy/wd-command-proxy.js +0 -221
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
45
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
require("reflect-metadata");
|
|
49
|
+
const chai_1 = require("chai");
|
|
50
|
+
const sinon_1 = __importDefault(require("sinon"));
|
|
51
|
+
const express_1 = __importDefault(require("express"));
|
|
52
|
+
const typedi_1 = require("typedi");
|
|
53
|
+
const SessionLifecycleService_1 = require("../../src/services/SessionLifecycleService");
|
|
54
|
+
const PluginContext_1 = require("../../src/PluginContext");
|
|
55
|
+
const IPluginArgs_1 = require("../../src/interfaces/IPluginArgs");
|
|
56
|
+
const deviceService = __importStar(require("../../src/data-service/device-service"));
|
|
57
|
+
const SessionManager_1 = require("../../src/sessions/SessionManager");
|
|
58
|
+
const SessionType_1 = __importDefault(require("../../src/enums/SessionType"));
|
|
59
|
+
const nodeWebDriverUrl_1 = require("../../src/gateway/nodeWebDriverUrl");
|
|
60
|
+
const container_registration_1 = require("../helpers/container-registration");
|
|
61
|
+
const loopbackServer_1 = require("../helpers/loopbackServer");
|
|
62
|
+
const loki_stores_1 = require("../helpers/loki-stores");
|
|
63
|
+
/**
|
|
64
|
+
* A session the hub creates on a node's phone (today's createSession path,
|
|
65
|
+
* through the plugin):
|
|
66
|
+
* - it is created under the node's own base path, which need not be the hub's;
|
|
67
|
+
* - it is registered in SESSION_MANAGER whatever the dashboard and video
|
|
68
|
+
* settings, because that is where the session gateway finds it.
|
|
69
|
+
*/
|
|
70
|
+
describe('a session the hub creates on a node', () => {
|
|
71
|
+
// The in-memory stores, also when this file runs on its own: without
|
|
72
|
+
// NODE_ENV=test the factory handed out the Prisma stores, which are the
|
|
73
|
+
// developer's ~/.cache/xenon/xenon.db.
|
|
74
|
+
(0, loki_stores_1.useLokiStores)();
|
|
75
|
+
const loopback = (0, loopbackServer_1.loopbackServers)();
|
|
76
|
+
let nodeOrigin;
|
|
77
|
+
let created;
|
|
78
|
+
let context;
|
|
79
|
+
let saved;
|
|
80
|
+
let restore;
|
|
81
|
+
const SESSION = 'node-session-1';
|
|
82
|
+
beforeEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
83
|
+
created = [];
|
|
84
|
+
const node = (0, express_1.default)();
|
|
85
|
+
node.use(express_1.default.json());
|
|
86
|
+
// The node's Appium runs at the root; the hub's at /wd/hub.
|
|
87
|
+
node.get('/xenon/api/webdriver', (0, nodeWebDriverUrl_1.webdriverInfoHandler)(() => ''));
|
|
88
|
+
node.post('/session', (req, res) => {
|
|
89
|
+
created.push({ url: req.originalUrl, body: req.body });
|
|
90
|
+
res.json({ value: { sessionId: SESSION, capabilities: { platformName: 'Android' } } });
|
|
91
|
+
});
|
|
92
|
+
const server = yield loopback.serve(node);
|
|
93
|
+
nodeOrigin = `http://127.0.0.1:${server.address().port}`;
|
|
94
|
+
restore = (0, container_registration_1.saveRegistrations)(nodeWebDriverUrl_1.NodeBasePathResolver);
|
|
95
|
+
typedi_1.Container.set(nodeWebDriverUrl_1.NodeBasePathResolver, new nodeWebDriverUrl_1.NodeBasePathResolver());
|
|
96
|
+
context = typedi_1.Container.get(PluginContext_1.PluginContext);
|
|
97
|
+
saved = Object.assign({}, context);
|
|
98
|
+
context.pluginArgs = Object.assign(Object.assign({}, IPluginArgs_1.DefaultPluginArgs), { enableDashboard: false });
|
|
99
|
+
context.nodeId = 'hub-node-id';
|
|
100
|
+
context.nodeBasePath = '/wd/hub';
|
|
101
|
+
sinon_1.default.stub(deviceService, 'updatedAllocatedDevice').resolves();
|
|
102
|
+
}));
|
|
103
|
+
afterEach(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
104
|
+
SessionManager_1.SESSION_MANAGER.removeSession(SESSION);
|
|
105
|
+
Object.assign(context, saved);
|
|
106
|
+
sinon_1.default.restore();
|
|
107
|
+
restore();
|
|
108
|
+
yield loopback.closeAll();
|
|
109
|
+
}));
|
|
110
|
+
const device = () => ({ udid: 'u1', host: nodeOrigin, nodeId: 'node-peer', platform: 'android' });
|
|
111
|
+
const caps = {
|
|
112
|
+
alwaysMatch: { platformName: 'Android', 'xe:options': { recordVideo: false } },
|
|
113
|
+
firstMatch: [{}],
|
|
114
|
+
};
|
|
115
|
+
it('is created under the node’s own base path', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
116
|
+
const svc = new SessionLifecycleService_1.SessionLifecycleService();
|
|
117
|
+
const response = yield svc.forwardSessionRequest(device(), caps);
|
|
118
|
+
(0, chai_1.expect)(created.map((c) => c.url)).to.deep.equal(['/session']);
|
|
119
|
+
(0, chai_1.expect)(response.value[0]).to.equal(SESSION);
|
|
120
|
+
}));
|
|
121
|
+
it('is registered in SESSION_MANAGER with the node’s URL, with the dashboard and video off', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
122
|
+
const svc = new SessionLifecycleService_1.SessionLifecycleService();
|
|
123
|
+
sinon_1.default.stub(svc, 'getFreshDevice').callsFake((d) => __awaiter(void 0, void 0, void 0, function* () { return d; }));
|
|
124
|
+
yield svc.finalizeSession({ protocol: 'W3C', value: [SESSION, { platformName: 'Android' }, 'W3C'] }, device(), caps, {}, true);
|
|
125
|
+
const session = SessionManager_1.SESSION_MANAGER.getSession(SESSION);
|
|
126
|
+
(0, chai_1.expect)(session, 'registered').to.not.equal(undefined);
|
|
127
|
+
(0, chai_1.expect)(session.getType()).to.equal(SessionType_1.default.REMOTE);
|
|
128
|
+
(0, chai_1.expect)(session.getWebDriverUrl()).to.equal(nodeOrigin);
|
|
129
|
+
}));
|
|
130
|
+
});
|
|
@@ -49,7 +49,7 @@ require("reflect-metadata");
|
|
|
49
49
|
const chai_1 = require("chai");
|
|
50
50
|
const sinon_1 = __importDefault(require("sinon"));
|
|
51
51
|
const express_1 = __importDefault(require("express"));
|
|
52
|
-
const
|
|
52
|
+
const loopbackRequest_1 = __importDefault(require("../helpers/loopbackRequest"));
|
|
53
53
|
const reservation_1 = __importDefault(require("../../src/app/routers/reservation"));
|
|
54
54
|
const deviceService = __importStar(require("../../src/data-service/device-service"));
|
|
55
55
|
const prisma_1 = require("../../src/prisma");
|
|
@@ -105,13 +105,13 @@ function buildApp(caller) {
|
|
|
105
105
|
app.use('/reservation', reservation_1.default);
|
|
106
106
|
return app;
|
|
107
107
|
}
|
|
108
|
-
const reserve = (caller, udid) => (0,
|
|
108
|
+
const reserve = (caller, udid) => (0, loopbackRequest_1.default)(buildApp(caller))
|
|
109
109
|
.post('/reservation')
|
|
110
110
|
.send({ udid, host: HOST, reservedBy: 'alice', duration: '1h' });
|
|
111
|
-
const extend = (caller, udid) => (0,
|
|
111
|
+
const extend = (caller, udid) => (0, loopbackRequest_1.default)(buildApp(caller))
|
|
112
112
|
.post(`/reservation/${udid}/${encodeURIComponent(HOST)}/extend`)
|
|
113
113
|
.send({ duration: '1h' });
|
|
114
|
-
const release = (caller, udid) => (0,
|
|
114
|
+
const release = (caller, udid) => (0, loopbackRequest_1.default)(buildApp(caller)).delete(`/reservation/${udid}/${encodeURIComponent(HOST)}`);
|
|
115
115
|
describe('reservations respect the team boundary', () => {
|
|
116
116
|
let reserveDevice;
|
|
117
117
|
let releaseReservation;
|
|
@@ -176,7 +176,7 @@ describe('reservations respect the team boundary', () => {
|
|
|
176
176
|
}));
|
|
177
177
|
describe('the list', () => {
|
|
178
178
|
const listed = (caller) => __awaiter(void 0, void 0, void 0, function* () {
|
|
179
|
-
const res = yield (0,
|
|
179
|
+
const res = yield (0, loopbackRequest_1.default)(buildApp(caller)).get('/reservation');
|
|
180
180
|
(0, chai_1.expect)(res.status).to.equal(200);
|
|
181
181
|
return res.body.reservations.map((r) => r.udid).sort();
|
|
182
182
|
});
|
|
@@ -16,7 +16,7 @@ require("reflect-metadata");
|
|
|
16
16
|
const chai_1 = require("chai");
|
|
17
17
|
const sinon_1 = __importDefault(require("sinon"));
|
|
18
18
|
const express_1 = __importDefault(require("express"));
|
|
19
|
-
const
|
|
19
|
+
const loopbackRequest_1 = __importDefault(require("../helpers/loopbackRequest"));
|
|
20
20
|
const typedi_1 = require("typedi");
|
|
21
21
|
const users_1 = require("../../src/app/routers/users");
|
|
22
22
|
const auth_1 = require("../../src/app/routers/auth");
|
|
@@ -71,7 +71,7 @@ describe('password-reset links', () => {
|
|
|
71
71
|
logged.push(args.map(String).join(' '));
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
const r = yield (0,
|
|
74
|
+
const r = yield (0, loopbackRequest_1.default)(usersApp({ userId: 'admin1', role: 'ADMIN' })).post('/users/m1/reset-link');
|
|
75
75
|
(0, chai_1.expect)(r.status).to.equal(200);
|
|
76
76
|
(0, chai_1.expect)(r.body.emailed).to.equal(false);
|
|
77
77
|
(0, chai_1.expect)(r.body.link).to.match(new RegExp(`/xenon/reset-password#${RAW}$`));
|
|
@@ -87,7 +87,7 @@ describe('password-reset links', () => {
|
|
|
87
87
|
.stub(typedi_1.Container.get(PasswordResetService_1.PasswordResetService), 'createToken')
|
|
88
88
|
.resolves({ raw: RAW, id: 't1' });
|
|
89
89
|
const send = sinon_1.default.stub(typedi_1.Container.get(EmailService_1.EmailService), 'send').resolves();
|
|
90
|
-
const r = yield (0,
|
|
90
|
+
const r = yield (0, loopbackRequest_1.default)(usersApp({ userId: 'admin1', role: 'ADMIN' })).post('/users/m1/reset-link');
|
|
91
91
|
(0, chai_1.expect)(r.status).to.equal(200);
|
|
92
92
|
(0, chai_1.expect)(r.body).to.include({ emailed: true });
|
|
93
93
|
(0, chai_1.expect)(r.body).to.not.have.property('link');
|
|
@@ -99,20 +99,20 @@ describe('password-reset links', () => {
|
|
|
99
99
|
.stub(prisma_1.prisma.user, 'findUnique')
|
|
100
100
|
.resolves(Object.assign(Object.assign({}, member), { id: 's1', role: 'SUPER_ADMIN' }));
|
|
101
101
|
const create = sinon_1.default.stub(typedi_1.Container.get(PasswordResetService_1.PasswordResetService), 'createToken');
|
|
102
|
-
const r = yield (0,
|
|
102
|
+
const r = yield (0, loopbackRequest_1.default)(usersApp({ userId: 'admin1', role: 'ADMIN' })).post('/users/s1/reset-link');
|
|
103
103
|
(0, chai_1.expect)(r.status).to.equal(403);
|
|
104
104
|
(0, chai_1.expect)(create.called).to.equal(false);
|
|
105
105
|
}));
|
|
106
106
|
it('refuses an ADMIN issuing a link for another ADMIN', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
107
107
|
sinon_1.default.stub(prisma_1.prisma.user, 'findUnique').resolves(Object.assign(Object.assign({}, member), { id: 'a2', role: 'ADMIN' }));
|
|
108
108
|
const create = sinon_1.default.stub(typedi_1.Container.get(PasswordResetService_1.PasswordResetService), 'createToken');
|
|
109
|
-
const r = yield (0,
|
|
109
|
+
const r = yield (0, loopbackRequest_1.default)(usersApp({ userId: 'admin1', role: 'ADMIN' })).post('/users/a2/reset-link');
|
|
110
110
|
(0, chai_1.expect)(r.status).to.equal(403);
|
|
111
111
|
(0, chai_1.expect)(create.called).to.equal(false);
|
|
112
112
|
}));
|
|
113
113
|
it('refuses a link for yourself', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
114
114
|
const create = sinon_1.default.stub(typedi_1.Container.get(PasswordResetService_1.PasswordResetService), 'createToken');
|
|
115
|
-
const r = yield (0,
|
|
115
|
+
const r = yield (0, loopbackRequest_1.default)(usersApp({ userId: 'me', role: 'SUPER_ADMIN' })).post('/users/me/reset-link');
|
|
116
116
|
(0, chai_1.expect)(r.status).to.equal(400);
|
|
117
117
|
(0, chai_1.expect)(create.called).to.equal(false);
|
|
118
118
|
}));
|
|
@@ -121,9 +121,9 @@ describe('password-reset links', () => {
|
|
|
121
121
|
const create = sinon_1.default.stub(typedi_1.Container.get(PasswordResetService_1.PasswordResetService), 'createToken');
|
|
122
122
|
const app = usersApp({ userId: 'root', role: 'SUPER_ADMIN' });
|
|
123
123
|
find.resolves(null);
|
|
124
|
-
(0, chai_1.expect)((yield (0,
|
|
124
|
+
(0, chai_1.expect)((yield (0, loopbackRequest_1.default)(app).post('/users/nope/reset-link')).status).to.equal(404);
|
|
125
125
|
find.resolves(Object.assign(Object.assign({}, member), { status: 'DISABLED' }));
|
|
126
|
-
(0, chai_1.expect)((yield (0,
|
|
126
|
+
(0, chai_1.expect)((yield (0, loopbackRequest_1.default)(app).post('/users/m1/reset-link')).status).to.equal(409);
|
|
127
127
|
(0, chai_1.expect)(create.called).to.equal(false);
|
|
128
128
|
}));
|
|
129
129
|
});
|
|
@@ -154,7 +154,7 @@ describe('password-reset links', () => {
|
|
|
154
154
|
.resolves({ id: 'u1', email: 'u@x.local', name: 'U', status: 'ACTIVE' });
|
|
155
155
|
const find = typedi_1.Container.get(UserService_1.UserService).findByEmail;
|
|
156
156
|
const create = sinon_1.default.stub(typedi_1.Container.get(PasswordResetService_1.PasswordResetService), 'createToken');
|
|
157
|
-
const r = yield (0,
|
|
157
|
+
const r = yield (0, loopbackRequest_1.default)(authApp()).post('/auth/forgot-password').send({ email: 'u@x.local' });
|
|
158
158
|
// The mint-or-skip decision runs in the same continuation as the user
|
|
159
159
|
// lookup, so once the lookup has resolved and a macrotask has passed,
|
|
160
160
|
// createToken has either been called or never will be.
|
|
@@ -172,7 +172,7 @@ describe('password-reset links', () => {
|
|
|
172
172
|
.stub(typedi_1.Container.get(PasswordResetService_1.PasswordResetService), 'createToken')
|
|
173
173
|
.resolves({ raw: RAW, id: 't1' });
|
|
174
174
|
const send = sinon_1.default.stub(typedi_1.Container.get(EmailService_1.EmailService), 'send').resolves();
|
|
175
|
-
yield (0,
|
|
175
|
+
yield (0, loopbackRequest_1.default)(authApp()).post('/auth/forgot-password').send({ email: 'u@x.local' });
|
|
176
176
|
yield until(() => send.called);
|
|
177
177
|
(0, chai_1.expect)(send.calledOnce).to.equal(true);
|
|
178
178
|
(0, chai_1.expect)(send.firstCall.args[0].text).to.include(`/xenon/reset-password#${RAW}`);
|
|
@@ -191,10 +191,10 @@ describe('password-reset links', () => {
|
|
|
191
191
|
const verify = sinon_1.default
|
|
192
192
|
.stub(typedi_1.Container.get(PasswordResetService_1.PasswordResetService), 'verifyToken')
|
|
193
193
|
.resolves({ id: 't1', userId: 'u1' });
|
|
194
|
-
const ok = yield (0,
|
|
194
|
+
const ok = yield (0, loopbackRequest_1.default)(app).post('/auth/reset-password/check').send({ token: RAW });
|
|
195
195
|
(0, chai_1.expect)(ok.status).to.equal(200);
|
|
196
196
|
(0, chai_1.expect)(verify.firstCall.args[0]).to.equal(RAW);
|
|
197
|
-
const old = yield (0,
|
|
197
|
+
const old = yield (0, loopbackRequest_1.default)(app).get(`/auth/reset-password/check/${RAW}`);
|
|
198
198
|
(0, chai_1.expect)(old.status).to.equal(404);
|
|
199
199
|
}));
|
|
200
200
|
});
|
|
@@ -210,7 +210,7 @@ describe('password-reset links', () => {
|
|
|
210
210
|
sinon_1.default.stub(reset, 'consume').resolves();
|
|
211
211
|
sinon_1.default.stub(typedi_1.Container.get(UserSessionService_1.UserSessionService), 'revokeAllForUser').resolves();
|
|
212
212
|
const revoke = sinon_1.default.stub(reset, 'revokeAllForUser').resolves(2);
|
|
213
|
-
const r = yield (0,
|
|
213
|
+
const r = yield (0, loopbackRequest_1.default)(app)
|
|
214
214
|
.post('/auth/reset-password')
|
|
215
215
|
.send({ token: RAW, newPassword: 'a-new-password' });
|
|
216
216
|
(0, chai_1.expect)(r.status).to.equal(204);
|
|
@@ -229,7 +229,7 @@ describe('password-reset links', () => {
|
|
|
229
229
|
const revoke = sinon_1.default
|
|
230
230
|
.stub(typedi_1.Container.get(PasswordResetService_1.PasswordResetService), 'revokeAllForUser')
|
|
231
231
|
.resolves(1);
|
|
232
|
-
const r = yield (0,
|
|
232
|
+
const r = yield (0, loopbackRequest_1.default)(app)
|
|
233
233
|
.post('/auth/change-password')
|
|
234
234
|
.send({ oldPassword: 'old-password', newPassword: 'new-password' });
|
|
235
235
|
(0, chai_1.expect)(r.status).to.equal(204);
|
|
@@ -51,8 +51,10 @@ const sinon_1 = __importDefault(require("sinon"));
|
|
|
51
51
|
const fs_1 = __importDefault(require("fs"));
|
|
52
52
|
const os_1 = __importDefault(require("os"));
|
|
53
53
|
const path_1 = __importDefault(require("path"));
|
|
54
|
+
const jose = __importStar(require("jose"));
|
|
54
55
|
const typedi_1 = require("typedi");
|
|
55
56
|
const SessionLifecycleService_1 = require("../../src/services/SessionLifecycleService");
|
|
57
|
+
const hubSessionToken_1 = require("../../src/gateway/hubSessionToken");
|
|
56
58
|
const PluginContext_1 = require("../../src/PluginContext");
|
|
57
59
|
const ApiKeyService_1 = require("../../src/services/ApiKeyService");
|
|
58
60
|
const UserService_1 = require("../../src/services/UserService");
|
|
@@ -111,7 +113,7 @@ describe('createSession — an app named by id follows the team rule', () => {
|
|
|
111
113
|
let restore;
|
|
112
114
|
let pendingCopy;
|
|
113
115
|
let finalCaps;
|
|
114
|
-
let
|
|
116
|
+
let release;
|
|
115
117
|
const create = (c) => __awaiter(void 0, void 0, void 0, function* () {
|
|
116
118
|
const next = sinon_1.default.stub().callsFake(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
117
119
|
driverCaps = JSON.parse(JSON.stringify(c));
|
|
@@ -152,8 +154,8 @@ describe('createSession — an app named by id follows the team rule', () => {
|
|
|
152
154
|
}));
|
|
153
155
|
sinon_1.default.stub(pendingSessions, 'removePendingSession').resolves();
|
|
154
156
|
sinon_1.default.stub(deviceService, 'updateDeviceProgress').resolves();
|
|
155
|
-
sinon_1.default.stub(deviceService, '
|
|
156
|
-
|
|
157
|
+
sinon_1.default.stub(deviceService, 'claimDeviceForSession').resolves(true);
|
|
158
|
+
release = sinon_1.default.stub(deviceService, 'releasePendingClaim').resolves(true);
|
|
157
159
|
svc = new SessionLifecycleService_1.SessionLifecycleService();
|
|
158
160
|
sinon_1.default.stub(svc, 'createSessionInstance').callsFake((...args) => {
|
|
159
161
|
finalCaps = JSON.parse(JSON.stringify(args[2]));
|
|
@@ -286,7 +288,7 @@ describe('createSession — an app named by id follows the team rule', () => {
|
|
|
286
288
|
(0, chai_1.expect)(text, where).to.not.match(/"token"|"accessKey"/);
|
|
287
289
|
}
|
|
288
290
|
}));
|
|
289
|
-
it('a peer node gets the ticket and the credentials; the hub keeps neither', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
291
|
+
it('a peer node gets the ticket and the hub’s token, never the credentials; the hub keeps neither', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
290
292
|
allocate.resolves({
|
|
291
293
|
udid: 'u2',
|
|
292
294
|
host: 'http://10.0.0.2:4723',
|
|
@@ -294,18 +296,19 @@ describe('createSession — an app named by id follows the team rule', () => {
|
|
|
294
296
|
nodeId: 'node-2',
|
|
295
297
|
});
|
|
296
298
|
let forwarded;
|
|
297
|
-
|
|
299
|
+
let hubToken;
|
|
300
|
+
sinon_1.default.stub(svc, 'forwardSessionRequest').callsFake((_d, fc, t) => __awaiter(void 0, void 0, void 0, function* () {
|
|
298
301
|
forwarded = JSON.parse(JSON.stringify(fc));
|
|
302
|
+
hubToken = t;
|
|
299
303
|
return { value: ['sess-2', { platformName: 'Android' }] };
|
|
300
304
|
}));
|
|
301
305
|
sinon_1.default.stub(svc, 'finalizeSession').resolves();
|
|
302
306
|
const c = caps('app-a', 'ak_team_a');
|
|
303
307
|
yield svc.createSession(sinon_1.default.stub(), {}, c);
|
|
304
308
|
(0, chai_1.expect)(parseAppUrl(forwarded.alwaysMatch['appium:app']).appId).to.equal('app-a');
|
|
305
|
-
(0, chai_1.expect)(forwarded.alwaysMatch['xe:options']).to.deep.equal({
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
});
|
|
309
|
+
(0, chai_1.expect)(forwarded.alwaysMatch['xe:options']).to.deep.equal({});
|
|
310
|
+
(0, chai_1.expect)(JSON.stringify(forwarded)).to.not.match(/"token"|"accessKey"/);
|
|
311
|
+
(0, chai_1.expect)(jose.decodeJwt(hubToken)).to.include({ aud: hubSessionToken_1.HUB_CREATE_AUDIENCE, udid: 'u2' });
|
|
309
312
|
for (const [where, seen] of Object.entries({ pendingCopy, sent: c })) {
|
|
310
313
|
const text = JSON.stringify(seen);
|
|
311
314
|
(0, chai_1.expect)(text, where).to.include(PLAIN);
|
|
@@ -326,6 +329,7 @@ describe('createSession — an app named by id follows the team rule', () => {
|
|
|
326
329
|
}
|
|
327
330
|
(0, chai_1.expect)(String(err === null || err === void 0 ? void 0 : err.message)).to.match(/app-a.*JWT key service not initialized/);
|
|
328
331
|
(0, chai_1.expect)(driverCaps, 'the driver was never started').to.equal(undefined);
|
|
329
|
-
(0, chai_1.expect)(
|
|
332
|
+
(0, chai_1.expect)(release.calledOnce).to.equal(true);
|
|
333
|
+
(0, chai_1.expect)(release.firstCall.args[0]).to.include({ udid: 'u1', host: 'h1' });
|
|
330
334
|
}));
|
|
331
335
|
});
|
|
@@ -16,7 +16,7 @@ require("reflect-metadata");
|
|
|
16
16
|
const chai_1 = require("chai");
|
|
17
17
|
const sinon_1 = __importDefault(require("sinon"));
|
|
18
18
|
const express_1 = __importDefault(require("express"));
|
|
19
|
-
const
|
|
19
|
+
const loopbackRequest_1 = __importDefault(require("../helpers/loopbackRequest"));
|
|
20
20
|
const fs_1 = __importDefault(require("fs"));
|
|
21
21
|
const os_1 = __importDefault(require("os"));
|
|
22
22
|
const path_1 = __importDefault(require("path"));
|
|
@@ -99,12 +99,12 @@ describe('session files', () => {
|
|
|
99
99
|
fs_1.default.rmSync(dir, { recursive: true, force: true });
|
|
100
100
|
});
|
|
101
101
|
it("serves a visible session's screenshot to a member", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
102
|
-
const r = yield (0,
|
|
102
|
+
const r = yield (0, loopbackRequest_1.default)(app(member)).get('/session/sess-shared/asset/screenshots/shot.png');
|
|
103
103
|
(0, chai_1.expect)(r.status).to.equal(200);
|
|
104
104
|
(0, chai_1.expect)(String(r.body)).to.equal('png of sess-shared');
|
|
105
105
|
}));
|
|
106
106
|
it('serves a video with Range, for seeking', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
107
|
-
const r = yield (0,
|
|
107
|
+
const r = yield (0, loopbackRequest_1.default)(app(member))
|
|
108
108
|
.get('/session/sess-shared/asset/video/sess-shared.mp4')
|
|
109
109
|
.set('Range', 'bytes=2-4');
|
|
110
110
|
(0, chai_1.expect)(r.status).to.equal(206);
|
|
@@ -116,15 +116,15 @@ describe('session files', () => {
|
|
|
116
116
|
error: true,
|
|
117
117
|
message: `Session with id ${id} not found`,
|
|
118
118
|
});
|
|
119
|
-
const hidden = yield (0,
|
|
120
|
-
const unknown = yield (0,
|
|
119
|
+
const hidden = yield (0, loopbackRequest_1.default)(app(member)).get('/session/sess-b/asset/screenshots/shot.png');
|
|
120
|
+
const unknown = yield (0, loopbackRequest_1.default)(app(member)).get('/session/sess-nope/asset/screenshots/shot.png');
|
|
121
121
|
(0, chai_1.expect)(unknown.status).to.equal(404);
|
|
122
122
|
(0, chai_1.expect)(unknown.body).to.deep.equal(unknownBody('sess-nope'));
|
|
123
123
|
(0, chai_1.expect)(hidden.status).to.equal(404);
|
|
124
124
|
(0, chai_1.expect)(hidden.body).to.deep.equal(unknownBody('sess-b'));
|
|
125
125
|
}));
|
|
126
126
|
it("an admin gets another team's session file", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
127
|
-
const r = yield (0,
|
|
127
|
+
const r = yield (0, loopbackRequest_1.default)(app(admin)).get('/session/sess-b/asset/screenshots/shot.png');
|
|
128
128
|
(0, chai_1.expect)(r.status).to.equal(200);
|
|
129
129
|
(0, chai_1.expect)(String(r.body)).to.equal('png of sess-b');
|
|
130
130
|
}));
|
|
@@ -136,14 +136,14 @@ describe('session files', () => {
|
|
|
136
136
|
'/session/sess-shared/asset/screenshots/%2e%2e%2finterceptor%2fsession.har',
|
|
137
137
|
'/session/sess-shared/asset/video/..%2f..%2frecordings%2fgrp%2fcomposite.mp4',
|
|
138
138
|
]) {
|
|
139
|
-
const r = yield (0,
|
|
139
|
+
const r = yield (0, loopbackRequest_1.default)(app(admin)).get(url);
|
|
140
140
|
(0, chai_1.expect)(r.status, url).to.equal(404);
|
|
141
141
|
(0, chai_1.expect)(r.text, url).to.not.include('HAR-SECRET');
|
|
142
142
|
(0, chai_1.expect)(r.text, url).to.not.include('COMPOSITE-SECRET');
|
|
143
143
|
}
|
|
144
144
|
}));
|
|
145
145
|
it('a missing file in a visible session is a JSON 404', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
146
|
-
const r = yield (0,
|
|
146
|
+
const r = yield (0, loopbackRequest_1.default)(app(member)).get('/session/sess-shared/asset/screenshots/none.png');
|
|
147
147
|
(0, chai_1.expect)(r.status).to.equal(404);
|
|
148
148
|
(0, chai_1.expect)(r.body).to.have.property('error');
|
|
149
149
|
}));
|
|
@@ -160,7 +160,7 @@ describe('session files', () => {
|
|
|
160
160
|
it('no longer serves session files to a caller with no login', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
161
161
|
const a = (0, express_1.default)();
|
|
162
162
|
a.use('/xenon', (0, index_1.createRouter)({ bindHostOrIp: '127.0.0.1', enableDashboard: true }));
|
|
163
|
-
const r = yield (0,
|
|
163
|
+
const r = yield (0, loopbackRequest_1.default)(a).get(`/xenon/session-recordings/${path_1.default.basename(probeDir)}/secret.txt`);
|
|
164
164
|
(0, chai_1.expect)(r.text).to.not.include(secret);
|
|
165
165
|
}));
|
|
166
166
|
});
|
|
@@ -36,15 +36,22 @@ const capsWith = (obj) => ({
|
|
|
36
36
|
describe('authorizeSessionRequest — identity', () => {
|
|
37
37
|
let svc;
|
|
38
38
|
let authDisabledBefore;
|
|
39
|
+
let restore;
|
|
39
40
|
beforeEach(() => {
|
|
40
41
|
authDisabledBefore = config_1.config.authDisabled;
|
|
41
42
|
config_1.config.authDisabled = false;
|
|
42
|
-
|
|
43
|
+
restore = (0, container_registration_1.saveRegistrations)(ApiKeyService_1.ApiKeyService, JwtKeyService_1.JwtKeyService, UserService_1.UserService);
|
|
44
|
+
// No such user and no teams, as the developer's database answered for
|
|
45
|
+
// these ids: the real lookups read that database. Nothing here is about
|
|
46
|
+
// the owner's teams.
|
|
47
|
+
typedi_1.Container.set(UserService_1.UserService, { findById: () => __awaiter(void 0, void 0, void 0, function* () { return null; }) });
|
|
48
|
+
sinon_1.default.stub(prisma_1.prisma.teamMember, 'findMany').resolves([]);
|
|
49
|
+
svc = new SessionLifecycleService_1.SessionLifecycleService();
|
|
43
50
|
});
|
|
44
51
|
afterEach(() => {
|
|
45
52
|
config_1.config.authDisabled = authDisabledBefore;
|
|
46
53
|
sinon_1.default.restore();
|
|
47
|
-
|
|
54
|
+
restore();
|
|
48
55
|
});
|
|
49
56
|
it('returns both ids for a valid xe:options pair', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
50
57
|
typedi_1.Container.set(ApiKeyService_1.ApiKeyService, {
|
|
@@ -304,6 +311,26 @@ describe('authorizeSessionRequest — leaseAccess', () => {
|
|
|
304
311
|
}
|
|
305
312
|
(0, chai_1.expect)(error === null || error === void 0 ? void 0 : error.message).to.match(/not allowed/);
|
|
306
313
|
}));
|
|
314
|
+
it('reads the team from xe:options.team, over a flat cap, and still checks it', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
315
|
+
withKey(keyRow('sessions'), user('MEMBER'));
|
|
316
|
+
teamRows.resolves([{ teamId: 'team_a' }, { teamId: 'team_b' }]);
|
|
317
|
+
const caps = (team) => capsWith({
|
|
318
|
+
'xe:options': { accessKey: 'ak', token: 'tk', team },
|
|
319
|
+
'xenon:team': 'team_a',
|
|
320
|
+
});
|
|
321
|
+
(0, chai_1.expect)(yield scope(caps('team_b'))).to.deep.equal({
|
|
322
|
+
callerTeamIds: ['team_b'],
|
|
323
|
+
scoped: true,
|
|
324
|
+
});
|
|
325
|
+
let error;
|
|
326
|
+
try {
|
|
327
|
+
yield invoke(svc, caps('team_c'));
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
error = e;
|
|
331
|
+
}
|
|
332
|
+
(0, chai_1.expect)(error === null || error === void 0 ? void 0 : error.message).to.equal("xe:options.team 'team_c' is not allowed for this API key");
|
|
333
|
+
}));
|
|
307
334
|
it('falls back to the shared pool when the lookup fails', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
308
335
|
withKey(keyRow('sessions'), null);
|
|
309
336
|
typedi_1.Container.set(UserService_1.UserService, { findById: sinon_1.default.stub().rejects(new Error('db down')) });
|