kv-ffi live test: a removed device re-enrols against the real server
Ignored like the other live test: a phone denied while pending keeps its identity over a reinstall and a fresh Add-a-device QR signs it straight in as a new device. Passed against kestrove-server reenroll 62fe35d. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -8,8 +8,9 @@
|
|||||||
//! KESTROVE_SERVER_URL=http://127.0.0.1:8080 KESTROVE_SETUP_TOKEN=live-test-setup-code \
|
//! KESTROVE_SERVER_URL=http://127.0.0.1:8080 KESTROVE_SETUP_TOKEN=live-test-setup-code \
|
||||||
//! cargo test -p kv-ffi --test live_server -- --ignored --nocapture
|
//! cargo test -p kv-ffi --test live_server -- --ignored --nocapture
|
||||||
//! ```
|
//! ```
|
||||||
//! The setup code works once, so restart the server with an empty database for every run. The run takes about
|
//! The setup code works once, so restart the server with an empty database for every run (one test per run:
|
||||||
//! four minutes because it waits out the server's rate limit on the login endpoints.
|
//! `two_devices_against_local_server` or `removed_device_reenrols_against_local_server`). They take a few minutes
|
||||||
|
//! because they wait out the server's rate limit on the login endpoints.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -131,3 +132,64 @@ async fn two_devices_against_local_server() {
|
|||||||
b.logout().await.unwrap();
|
b.logout().await.unwrap();
|
||||||
println!("live sync OK against {url}");
|
println!("live sync OK against {url}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A platform signer whose identity can be reset, like the iOS Keychain one (its items survive a reinstall).
|
||||||
|
struct Resettable(std::sync::Mutex<SoftwareSigner>);
|
||||||
|
|
||||||
|
impl DeviceSigner for Resettable {
|
||||||
|
fn device_id(&self) -> String {
|
||||||
|
kv_sync::DeviceSigner::device_id(&*self.0.lock().unwrap()).to_string()
|
||||||
|
}
|
||||||
|
fn public_key(&self) -> Vec<u8> {
|
||||||
|
kv_sync::DeviceSigner::public_key(&*self.0.lock().unwrap())
|
||||||
|
}
|
||||||
|
fn sign(&self, message: Vec<u8>) -> Result<Vec<u8>, CoreError> {
|
||||||
|
kv_sync::DeviceSigner::sign(&*self.0.lock().unwrap(), &message)
|
||||||
|
.map_err(|e| CoreError::Internal { msg: e.to_string() })
|
||||||
|
}
|
||||||
|
fn reset(&self) -> Result<(), CoreError> {
|
||||||
|
*self.0.lock().unwrap() = SoftwareSigner::generate(Uuid::new_v4());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// docs/03 "Removed devices" against the real server: the phone waits for approval, is denied, keeps its identity
|
||||||
|
/// over a "reinstall", and a fresh "Add a device" QR gets it straight in as a new device.
|
||||||
|
#[tokio::test]
|
||||||
|
#[ignore = "needs a fresh local kestrove-server; see the module docs"]
|
||||||
|
async fn removed_device_reenrols_against_local_server() {
|
||||||
|
let url = std::env::var("KESTROVE_SERVER_URL").unwrap_or_else(|_| "http://127.0.0.1:8080".into());
|
||||||
|
let token =
|
||||||
|
std::env::var("KESTROVE_SETUP_TOKEN").expect("set KESTROVE_SETUP_TOKEN to the token the server printed");
|
||||||
|
let pw = "a long master password for the live test";
|
||||||
|
|
||||||
|
let web = VaultCore::new_in_memory();
|
||||||
|
web.create_account(pw.into()).unwrap();
|
||||||
|
connect(&web, &url, "web vault");
|
||||||
|
web.register_account(token).await.unwrap();
|
||||||
|
let id = web.save_item(item("Bank", "pw-a")).unwrap();
|
||||||
|
web.sync_now().await.unwrap();
|
||||||
|
|
||||||
|
let signer = Arc::new(Resettable(std::sync::Mutex::new(SoftwareSigner::generate(Uuid::new_v4()))));
|
||||||
|
let phone = VaultCore::new_in_memory();
|
||||||
|
phone.set_device_signer(signer.clone(), "iPhone".into(), PlatformDto::Ios).unwrap();
|
||||||
|
let kit = kv_ffi::parse_setup_payload(web.setup_payload_for_kit().unwrap()).unwrap();
|
||||||
|
assert_eq!(phone.login_with_setup(kit, pw.into()).await.unwrap(), LoginResult::PendingApproval);
|
||||||
|
let old_id = signer.device_id();
|
||||||
|
web.revoke_device(old_id.clone()).await.unwrap();
|
||||||
|
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(61)).await; // rate limit
|
||||||
|
// Reinstall: a new core, the same identity, a fresh unbound ticket.
|
||||||
|
let phone = VaultCore::new_in_memory();
|
||||||
|
phone.set_device_signer(signer.clone(), "iPhone".into(), PlatformDto::Ios).unwrap();
|
||||||
|
let setup = kv_ffi::parse_setup_payload(web.add_device_payload().await.unwrap().payload).unwrap();
|
||||||
|
assert_eq!(phone.login_with_setup(setup, pw.into()).await.unwrap(), LoginResult::Ok);
|
||||||
|
assert_ne!(signer.device_id(), old_id);
|
||||||
|
phone.sync_now().await.unwrap();
|
||||||
|
assert_eq!(phone.get_item(id).unwrap().password.as_deref(), Some("pw-a"));
|
||||||
|
let devices = web.list_devices().await.unwrap();
|
||||||
|
assert!(devices.iter().any(|d| d.device_id == old_id && d.revoked && !d.approved));
|
||||||
|
assert!(devices.iter().any(|d| d.device_id == signer.device_id() && d.approved));
|
||||||
|
assert!(matches!(web.approve_device(old_id).await, Err(CoreError::NotFound)));
|
||||||
|
println!("removed device re-enrolled OK against {url}");
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user