mirror of
https://github.com/ruvnet/RuView
synced 2026-07-28 18:21:42 +00:00
31fb3d53f6
Phase 1 could verify a token and phase 3 could gate on one, but there was no way for a user to OBTAIN one. This closes that: sign in with a Cognitum account and get a token a RuView sensing server accepts, instead of everyone sharing one static RUVIEW_API_TOKEN string. Lives in `ruview-auth` behind a non-default `login` feature rather than in the CLI, so the Tauri desktop app can reuse it instead of growing a second copy. A server built with default features still gets the verifier and nothing else — no reqwest, no tokio net, no browser launcher. (This amends ADR-271's "no login flow in this crate" note; the reason for that line was to keep the server lean, and a feature gate achieves it without duplication.) Ported from meta-proxy's src/oauth/, cross-checked against musica's cognitum_provider.rs — two independent implementations against this same AS. Where they agree, this follows both: redirect path EXACTLY /oauth/callback, 60-second refresh skew, OOB fallback on SSH/CONTAINER//.dockerenv. Refresh is the part with teeth. Identity rotates refresh tokens with reuse detection, so presenting a spent one revokes the whole session family. Both obvious implementations are wrong: refreshing concurrently looks like replay, and retrying a failed refresh with the same token IS the replay. So `Session::ensure_fresh` holds an async mutex across the await, re-checks expiry after acquiring it (the waiter usually finds the work already done), persists the rotated token BEFORE returning it, and never retries. A missing expires_at counts as expired rather than being given a guessed default. Least scope by default: `login` requests `sensing:read`. `--admin` adds `sensing:admin` explicitly, and requests both because there is no scope hierarchy server-side. A session that streams poses should not casually hold the capability to delete the model it streams through. Credentials are written atomically and 0600 (temp file, chmod BEFORE rename) — the same discipline the seed applies to its cloud key. `logout` is local-only and says so: it makes this machine unable to act as you, but revoking the session everywhere is an account-level action. Also `whoami`, which reports whether the stored token is live — an expired-looking session is the most common reason a command starts 401ing, and it should be visible directly rather than inferred from a failure elsewhere. Verified against PRODUCTION, not just locally: authorize URLs built by this exact code path return HTTP 200 from auth.cognitum.one for both `sensing:read` and `sensing:read sensing:admin`, which exercises the real client_id, scope encoding, PKCE parameters and redirect_uri shape. Tests: 74 with --features login (51 unit + 21 verifier matrix + 2 doctests), including the RFC 7636 Appendix B vector, multi-scope URL encoding (a space that is hand-formatted rather than encoded silently truncates the request), a real TCP callback round-trip, callback timeout, 0600 permissions asserted on disk, atomic-save leaving no temp file, and refresh-window boundaries. Unchanged: 43 with default features, 501 in the sensing server. Co-Authored-By: Ruflo & AQE
82 lines
2.5 KiB
TOML
82 lines
2.5 KiB
TOML
[package]
|
|
name = "wifi-densepose-cli"
|
|
version = "0.3.1"
|
|
edition.workspace = true
|
|
description = "CLI for WiFi-DensePose"
|
|
authors.workspace = true
|
|
license.workspace = true
|
|
repository.workspace = true
|
|
documentation = "https://docs.rs/wifi-densepose-cli"
|
|
keywords = ["wifi", "cli", "densepose", "disaster", "detection"]
|
|
categories = ["command-line-utilities", "science"]
|
|
readme = "README.md"
|
|
|
|
[[bin]]
|
|
name = "wifi-densepose"
|
|
path = "src/main.rs"
|
|
|
|
[features]
|
|
# `mat` pulls wifi-densepose-mat → -nn → ort (ONNX) → openssl-sys, which does NOT
|
|
# cross-compile to aarch64 and is irrelevant to the calibration path. Build the
|
|
# Pi/appliance calibration binary with `--no-default-features` to exclude it.
|
|
default = ["mat"]
|
|
mat = ["dep:wifi-densepose-mat"]
|
|
|
|
[dependencies]
|
|
# Internal crates
|
|
wifi-densepose-mat = { version = "0.3.0", path = "../wifi-densepose-mat", optional = true }
|
|
wifi-densepose-signal = { version = "0.3.1", path = "../wifi-densepose-signal", default-features = false }
|
|
wifi-densepose-core = { version = "0.3.0", path = "../wifi-densepose-core" }
|
|
wifi-densepose-calibration = { version = "0.3.0", path = "../wifi-densepose-calibration" }
|
|
|
|
# Linear algebra / complex numbers (used by calibrate.rs to build CsiFrame)
|
|
ndarray = { workspace = true }
|
|
num-complex = { workspace = true }
|
|
|
|
# CLI framework
|
|
clap = { version = "4.4", features = ["derive", "env", "cargo"] }
|
|
|
|
# Output formatting
|
|
colored = "2.1"
|
|
tabled = { version = "0.20", features = ["ansi"] }
|
|
indicatif = "0.17"
|
|
console = "0.16"
|
|
|
|
# Async runtime
|
|
tokio = { version = "1.35", features = ["full"] }
|
|
|
|
# HTTP API server (calibrate-serve subcommand — drives a future UI)
|
|
axum = { workspace = true }
|
|
tower-http = { version = "0.6", features = ["cors", "trace"] }
|
|
|
|
# Serialization
|
|
serde = { version = "1.0", features = ["derive"] }
|
|
serde_json = "1.0"
|
|
csv = "1.3"
|
|
|
|
# Error handling
|
|
anyhow = "1.0"
|
|
|
|
# ADR-271 phase 2 — `login`/`logout`/`whoami`. The `login` feature carries the
|
|
# interactive half (PKCE, loopback, OOB paste, credential store, refresh);
|
|
# the sensing server depends on this same crate with default features and
|
|
# gets only the verifier.
|
|
ruview-auth = { path = "../ruview-auth", features = ["login"] }
|
|
thiserror = "2.0"
|
|
|
|
# Time
|
|
chrono = { version = "0.4", features = ["serde"] }
|
|
|
|
# UUID
|
|
uuid = { version = "1.6", features = ["v4", "serde"] }
|
|
|
|
# Logging
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
|
|
|
[dev-dependencies]
|
|
assert_cmd = "2.0"
|
|
predicates = "3.0"
|
|
tempfile = "3.9"
|
|
tower = { workspace = true }
|