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 \
|
||||
//! 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
|
||||
//! four minutes because it waits out the server's rate limit on the login endpoints.
|
||||
//! The setup code works once, so restart the server with an empty database for every run (one test per run:
|
||||
//! `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;
|
||||
|
||||
@@ -131,3 +132,64 @@ async fn two_devices_against_local_server() {
|
||||
b.logout().await.unwrap();
|
||||
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