pub mod mapper;
use crate::{
cdk::types::Principal,
domain::policy::auth::{
RootDelegationRenewalBatch, RootIssuerPolicy, RootIssuerRenewalAttempt,
RootIssuerRenewalState, RootIssuerRenewalTemplate,
},
dto::auth::{
ActiveDelegationProof, ChainKeyBatchHeaderV1, ChainKeyBatchWitnessV1,
ChainKeyDelegationCertV1, ChainKeyRootSignatureV1, DelegationCert,
},
ops::storage::auth::mapper::{
ActiveDelegationProofRecordMapper, ChainKeyRootDelegationBatchRecordMapper,
RootDelegationRenewalBatchRecordMapper, RootIssuerPolicyRecordMapper,
RootIssuerRenewalAttemptRecordMapper, RootIssuerRenewalStateRecordMapper,
RootIssuerRenewalTemplateRecordMapper,
},
storage::stable::auth::{
AuthState, DelegatedSessionBootstrapBindingRecord, DelegatedSessionRecord,
RootProvisionerRecord,
},
};
pub use crate::storage::stable::auth::DelegatedSessionUpsertResult;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DelegatedSession {
pub wallet_pid: Principal,
pub delegated_pid: Principal,
pub issued_at: u64,
pub expires_at: u64,
pub bootstrap_token_fingerprint: Option<[u8; 32]>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DelegatedSessionBootstrapBinding {
pub wallet_pid: Principal,
pub delegated_pid: Principal,
pub token_fingerprint: [u8; 32],
pub bound_at: u64,
pub expires_at: u64,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct RootDelegationRenewalProvisioner {
pub principal: Principal,
pub enabled: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ChainKeyRootDelegationBatchStatus {
Prepared,
Signing,
Signed,
Installing,
Installed,
FailedRetryable,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ChainKeyRootDelegationBatchIssuer {
pub issuer_pid: Principal,
pub cert_hash: [u8; 32],
pub delegation_cert: DelegationCert,
pub chain_key_delegation_cert: ChainKeyDelegationCertV1,
pub issuer_witness: ChainKeyBatchWitnessV1,
pub refresh_after_ns: u64,
pub installed_at_ns: Option<u64>,
pub last_failure: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ChainKeyRootDelegationBatch {
pub batch_id: [u8; 32],
pub status: ChainKeyRootDelegationBatchStatus,
pub header_hash: [u8; 32],
pub header: ChainKeyBatchHeaderV1,
pub signature: Option<ChainKeyRootSignatureV1>,
pub issuers: Vec<ChainKeyRootDelegationBatchIssuer>,
pub prepared_at_ns: u64,
pub signed_at_ns: Option<u64>,
pub install_started_at_ns: Option<u64>,
pub installed_at_ns: Option<u64>,
pub retry_after_ns: Option<u64>,
pub failure: Option<String>,
}
pub struct AuthStateOps;
impl AuthStateOps {
#[must_use]
pub fn delegated_session(wallet_pid: Principal, now_secs: u64) -> Option<DelegatedSession> {
AuthState::get_active_delegated_session(wallet_pid, now_secs)
.map(delegated_session_record_to_view)
}
#[must_use]
pub fn delegated_session_subject(wallet_pid: Principal, now_secs: u64) -> Option<Principal> {
Self::delegated_session(wallet_pid, now_secs).map(|session| session.delegated_pid)
}
#[cfg(test)]
pub fn upsert_delegated_session(
session: DelegatedSession,
now_secs: u64,
) -> DelegatedSessionUpsertResult {
AuthState::upsert_delegated_session(delegated_session_view_to_record(session), now_secs)
}
pub fn upsert_delegated_session_with_bootstrap_binding(
session: DelegatedSession,
binding: DelegatedSessionBootstrapBinding,
now_secs: u64,
) -> DelegatedSessionUpsertResult {
AuthState::upsert_delegated_session_with_bootstrap_binding(
delegated_session_view_to_record(session),
delegated_session_bootstrap_binding_view_to_record(binding),
now_secs,
)
}
pub fn clear_delegated_session(wallet_pid: Principal) {
AuthState::clear_delegated_session(wallet_pid);
}
#[must_use]
pub fn prune_expired_delegated_sessions(now_secs: u64) -> usize {
AuthState::prune_expired_delegated_sessions(now_secs)
}
#[must_use]
pub fn delegated_session_bootstrap_binding(
token_fingerprint: [u8; 32],
now_secs: u64,
) -> Option<DelegatedSessionBootstrapBinding> {
AuthState::get_active_delegated_session_bootstrap_binding(token_fingerprint, now_secs)
.map(delegated_session_bootstrap_binding_record_to_view)
}
#[must_use]
pub fn prune_expired_delegated_session_bootstrap_bindings(now_secs: u64) -> usize {
AuthState::prune_expired_delegated_session_bootstrap_bindings(now_secs)
}
#[must_use]
pub fn active_delegation_proof(now_ns: u64) -> Option<ActiveDelegationProof> {
let proof = AuthState::get_active_delegation_proof()
.map(ActiveDelegationProofRecordMapper::record_to_dto)?;
if now_ns < proof.not_before_ns || now_ns >= proof.expires_at_ns {
return None;
}
Some(proof)
}
#[must_use]
pub fn active_delegation_proof_snapshot() -> Option<ActiveDelegationProof> {
AuthState::get_active_delegation_proof()
.map(ActiveDelegationProofRecordMapper::record_to_dto)
}
pub fn set_active_delegation_proof(proof: ActiveDelegationProof) {
AuthState::set_active_delegation_proof(ActiveDelegationProofRecordMapper::dto_to_record(
proof,
));
}
#[cfg(test)]
pub fn clear_active_delegation_proof() {
AuthState::clear_active_delegation_proof();
}
#[must_use]
pub fn root_issuer_policy(issuer_pid: Principal) -> Option<RootIssuerPolicy> {
AuthState::get_root_issuer(issuer_pid).map(RootIssuerPolicyRecordMapper::record_to_policy)
}
#[must_use]
pub fn root_issuer_policies() -> Vec<RootIssuerPolicy> {
AuthState::list_root_issuers()
.into_iter()
.map(RootIssuerPolicyRecordMapper::record_to_policy)
.collect()
}
pub fn upsert_root_issuer_policy(policy: RootIssuerPolicy) {
AuthState::upsert_root_issuer(RootIssuerPolicyRecordMapper::policy_to_record(policy));
}
#[must_use]
pub fn delegated_auth_registry_epoch() -> u64 {
AuthState::delegated_auth_registry_epoch()
}
pub fn advance_delegated_auth_registry_epoch() -> u64 {
AuthState::advance_delegated_auth_registry_epoch()
}
#[must_use]
#[cfg(test)]
pub fn delegated_auth_proof_epoch() -> u64 {
AuthState::delegated_auth_proof_epoch()
}
pub fn advance_delegated_auth_proof_epoch_at_least(min_epoch: u64) -> u64 {
AuthState::advance_delegated_auth_proof_epoch_at_least(min_epoch)
}
#[must_use]
pub fn root_issuer_renewal_template(
issuer_pid: Principal,
) -> Option<RootIssuerRenewalTemplate> {
AuthState::get_root_issuer_renewal_template(issuer_pid)
.map(RootIssuerRenewalTemplateRecordMapper::record_to_template)
}
#[must_use]
pub fn root_issuer_renewal_templates() -> Vec<RootIssuerRenewalTemplate> {
AuthState::list_root_issuer_renewal_templates()
.into_iter()
.map(RootIssuerRenewalTemplateRecordMapper::record_to_template)
.collect()
}
pub fn upsert_root_issuer_renewal_template(template: RootIssuerRenewalTemplate) {
AuthState::upsert_root_issuer_renewal_template(
RootIssuerRenewalTemplateRecordMapper::template_to_record(template),
);
}
#[must_use]
pub fn root_issuer_renewal_state(issuer_pid: Principal) -> Option<RootIssuerRenewalState> {
AuthState::get_root_issuer_renewal_state(issuer_pid)
.map(RootIssuerRenewalStateRecordMapper::record_to_state)
}
pub fn upsert_root_issuer_renewal_state(state: RootIssuerRenewalState) {
AuthState::upsert_root_issuer_renewal_state(
RootIssuerRenewalStateRecordMapper::state_to_record(state),
);
}
#[must_use]
pub fn root_issuer_renewal_attempt(attempt_id: [u8; 32]) -> Option<RootIssuerRenewalAttempt> {
AuthState::get_root_issuer_renewal_attempt(attempt_id)
.map(RootIssuerRenewalAttemptRecordMapper::record_to_attempt)
}
pub fn upsert_root_issuer_renewal_attempt(attempt: RootIssuerRenewalAttempt) {
AuthState::upsert_root_issuer_renewal_attempt(
RootIssuerRenewalAttemptRecordMapper::attempt_to_record(attempt),
);
}
#[must_use]
pub fn root_delegation_renewal_batch(batch_id: [u8; 32]) -> Option<RootDelegationRenewalBatch> {
AuthState::get_root_delegation_renewal_batch(batch_id)
.map(RootDelegationRenewalBatchRecordMapper::record_to_batch)
}
#[must_use]
pub fn root_delegation_renewal_batches() -> Vec<RootDelegationRenewalBatch> {
AuthState::list_root_delegation_renewal_batches()
.into_iter()
.map(RootDelegationRenewalBatchRecordMapper::record_to_batch)
.collect()
}
#[allow(
dead_code,
reason = "pre-0.76 bridge-backed renewal batch writer is retained for historical scheduler code during the hard-cut migration"
)]
pub fn upsert_root_delegation_renewal_batch(batch: RootDelegationRenewalBatch) {
AuthState::upsert_root_delegation_renewal_batch(
RootDelegationRenewalBatchRecordMapper::batch_to_record(batch),
);
}
pub fn prune_root_delegation_renewal_batches(now_ns: u64) -> usize {
AuthState::prune_root_delegation_renewal_batches(now_ns)
}
#[must_use]
pub fn chain_key_root_delegation_batch(
batch_id: [u8; 32],
) -> Option<ChainKeyRootDelegationBatch> {
AuthState::get_chain_key_root_delegation_batch(batch_id)
.map(ChainKeyRootDelegationBatchRecordMapper::record_to_batch)
}
#[must_use]
pub fn chain_key_root_delegation_batches() -> Vec<ChainKeyRootDelegationBatch> {
AuthState::list_chain_key_root_delegation_batches()
.into_iter()
.map(ChainKeyRootDelegationBatchRecordMapper::record_to_batch)
.collect()
}
pub fn upsert_chain_key_root_delegation_batch(batch: ChainKeyRootDelegationBatch) {
AuthState::upsert_chain_key_root_delegation_batch(
ChainKeyRootDelegationBatchRecordMapper::batch_to_record(batch),
);
}
pub fn prune_chain_key_root_delegation_batches(now_ns: u64) -> usize {
AuthState::prune_chain_key_root_delegation_batches(now_ns)
}
#[must_use]
pub fn root_delegation_renewal_provisioner(
principal: Principal,
) -> Option<RootDelegationRenewalProvisioner> {
AuthState::get_root_provisioner(principal).map(root_provisioner_record_to_view)
}
#[must_use]
pub fn root_delegation_renewal_provisioners() -> Vec<RootDelegationRenewalProvisioner> {
AuthState::list_root_provisioners()
.into_iter()
.map(root_provisioner_record_to_view)
.collect()
}
#[must_use]
pub fn is_root_delegation_renewal_provisioner(principal: Principal) -> bool {
Self::root_delegation_renewal_provisioner(principal).is_some_and(|view| view.enabled)
}
pub fn upsert_root_delegation_renewal_provisioner(
provisioner: RootDelegationRenewalProvisioner,
) {
AuthState::upsert_root_provisioner(root_provisioner_view_to_record(provisioner));
}
}
const fn delegated_session_record_to_view(record: DelegatedSessionRecord) -> DelegatedSession {
DelegatedSession {
wallet_pid: record.wallet_pid,
delegated_pid: record.delegated_pid,
issued_at: record.issued_at,
expires_at: record.expires_at,
bootstrap_token_fingerprint: record.bootstrap_token_fingerprint,
}
}
const fn delegated_session_view_to_record(view: DelegatedSession) -> DelegatedSessionRecord {
DelegatedSessionRecord {
wallet_pid: view.wallet_pid,
delegated_pid: view.delegated_pid,
issued_at: view.issued_at,
expires_at: view.expires_at,
bootstrap_token_fingerprint: view.bootstrap_token_fingerprint,
}
}
const fn delegated_session_bootstrap_binding_record_to_view(
record: DelegatedSessionBootstrapBindingRecord,
) -> DelegatedSessionBootstrapBinding {
DelegatedSessionBootstrapBinding {
wallet_pid: record.wallet_pid,
delegated_pid: record.delegated_pid,
token_fingerprint: record.token_fingerprint,
bound_at: record.bound_at,
expires_at: record.expires_at,
}
}
const fn delegated_session_bootstrap_binding_view_to_record(
view: DelegatedSessionBootstrapBinding,
) -> DelegatedSessionBootstrapBindingRecord {
DelegatedSessionBootstrapBindingRecord {
wallet_pid: view.wallet_pid,
delegated_pid: view.delegated_pid,
token_fingerprint: view.token_fingerprint,
bound_at: view.bound_at,
expires_at: view.expires_at,
}
}
const fn root_provisioner_record_to_view(
record: RootProvisionerRecord,
) -> RootDelegationRenewalProvisioner {
RootDelegationRenewalProvisioner {
principal: record.principal,
enabled: record.enabled,
}
}
const fn root_provisioner_view_to_record(
view: RootDelegationRenewalProvisioner,
) -> RootProvisionerRecord {
RootProvisionerRecord {
principal: view.principal,
enabled: view.enabled,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
domain::policy::auth::{
RootDelegatedRoleGrantPolicy, RootDelegationAudiencePolicy, RootDelegationRenewalBatch,
RootIssuerPolicy, RootIssuerRenewalAttempt, RootIssuerRenewalAttemptStatus,
RootIssuerRenewalOutcome, RootIssuerRenewalProofRef, RootIssuerRenewalState,
RootIssuerRenewalTemplate,
},
dto::auth::{
ChainKeyAlgorithm, ChainKeyBatchHeaderV1, ChainKeyBatchWitnessStepV1,
ChainKeyBatchWitnessV1, ChainKeyDelegationCertV1, ChainKeyKeyId,
ChainKeyRootSignatureV1, DelegatedAuthIssuerPolicySnapshotV1,
DelegatedAuthRegistrySnapshotV1, DelegatedRoleGrant, DelegationAudience,
DelegationCert, DelegationProof, IcCanisterSignatureProofV1,
IcChainKeyBatchSignatureProofV1, IssuerProofAlgorithm, IssuerProofBinding,
RootKeyPolicyV1, RootProof, RootProofMode,
},
ids::{BuildNetwork, CanisterRole},
};
fn p(id: u8) -> Principal {
Principal::from_slice(&[id; 29])
}
fn active_proof() -> ActiveDelegationProof {
let issuer_proof_alg = IssuerProofAlgorithm::IcCanisterSignatureV1;
let issuer_proof_binding = IssuerProofBinding::IcCanisterSignatureV1 { seed_hash: [5; 32] };
ActiveDelegationProof {
proof: DelegationProof {
cert: DelegationCert {
root_pid: p(1),
issuer_pid: p(2),
issuer_proof_alg,
issuer_proof_binding_hash: [6; 32],
issuer_proof_binding,
issued_at_ns: 10,
not_before_ns: 20,
expires_at_ns: 100,
max_token_ttl_ns: 30,
aud: DelegationAudience::CanicSubnet(p(7)),
grants: vec![DelegatedRoleGrant {
target: CanisterRole::owned("project_instance".to_string()),
scopes: vec!["read".to_string(), "write".to_string()],
}],
},
root_proof: RootProof::IcCanisterSignatureV1(IcCanisterSignatureProofV1 {
signature_cbor: vec![8; 64],
public_key_der: vec![9; 32],
}),
},
cert_hash: [10; 32],
not_before_ns: 20,
expires_at_ns: 100,
refresh_after_ns: 80,
installed_at_ns: 15,
installed_by: p(11),
}
}
fn chain_key_active_proof() -> ActiveDelegationProof {
let mut proof = active_proof();
proof.proof.root_proof = RootProof::IcChainKeyBatchSignatureV1(chain_key_root_proof(
proof.proof.cert.root_pid,
proof.proof.cert.issuer_pid,
));
proof.cert_hash = [44; 32];
proof
}
fn chain_key_root_proof(
root_canister_id: Principal,
issuer_canister_id: Principal,
) -> IcChainKeyBatchSignatureProofV1 {
let key_id = ChainKeyKeyId {
name: "test_key_1".to_string(),
};
IcChainKeyBatchSignatureProofV1 {
header: ChainKeyBatchHeaderV1 {
schema_version: 1,
root_canister_id,
batch_id: [31; 32],
proof_epoch: 2,
registry_epoch: 3,
registry_hash: [32; 32],
tree_root: [33; 32],
not_before_ns: 20,
expires_at_ns: 100,
algorithm: ChainKeyAlgorithm::EcdsaSecp256k1,
key_id: key_id.clone(),
derivation_path_hash: [34; 32],
key_version: 4,
},
delegation_cert: ChainKeyDelegationCertV1 {
root_canister_id,
issuer_canister_id,
proof_epoch: 2,
issuer_proof_algorithm: IssuerProofAlgorithm::IcCanisterSignatureV1,
issuer_proof_binding_hash: [35; 32],
issuer_proof_binding: IssuerProofBinding::IcCanisterSignatureV1 {
seed_hash: [36; 32],
},
max_token_ttl_ns: 30,
audience: DelegationAudience::CanicSubnet(p(7)),
grants: vec![DelegatedRoleGrant {
target: CanisterRole::owned("project_instance".to_string()),
scopes: vec!["read".to_string(), "write".to_string()],
}],
not_before_ns: 20,
expires_at_ns: 100,
registry_epoch: 3,
registry_hash: [32; 32],
},
issuer_witness: ChainKeyBatchWitnessV1 {
steps: vec![
ChainKeyBatchWitnessStepV1::LeftSibling([37; 32]),
ChainKeyBatchWitnessStepV1::RightSibling([38; 32]),
],
},
signature: ChainKeyRootSignatureV1 {
algorithm: ChainKeyAlgorithm::EcdsaSecp256k1,
key_id,
derivation_path: vec![b"canic".to_vec(), b"delegation".to_vec()],
public_key: vec![39; 33],
signature: vec![40; 64],
},
}
}
fn root_key_policy() -> RootKeyPolicyV1 {
RootKeyPolicyV1 {
root_canister_id: p(1),
proof_mode: RootProofMode::ChainKeyBatch,
algorithm: ChainKeyAlgorithm::EcdsaSecp256k1,
key_id: ChainKeyKeyId {
name: "test_key_1".to_string(),
},
derivation_path_hash: [50; 32],
public_key: vec![51; 33],
key_version: 4,
min_accepted_key_version: 4,
min_accepted_proof_epoch: 2,
min_accepted_registry_epoch: 3,
max_revocation_latency_ns: 600,
valid_from_ns: 20,
accept_until_ns: 1_000,
build_network: BuildNetwork::Local,
}
}
fn registry_snapshot() -> DelegatedAuthRegistrySnapshotV1 {
DelegatedAuthRegistrySnapshotV1 {
schema_version: 1,
root_canister_id: p(1),
registry_epoch: 3,
proof_mode: RootProofMode::ChainKeyBatch,
root_key_policy_hash: [52; 32],
issuer_policies: vec![registry_issuer_policy(p(2)), registry_issuer_policy(p(3))],
}
}
fn registry_issuer_policy(
issuer_canister_id: Principal,
) -> DelegatedAuthIssuerPolicySnapshotV1 {
DelegatedAuthIssuerPolicySnapshotV1 {
issuer_canister_id,
enabled: true,
preferred_proof_mode: RootProofMode::ChainKeyBatch,
allowed_audiences: vec![DelegationAudience::CanicSubnet(p(7))],
allowed_grants: vec![DelegatedRoleGrant {
target: CanisterRole::owned("project_instance".to_string()),
scopes: vec!["read".to_string(), "write".to_string()],
}],
max_root_proof_ttl_ns: 600,
max_token_ttl_ns: 30,
issuer_proof_algorithm: IssuerProofAlgorithm::IcCanisterSignatureV1,
issuer_proof_binding_hash: [53; 32],
renewal_template_hash: [54; 32],
}
}
#[test]
fn active_delegation_proof_round_trips_and_filters_by_time() {
AuthStateOps::clear_active_delegation_proof();
let proof = active_proof();
AuthStateOps::set_active_delegation_proof(proof.clone());
assert_eq!(AuthStateOps::active_delegation_proof(19), None);
assert_eq!(AuthStateOps::active_delegation_proof(20), Some(proof));
assert!(AuthStateOps::active_delegation_proof(99).is_some());
assert_eq!(AuthStateOps::active_delegation_proof(100), None);
AuthStateOps::clear_active_delegation_proof();
assert_eq!(AuthStateOps::active_delegation_proof(20), None);
}
#[test]
fn chain_key_active_delegation_proof_round_trips_through_auth_state() {
AuthStateOps::clear_active_delegation_proof();
let proof = chain_key_active_proof();
AuthStateOps::set_active_delegation_proof(proof.clone());
assert_eq!(AuthStateOps::active_delegation_proof(20), Some(proof));
AuthStateOps::clear_active_delegation_proof();
}
#[test]
fn delegated_auth_proof_epoch_advances_monotonically_from_minimum() {
let before = AuthStateOps::delegated_auth_proof_epoch();
let minimum = before.saturating_add(5);
let first = AuthStateOps::advance_delegated_auth_proof_epoch_at_least(minimum);
let second = AuthStateOps::advance_delegated_auth_proof_epoch_at_least(1);
assert_eq!(first, minimum);
assert_eq!(second, first.saturating_add(1));
assert_eq!(AuthStateOps::delegated_auth_proof_epoch(), second);
}
#[test]
fn chain_key_policy_and_registry_snapshot_round_trip_through_records() {
let policy = root_key_policy();
let policy_record = mapper::RootKeyPolicyRecordMapper::dto_to_record(policy.clone());
assert_eq!(
mapper::RootKeyPolicyRecordMapper::record_to_dto(policy_record),
policy
);
let snapshot = registry_snapshot();
let snapshot_record =
mapper::DelegatedAuthRegistrySnapshotRecordMapper::dto_to_record(snapshot.clone());
assert_eq!(
mapper::DelegatedAuthRegistrySnapshotRecordMapper::record_to_dto(snapshot_record),
snapshot
);
}
#[test]
fn root_issuer_policy_round_trips_through_auth_state() {
let policy = RootIssuerPolicy {
issuer_pid: p(31),
enabled: true,
allowed_audiences: vec![
RootDelegationAudiencePolicy::Canister(p(32)),
RootDelegationAudiencePolicy::CanicSubnet(p(33)),
RootDelegationAudiencePolicy::Project("test".to_string()),
],
allowed_grants: vec![RootDelegatedRoleGrantPolicy {
target: CanisterRole::owned("project_instance".to_string()),
scopes: vec!["canic.issue".to_string(), "canic.read".to_string()],
}],
max_cert_ttl_ns: 120_000_000_000,
refresh_after_ratio_bps: 8_000,
};
AuthStateOps::upsert_root_issuer_policy(policy.clone());
assert_eq!(AuthStateOps::root_issuer_policy(p(31)), Some(policy));
assert_eq!(AuthStateOps::root_issuer_policy(p(34)), None);
}
#[test]
fn root_issuer_renewal_template_round_trips_through_auth_state() {
let template = RootIssuerRenewalTemplate {
issuer_pid: p(41),
enabled: true,
audience: RootDelegationAudiencePolicy::Project("test".to_string()),
grants: vec![RootDelegatedRoleGrantPolicy {
target: CanisterRole::owned("project_instance".to_string()),
scopes: vec!["canic.read".to_string()],
}],
cert_ttl_ns: 120_000_000_000,
};
AuthStateOps::upsert_root_issuer_renewal_template(template.clone());
assert_eq!(
AuthStateOps::root_issuer_renewal_template(p(41)),
Some(template)
);
assert_eq!(AuthStateOps::root_issuer_renewal_template(p(42)), None);
}
#[test]
fn root_issuer_renewal_state_round_trips_through_auth_state() {
let state = RootIssuerRenewalState {
issuer_pid: p(51),
template_fingerprint: [1; 32],
last_installed_cert_hash: Some([2; 32]),
last_installed_expires_at_ns: Some(200),
last_installed_refresh_after_ns: Some(160),
active_attempt_id: Some([3; 32]),
last_outcome: RootIssuerRenewalOutcome::RetrievalExpired,
consecutive_failures: 2,
next_attempt_after_ns: 90,
updated_at_ns: 80,
};
AuthStateOps::upsert_root_issuer_renewal_state(state.clone());
assert_eq!(AuthStateOps::root_issuer_renewal_state(p(51)), Some(state));
assert_eq!(AuthStateOps::root_issuer_renewal_state(p(52)), None);
}
#[test]
fn root_issuer_renewal_attempt_round_trips_through_auth_state() {
let attempt = RootIssuerRenewalAttempt {
attempt_id: [4; 32],
issuer_pid: p(61),
template_fingerprint: [5; 32],
batch_id: [6; 32],
proof_ref: RootIssuerRenewalProofRef {
issuer_pid: p(61),
cert_hash: [7; 32],
},
status: RootIssuerRenewalAttemptStatus::Prepared,
prepared_at_ns: 10,
retrieval_expires_at_ns: 70,
install_deadline_ns: 90,
prepared_cert_hash: [7; 32],
prepared_expires_at_ns: 200,
prepared_refresh_after_ns: 160,
failure: Some(RootIssuerRenewalOutcome::RetrievalExpired),
};
AuthStateOps::upsert_root_issuer_renewal_attempt(attempt.clone());
assert_eq!(
AuthStateOps::root_issuer_renewal_attempt([4; 32]),
Some(attempt)
);
assert_eq!(AuthStateOps::root_issuer_renewal_attempt([8; 32]), None);
}
#[test]
fn root_delegation_renewal_batch_round_trips_through_auth_state() {
let batch = RootDelegationRenewalBatch {
batch_id: [9; 32],
attempt_ids: vec![[10; 32], [11; 32]],
prepared_at_ns: 20,
retrieval_expires_at_ns: 80,
};
AuthStateOps::upsert_root_delegation_renewal_batch(batch.clone());
assert_eq!(
AuthStateOps::root_delegation_renewal_batch([9; 32]),
Some(batch)
);
assert_eq!(
AuthStateOps::root_delegation_renewal_batches()
.into_iter()
.filter(|candidate| candidate.batch_id == [9; 32])
.count(),
1
);
assert_eq!(AuthStateOps::root_delegation_renewal_batch([12; 32]), None);
}
#[test]
fn root_delegation_renewal_batch_prune_removes_expired_batches() {
let expired_batch = RootDelegationRenewalBatch {
batch_id: [13; 32],
attempt_ids: vec![[14; 32]],
prepared_at_ns: 10,
retrieval_expires_at_ns: 20,
};
let fresh_batch = RootDelegationRenewalBatch {
batch_id: [15; 32],
attempt_ids: vec![[16; 32]],
prepared_at_ns: 10,
retrieval_expires_at_ns: 21,
};
AuthStateOps::upsert_root_delegation_renewal_batch(expired_batch);
AuthStateOps::upsert_root_delegation_renewal_batch(fresh_batch.clone());
assert_eq!(AuthStateOps::prune_root_delegation_renewal_batches(20), 1);
assert_eq!(AuthStateOps::root_delegation_renewal_batch([13; 32]), None);
assert_eq!(
AuthStateOps::root_delegation_renewal_batch([15; 32]),
Some(fresh_batch)
);
}
#[test]
fn root_delegation_renewal_provisioner_round_trips_through_auth_state() {
let provisioner = RootDelegationRenewalProvisioner {
principal: p(61),
enabled: true,
};
AuthStateOps::upsert_root_delegation_renewal_provisioner(provisioner);
assert_eq!(
AuthStateOps::root_delegation_renewal_provisioner(p(61)),
Some(provisioner)
);
assert!(AuthStateOps::is_root_delegation_renewal_provisioner(p(61)));
assert!(!AuthStateOps::is_root_delegation_renewal_provisioner(p(62)));
AuthStateOps::upsert_root_delegation_renewal_provisioner(
RootDelegationRenewalProvisioner {
principal: p(61),
enabled: false,
},
);
assert!(!AuthStateOps::is_root_delegation_renewal_provisioner(p(61)));
}
}