use blindplane_access::{
AccessError, AccessGrant, AccessIssuer, AccessValidationPolicy, GrantSpec, Permissions,
PrincipalKeypair, PrincipalKind, RevocationSpec, RevocationState, RoleKeypair,
};
fn principal(id: &str, seed: u8) -> PrincipalKeypair {
PrincipalKeypair::from_secret_bytes(
"acme",
id,
PrincipalKind::User,
1,
[seed; 32],
[seed.wrapping_add(1); 32],
)
.unwrap()
}
fn grant_spec() -> GrantSpec {
GrantSpec {
grant_id: "grant-alice-audit".into(),
scope: "audit".into(),
permissions: Permissions::READ | Permissions::OBSERVE,
authorization_epoch: 7,
not_before: 100,
not_after: 200,
}
}
fn revocation(
issuer: &AccessIssuer,
minimum_authorization_epoch: u64,
minimum_role_key_epoch: u64,
) -> RevocationState {
RevocationState::issue(
issuer,
"acme",
"alice",
"audit",
RevocationSpec {
revision: 3,
minimum_authorization_epoch,
minimum_role_key_epoch,
issued_at: 90,
},
)
.unwrap()
}
#[test]
fn current_grant_restores_the_exact_role_key() {
let alice = principal("alice", 7);
let issuer = AccessIssuer::from_seed("tenant-admin", [11; 32]).unwrap();
let role =
RoleKeypair::from_secret_bytes("acme", "audit", "tenant-audit", 4, [13; 32]).unwrap();
let grant = AccessGrant::issue(&issuer, alice.principal(), &role, grant_spec()).unwrap();
let encoded = grant.encode();
let decoded = AccessGrant::decode(&encoded, &AccessValidationPolicy::default()).unwrap();
assert_eq!(decoded, grant);
let state = revocation(&issuer, 7, 4);
let verified = state
.verify(&issuer.trusted(), "acme", "alice", "audit")
.unwrap();
let opened = decoded
.open_role(&alice, &issuer.trusted(), 150, &verified)
.unwrap();
assert_eq!(opened.role_id(), "tenant-audit");
assert_eq!(opened.key_epoch(), 4);
assert_eq!(opened.public_key(), role.public_key());
assert!(grant.permissions().contains(Permissions::READ));
assert!(grant.permissions().contains(Permissions::OBSERVE));
assert!(!grant.permissions().contains(Permissions::ADMINISTER));
}
#[test]
fn wrong_subject_and_revoked_epochs_cannot_open_a_role() {
let alice = principal("alice", 7);
let bob = principal("bob", 17);
let issuer = AccessIssuer::from_seed("tenant-admin", [11; 32]).unwrap();
let role =
RoleKeypair::from_secret_bytes("acme", "audit", "tenant-audit", 4, [13; 32]).unwrap();
let grant = AccessGrant::issue(&issuer, alice.principal(), &role, grant_spec()).unwrap();
let current_state = revocation(&issuer, 7, 4);
let current = current_state
.verify(&issuer.trusted(), "acme", "alice", "audit")
.unwrap();
assert_eq!(
grant.open_role(&bob, &issuer.trusted(), 150, ¤t),
Err(AccessError::SubjectMismatch)
);
let auth_state = revocation(&issuer, 8, 4);
let newer_authorization = auth_state
.verify(&issuer.trusted(), "acme", "alice", "audit")
.unwrap();
assert_eq!(
grant.open_role(&alice, &issuer.trusted(), 150, &newer_authorization),
Err(AccessError::Revoked)
);
let role_state = revocation(&issuer, 7, 5);
let newer_role = role_state
.verify(&issuer.trusted(), "acme", "alice", "audit")
.unwrap();
assert_eq!(
grant.open_role(&alice, &issuer.trusted(), 150, &newer_role),
Err(AccessError::Revoked)
);
}
#[test]
fn grant_and_revocation_signatures_are_verified_during_decode() {
let alice = principal("alice", 7);
let issuer = AccessIssuer::from_seed("tenant-admin", [11; 32]).unwrap();
let role =
RoleKeypair::from_secret_bytes("acme", "audit", "tenant-audit", 4, [13; 32]).unwrap();
let grant = AccessGrant::issue(&issuer, alice.principal(), &role, grant_spec()).unwrap();
let mut grant_bytes = grant.encode();
*grant_bytes.last_mut().unwrap() ^= 1;
assert_eq!(
AccessGrant::decode(&grant_bytes, &AccessValidationPolicy::default()),
Err(AccessError::InvalidSignature)
);
let mut revocation_bytes = revocation(&issuer, 7, 4).encode();
*revocation_bytes.last_mut().unwrap() ^= 1;
assert_eq!(
RevocationState::decode(&revocation_bytes, &AccessValidationPolicy::default()),
Err(AccessError::InvalidSignature)
);
}
#[test]
fn grants_require_permissions_and_a_valid_time_window() {
let alice = principal("alice", 7);
let issuer = AccessIssuer::from_seed("tenant-admin", [11; 32]).unwrap();
let role =
RoleKeypair::from_secret_bytes("acme", "audit", "tenant-audit", 4, [13; 32]).unwrap();
let mut invalid = grant_spec();
invalid.permissions = Permissions::NONE;
assert_eq!(
AccessGrant::issue(&issuer, alice.principal(), &role, invalid),
Err(AccessError::InvalidPermissions)
);
let mut invalid = grant_spec();
invalid.not_before = 201;
assert_eq!(
AccessGrant::issue(&issuer, alice.principal(), &role, invalid),
Err(AccessError::InvalidEpoch)
);
}