use super::admission_impl::PersistedManagedScopeGrantRecord;
use super::core_impl::PERSISTENT_MANAGED_ADMISSION_SCOPE;
use super::*;
async fn client_with_storage() -> (Client, std::path::PathBuf, std::path::PathBuf) {
let client = Client::new_provider_neutral("persistence-test".to_string(), Box::new(|| None));
let directory = std::env::temp_dir().join(format!(
"openrtc-grant-order-{}",
crate::session_token::generate_nonce()
));
tokio::fs::create_dir_all(&directory).await.unwrap();
*client.native_device_base_dir.write().await = Some(directory.clone());
let path = client
.managed_scope_grant_path(PERSISTENT_MANAGED_ADMISSION_SCOPE)
.await
.unwrap();
(client, directory, path)
}
fn serialized(token: &str) -> Vec<u8> {
serde_json::to_vec(&PersistedManagedScopeGrantRecord {
scope: PERSISTENT_MANAGED_ADMISSION_SCOPE.to_string(),
token: token.to_string(),
max_connections: 0,
})
.unwrap()
}
fn install_current(client: &Client, token: &str) {
let mut cache = client.managed_scope_tickets.write().unwrap();
cache.persistent_load_allowed = false;
client.session_token_registry.register(
token.to_string(),
PERSISTENT_MANAGED_ADMISSION_SCOPE,
0,
);
cache.insert(
PERSISTENT_MANAGED_ADMISSION_SCOPE.to_string(),
CachedManagedScopeTicket {
scope: PERSISTENT_MANAGED_ADMISSION_SCOPE.into(),
token: token.to_string(),
max_connections: 0,
compound_ticket: String::new(),
iroh_ticket: String::new(),
},
);
}
async fn persisted_token(path: &std::path::Path) -> String {
serde_json::from_slice::<PersistedManagedScopeGrantRecord>(
&tokio::fs::read(path).await.unwrap(),
)
.unwrap()
.token
}
#[tokio::test]
async fn old_scope_finish_cannot_delete_newly_persisted_same_scope_grant() {
let (client, directory, path) = client_with_storage().await;
install_current(&client, "old");
client
.persist_managed_scope_grant(PERSISTENT_MANAGED_ADMISSION_SCOPE, "old", 0)
.await
.unwrap();
client.begin_revoke_tokens_by_scope(PERSISTENT_MANAGED_ADMISSION_SCOPE);
install_current(&client, "fresh");
client
.persist_managed_scope_grant(PERSISTENT_MANAGED_ADMISSION_SCOPE, "fresh", 0)
.await
.unwrap();
client
.delete_persisted_managed_scope_grant(PERSISTENT_MANAGED_ADMISSION_SCOPE)
.await
.unwrap();
assert_eq!(persisted_token(&path).await, "fresh");
assert!(client
.session_token_registry
.validate_and_consume("old")
.is_err());
assert!(client
.session_token_registry
.validate_and_consume("fresh")
.is_ok());
tokio::fs::remove_dir_all(directory).await.unwrap();
}
#[tokio::test]
async fn prepared_old_write_cannot_overwrite_new_same_scope_grant() {
let (client, directory, path) = client_with_storage().await;
install_current(&client, "old");
let prepared = Client::prepare_private_managed_scope_grant(&path, &serialized("old"))
.await
.unwrap();
client.begin_revoke_tokens_by_scope(PERSISTENT_MANAGED_ADMISSION_SCOPE);
install_current(&client, "fresh");
client
.persist_managed_scope_grant(PERSISTENT_MANAGED_ADMISSION_SCOPE, "fresh", 0)
.await
.unwrap();
assert!(!client
.commit_prepared_managed_scope_grant(
&path,
&prepared,
PERSISTENT_MANAGED_ADMISSION_SCOPE,
"old",
0
)
.unwrap());
assert_eq!(persisted_token(&path).await, "fresh");
tokio::fs::remove_dir_all(directory).await.unwrap();
}
#[tokio::test]
async fn revoke_during_pending_startup_load_prevents_old_secret_registration() {
for revoke_token in [false, true] {
let (client, directory, path) = client_with_storage().await;
tokio::fs::write(&path, serialized("old")).await.unwrap();
let path_owner = client.native_device_base_dir.write().await;
let mut load =
std::pin::pin!(client
.rehydrate_persistent_managed_scope_ticket(PERSISTENT_MANAGED_ADMISSION_SCOPE));
assert!(matches!(
futures::poll!(load.as_mut()),
std::task::Poll::Pending
));
if revoke_token {
client.revoke_session_token("old");
} else {
client.begin_revoke_tokens_by_scope(PERSISTENT_MANAGED_ADMISSION_SCOPE);
}
drop(path_owner);
load.await.unwrap();
assert!(!client
.managed_scope_tickets
.read()
.unwrap()
.contains_key(PERSISTENT_MANAGED_ADMISSION_SCOPE));
assert!(client
.session_token_registry
.validate_and_consume("old")
.is_err());
tokio::fs::remove_dir_all(directory).await.unwrap();
}
}
#[tokio::test]
async fn startup_import_is_consumed_and_failed_delete_cannot_resurrect_it() {
let (client, directory, path) = client_with_storage().await;
tokio::fs::write(&path, serialized("old")).await.unwrap();
client
.rehydrate_persistent_managed_scope_ticket(PERSISTENT_MANAGED_ADMISSION_SCOPE)
.await
.unwrap();
assert_eq!(
client
.managed_scope_tickets
.read()
.unwrap()
.get(PERSISTENT_MANAGED_ADMISSION_SCOPE)
.unwrap()
.token,
"old"
);
client.begin_revoke_tokens_by_scope(PERSISTENT_MANAGED_ADMISSION_SCOPE);
tokio::fs::write(&path, serialized("old")).await.unwrap();
client
.rehydrate_persistent_managed_scope_ticket(PERSISTENT_MANAGED_ADMISSION_SCOPE)
.await
.unwrap();
assert!(client
.session_token_registry
.validate_and_consume("old")
.is_err());
assert!(!client
.managed_scope_tickets
.read()
.unwrap()
.contains_key(PERSISTENT_MANAGED_ADMISSION_SCOPE));
tokio::fs::remove_dir_all(directory).await.unwrap();
}
#[tokio::test]
async fn current_revocation_removes_disk_grant_and_unrelated_scope_preserves_startup() {
let (client, directory, path) = client_with_storage().await;
tokio::fs::write(&path, serialized("old")).await.unwrap();
client.begin_revoke_tokens_by_scope("unrelated");
client
.rehydrate_persistent_managed_scope_ticket(PERSISTENT_MANAGED_ADMISSION_SCOPE)
.await
.unwrap();
assert!(client
.session_token_registry
.validate_and_consume("old")
.is_ok());
client.begin_revoke_tokens_by_scope(PERSISTENT_MANAGED_ADMISSION_SCOPE);
client
.delete_persisted_managed_scope_grant(PERSISTENT_MANAGED_ADMISSION_SCOPE)
.await
.unwrap();
assert!(!path.exists());
tokio::fs::remove_dir_all(directory).await.unwrap();
}
#[tokio::test]
async fn unrelated_token_revoke_preserves_startup_grant_but_exact_revoke_blocks_it() {
for matching in [false, true] {
let (client, directory, path) = client_with_storage().await;
tokio::fs::write(&path, serialized("persisted"))
.await
.unwrap();
client.revoke_session_token(if matching { "persisted" } else { "unrelated" });
assert_eq!(
path.exists(),
!matching,
"exact disk-only revocation must also be durable"
);
client
.rehydrate_persistent_managed_scope_ticket(PERSISTENT_MANAGED_ADMISSION_SCOPE)
.await
.unwrap();
assert_eq!(
client
.session_token_registry
.validate_and_consume("persisted")
.is_ok(),
!matching
);
tokio::fs::remove_dir_all(directory).await.unwrap();
}
}
#[tokio::test]
async fn explicit_clear_prevents_pending_startup_import() {
let (client, directory, path) = client_with_storage().await;
tokio::fs::write(&path, serialized("old")).await.unwrap();
client.clear_session_tokens();
client
.rehydrate_persistent_managed_scope_ticket(PERSISTENT_MANAGED_ADMISSION_SCOPE)
.await
.unwrap();
assert!(client
.session_token_registry
.validate_and_consume("old")
.is_err());
tokio::fs::remove_dir_all(directory).await.unwrap();
}