mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 05:23:41 +08:00
fab3ec9aeb
Add Galaxy web assets and client settings interfaces with their bundled vendor notices. Co-authored-by: Danny <dannyirwin31@yahoo.com> Co-authored-by: Prabhaav Pillai <prabhaav@msu.edu> Co-authored-by: dirwin31 <83434411+dirwin31@users.noreply.github.com> Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com> Co-authored-by: Dom <168790843+firestar5683@users.noreply.github.com> Co-authored-by: prabhaavp <prabhaav@msu.edu>
213 lines
9.7 KiB
JavaScript
213 lines
9.7 KiB
JavaScript
import assert from "node:assert/strict"
|
|
import { LocalAuth } from "../web/js/auth-client.js"
|
|
import { MonitorFeed } from "../web/js/monitor-feed.js"
|
|
import { readFileSync } from "node:fs"
|
|
|
|
const states = [], calls = []
|
|
const responses = [
|
|
{ ok: true, body: { state: "configured", authenticated: false } },
|
|
{ ok: false, status: 401 },
|
|
{ ok: true, body: { authenticated: true } },
|
|
{ ok: true, body: { authenticated: false } },
|
|
]
|
|
const auth = new LocalAuth({ publish: (state) => states.push(state), fetcher: async (url, options) => {
|
|
calls.push({ url, options })
|
|
const response = responses.shift()
|
|
return { ok: response.ok, status: response.status ?? 200, json: async () => response.body }
|
|
} })
|
|
await auth.check()
|
|
assert.equal(states.at(-1).status, "login")
|
|
assert.equal(await auth.login("wrongpass"), false)
|
|
assert.equal(states.at(-1).error, "Sign in failed.")
|
|
assert.equal(await auth.login("correctpass"), true)
|
|
assert.equal(states.at(-1).status, "authenticated")
|
|
assert.equal(calls[2].options.credentials, "same-origin")
|
|
assert.equal(calls[2].options.headers["Content-Type"], "application/json")
|
|
assert.equal(await auth.logout(), true)
|
|
assert.equal(states.at(-1).status, "login")
|
|
assert.equal(calls[3].options.body, "{}")
|
|
|
|
const setup = new LocalAuth({ publish: (state) => states.push(state), fetcher: async () =>
|
|
({ ok: true, json: async () => ({ state: "setup_required", authenticated: false }) }) })
|
|
await setup.check()
|
|
assert.equal(states.at(-1).status, "setup_required")
|
|
const unavailable = new LocalAuth({ publish: (state) => states.push(state), fetcher: async () =>
|
|
({ ok: true, json: async () => ({ state: "unavailable", authenticated: false }) }) })
|
|
await unavailable.check()
|
|
assert.equal(states.at(-1).status, "unavailable")
|
|
|
|
let unauthorized = 0
|
|
const samples = []
|
|
const feed = new MonitorFeed({ mode: "local", publish: (state) => samples.push(state), unauthorized: () => unauthorized++,
|
|
fetcher: async () => ({ ok: false, status: 401 }) })
|
|
feed.start()
|
|
for (let i = 0; i < 5; i++) await Promise.resolve()
|
|
assert.equal(unauthorized, 1)
|
|
assert.equal(samples.at(-1).snapshot, null)
|
|
assert.equal(feed.stopped, true)
|
|
assert.equal(feed.timer, null)
|
|
|
|
const accessLostStates = []
|
|
let accessLost = 0
|
|
const accessLostFeed = new MonitorFeed({ mode: "local", publish: (state) => accessLostStates.push(state),
|
|
unauthorized: () => accessLost++, fetcher: async () => ({ ok: false, status: 503, json: async () => ({ code: "setup_required" }) }) })
|
|
accessLostFeed.start()
|
|
for (let i = 0; i < 5; i++) await Promise.resolve()
|
|
assert.equal(accessLost, 1)
|
|
assert.equal(accessLostStates.at(-1).snapshot, null)
|
|
assert.equal(accessLostFeed.stopped, true)
|
|
|
|
const lostLogoutStates = []
|
|
const lostLogout = new LocalAuth({ publish: (state) => lostLogoutStates.push(state), fetcher: async () => ({ ok: false, status: 401 }) })
|
|
assert.equal(await lostLogout.logout(), false)
|
|
assert.equal(lostLogoutStates.at(-1).status, "login")
|
|
|
|
const previewCalls = []
|
|
const preview = new MonitorFeed({ mode: "sample", publish: () => {}, fetcher: async (url) => {
|
|
previewCalls.push(url)
|
|
return { ok: false, status: 404 }
|
|
} })
|
|
preview.start()
|
|
for (let i = 0; i < 5; i++) await Promise.resolve()
|
|
assert.deepEqual(previewCalls, ["./data/system-monitor.sample.json"])
|
|
preview.stop()
|
|
|
|
const deferred = () => { let resolve; const promise = new Promise((done) => { resolve = done }); return { promise, resolve } }
|
|
const flush = async () => { for (let i = 0; i < 10; i++) await Promise.resolve() }
|
|
|
|
// A slow session check may not replace a completed sign-in.
|
|
const oldCheck = deferred(), raceStates = []
|
|
const race = new LocalAuth({ publish: (state) => raceStates.push(state), fetcher: (url) =>
|
|
url.endsWith("session") ? oldCheck.promise : Promise.resolve({ ok: true, json: async () => ({ authenticated: true }) }) })
|
|
const checking = race.check()
|
|
assert.equal(await race.login("password123"), true)
|
|
oldCheck.resolve({ ok: true, json: async () => ({ state: "configured", authenticated: false }) })
|
|
await checking
|
|
assert.equal(raceStates.at(-1).status, "authenticated")
|
|
|
|
// Two successful sign-ins execute in order and only the latest publishes state.
|
|
const first = deferred(), second = deferred(), signStates = []
|
|
let loginNumber = 0
|
|
const sign = new LocalAuth({ publish: (state) => signStates.push(state), fetcher: (url) => {
|
|
if (url.endsWith("login")) return ++loginNumber === 1 ? first.promise : second.promise
|
|
return Promise.resolve({ ok: true })
|
|
} })
|
|
const firstLogin = sign.login("first-password")
|
|
const secondLogin = sign.login("second-password")
|
|
await flush()
|
|
assert.equal(loginNumber, 1)
|
|
first.resolve({ ok: true })
|
|
assert.equal(await firstLogin, false)
|
|
await flush()
|
|
assert.equal(loginNumber, 2)
|
|
second.resolve({ ok: true })
|
|
assert.equal(await secondLogin, true)
|
|
assert.equal(signStates.at(-1).status, "authenticated")
|
|
|
|
const waitingLogin = deferred(), waitingLogout = deferred(), logoutStates = [], logoutCalls = []
|
|
const serialized = new LocalAuth({ publish: (state) => logoutStates.push(state), fetcher: (url) => {
|
|
logoutCalls.push(url)
|
|
return url.endsWith("login") ? waitingLogin.promise : waitingLogout.promise
|
|
} })
|
|
const pendingSignIn = serialized.login("password123")
|
|
const pendingSignOut = serialized.logout()
|
|
await flush()
|
|
assert.deepEqual(logoutCalls, ["./api/auth/login"])
|
|
waitingLogin.resolve({ ok: true })
|
|
await pendingSignIn
|
|
await flush()
|
|
assert.deepEqual(logoutCalls, ["./api/auth/login", "./api/auth/logout"])
|
|
waitingLogout.resolve({ ok: true })
|
|
assert.equal(await pendingSignOut, true)
|
|
assert.equal(logoutStates.at(-1).status, "login")
|
|
|
|
// A login requested while logout waits must run after logout's cookie mutation.
|
|
const queuedLogin1 = deferred(), queuedLogout = deferred(), queuedLogin2 = deferred()
|
|
const orderedCalls = [], orderedStates = []
|
|
let orderedLoginNumber = 0
|
|
const ordered = new LocalAuth({ publish: (state) => orderedStates.push(state), fetcher: (url) => {
|
|
orderedCalls.push(url)
|
|
if (url.endsWith("login")) return ++orderedLoginNumber === 1 ? queuedLogin1.promise : queuedLogin2.promise
|
|
return queuedLogout.promise
|
|
} })
|
|
const orderedFirst = ordered.login("first-password")
|
|
const orderedSignOut = ordered.logout()
|
|
const orderedSecond = ordered.login("second-password")
|
|
await flush()
|
|
assert.deepEqual(orderedCalls, ["./api/auth/login"])
|
|
queuedLogin1.resolve({ ok: true })
|
|
assert.equal(await orderedFirst, false)
|
|
await flush()
|
|
assert.deepEqual(orderedCalls, ["./api/auth/login", "./api/auth/logout"])
|
|
queuedLogout.resolve({ ok: true })
|
|
assert.equal(await orderedSignOut, false)
|
|
await flush()
|
|
assert.deepEqual(orderedCalls, ["./api/auth/login", "./api/auth/logout", "./api/auth/login"])
|
|
queuedLogin2.resolve({ ok: true })
|
|
assert.equal(await orderedSecond, true)
|
|
assert.deepEqual(orderedStates, [{ status: "authenticated", error: "", localAccess: false }])
|
|
|
|
const localStates = [], localCalls = []
|
|
let localSession = { state: "configured", authenticated: true, localAccess: true }
|
|
const local = new LocalAuth({ publish: (state) => localStates.push(state), fetcher: async (url) => {
|
|
localCalls.push(url)
|
|
return { ok: true, json: async () => localSession }
|
|
} })
|
|
await local.check()
|
|
assert.deepEqual(localStates.at(-1), { status: "authenticated", error: "", localAccess: true })
|
|
assert.equal(await local.login("unused"), false)
|
|
assert.equal(await local.logout(), false)
|
|
assert.deepEqual(localCalls, ["./api/auth/session"])
|
|
assert.equal(localStates.at(-1).status, "authenticated")
|
|
local.expired()
|
|
assert.deepEqual(localStates.at(-1), { status: "checking", error: "", localAccess: false })
|
|
await local.check()
|
|
assert.equal(localStates.at(-1).localAccess, true)
|
|
assert.equal(localStates.at(-1).status, "authenticated")
|
|
localSession = { state: "configured", authenticated: false, localAccess: false }
|
|
await local.check()
|
|
assert.deepEqual(localStates.at(-1), { status: "login", error: "", localAccess: false })
|
|
localSession = { state: "configured", authenticated: false, localAccess: true }
|
|
await local.check()
|
|
assert.equal(localStates.at(-1).status, "login")
|
|
assert.equal(localStates.at(-1).localAccess, false)
|
|
localSession = { state: "configured", authenticated: true, localAccess: "true" }
|
|
await local.check()
|
|
assert.equal(localStates.at(-1).localAccess, false)
|
|
|
|
const app = readFileSync(new URL("../web/js/app.js", import.meta.url), "utf8")
|
|
assert.match(app, /authState\.status === 'authenticated' && !authState\.localAccess/)
|
|
assert.match(app, /state\.monitorMode === 'local' && authState\.status !== 'authenticated'/)
|
|
assert.match(app, /sessionExpired\(\) \{ if \(initializing\) return; auth\.expired\(\); initialize\(\) \}/)
|
|
|
|
const oldMonitor = deferred(), nextMonitor = deferred(), lateStates = []
|
|
let lateUnauthorized = 0, monitorRequests = 0
|
|
const lateFeed = new MonitorFeed({ mode: "local", publish: (state) => lateStates.push(state),
|
|
unauthorized: () => lateUnauthorized++, fetcher: () => ++monitorRequests === 1 ? oldMonitor.promise : nextMonitor.promise })
|
|
lateFeed.start()
|
|
lateFeed.stop()
|
|
lateFeed.start()
|
|
oldMonitor.resolve({ ok: false, status: 401 })
|
|
await flush()
|
|
assert.equal(lateUnauthorized, 0)
|
|
assert.equal(lateFeed.stopped, false)
|
|
assert.equal(lateStates.length, 0)
|
|
lateFeed.stop()
|
|
console.log("Galaxy browser auth, expiry and static-preview isolation checks passed")
|
|
|
|
const gatewayStates = [], gatewayCalls = []
|
|
let gatewayAuthenticated = true
|
|
const gateway = new LocalAuth({ publish: value => gatewayStates.push(value), fetcher: async (path) => {
|
|
gatewayCalls.push(path)
|
|
return { ok: true, json: async () => ({ state: "configured", authenticated: gatewayAuthenticated, gatewayAccess: true }) }
|
|
} })
|
|
await gateway.check()
|
|
assert.equal(gatewayStates.at(-1).status, "authenticated")
|
|
assert.equal(gatewayStates.at(-1).gatewayAccess, true)
|
|
assert.equal(await gateway.login("never-submit-to-device"), false)
|
|
assert.equal(await gateway.logout(), false)
|
|
assert.deepEqual(gatewayCalls, ["./api/auth/session"])
|
|
gatewayAuthenticated = false
|
|
await gateway.check()
|
|
assert.equal(gatewayStates.at(-1).status, "gateway_login")
|