use buffa::EnumValue;
use polyc_proto::proto::polychrome::state::v1 as pb;
use polyc_state::{
command::{CommandEnvelope, CommandMetadata, ResourceBounds},
digest::ContentDigest,
error::StateError,
id::{Audience, CommandId, NamespaceId, Purpose},
revision::Revision,
sessions::{
ExpirationEntry, ExpirationKind, ExpirationShard, SessionCommand, SessionFamily,
SessionOperation, SessionPrincipal, SessionRoot, session_scope,
},
versioned::{
EntryExpectation, MAX_MUTATIONS_PER_TRANSACTION, MAX_TRANSACTION_PAYLOAD_BYTES,
authority::{AuthorizationEpoch, PersonaId, SessionId, SessionRecord},
},
};
use crate::wire::{fixed_bytes, malformed, required};
fn expected_to_wire(value: EntryExpectation) -> pb::SessionExpectedEntry {
use pb::__buffa::oneof::session_expected_entry::Expected;
let expected = match value {
EntryExpectation::Absent => Expected::from(pb::SessionExpectedAbsent {
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
EntryExpectation::Revision(revision) => Expected::from(pb::SessionExpectedRevision {
revision: revision.get(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
};
pb::SessionExpectedEntry {
expected: Some(expected),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
fn expected_from_wire(value: pb::SessionExpectedEntry) -> Result<EntryExpectation, StateError> {
use pb::__buffa::oneof::session_expected_entry::Expected;
match value.expected {
Some(Expected::Absent(_)) => Ok(EntryExpectation::Absent),
Some(Expected::Revision(value)) => {
Ok(EntryExpectation::Revision(Revision::new(value.revision)))
}
None => Err(malformed(
"expected",
"a session operation declares its exact row premise",
)),
}
}
pub(crate) fn principal_to_wire(value: &SessionPrincipal) -> pb::SessionPrincipal {
use pb::__buffa::oneof::session_principal::Principal;
let principal = match value {
SessionPrincipal::Persona(persona) => Principal::Persona(persona.as_str().to_owned()),
SessionPrincipal::Wallet(address) => Principal::WalletAddress(address.clone()),
};
pb::SessionPrincipal {
principal: Some(principal),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn principal_from_wire(
value: pb::SessionPrincipal,
) -> Result<SessionPrincipal, StateError> {
use pb::__buffa::oneof::session_principal::Principal;
match value.principal {
Some(Principal::Persona(value)) => Ok(SessionPrincipal::Persona(PersonaId::new(value))),
Some(Principal::WalletAddress(value)) => Ok(SessionPrincipal::Wallet(value)),
None => Err(malformed(
"principal",
"a session principal names a persona or wallet",
)),
}
}
fn root_to_wire(value: &SessionRoot) -> pb::SessionRoot {
use pb::__buffa::oneof::session_root::Root;
let root = match value {
SessionRoot::PersonaCredential { persona } => Root::Persona(persona.as_str().to_owned()),
SessionRoot::Wallet { address } => Root::WalletAddress(address.clone()),
};
pb::SessionRoot {
root: Some(root),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
fn root_from_wire(value: pb::SessionRoot) -> Result<SessionRoot, StateError> {
use pb::__buffa::oneof::session_root::Root;
match value.root {
Some(Root::Persona(value)) => Ok(SessionRoot::PersonaCredential {
persona: PersonaId::new(value),
}),
Some(Root::WalletAddress(value)) => Ok(SessionRoot::Wallet { address: value }),
None => Err(malformed(
"root",
"a session family names one credential root",
)),
}
}
pub(crate) fn family_to_wire(value: &SessionFamily) -> pb::StateSessionFamily {
pb::StateSessionFamily {
family_id: value.id().as_str().to_owned(),
generation: value.generation(),
previous_rotated_at_ms: value.previous_rotated_at_ms(),
root: buffa::MessageField::some(root_to_wire(value.root())),
created_at_ms: value.created_at_ms(),
last_seen_at_ms: value.last_seen_at_ms(),
absolute_expires_at_ms: value.absolute_expires_at_ms(),
revoked_at_ms: value.revoked_at_ms(),
revoked_reason: value.revoked_reason().to_owned(),
device_public_key: value.device_public_key().to_vec(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn family_from_wire(value: pb::StateSessionFamily) -> Result<SessionFamily, StateError> {
Ok(SessionFamily::new(
SessionId::new(value.family_id),
value.generation,
value.previous_rotated_at_ms,
root_from_wire(required(
"root",
"a session family carries its credential root",
value.root,
)?)?,
value.created_at_ms,
value.last_seen_at_ms,
value.absolute_expires_at_ms,
value.revoked_at_ms,
value.revoked_reason,
value.device_public_key,
))
}
pub(crate) fn expiration_to_wire(value: &ExpirationShard) -> pb::SessionExpirationShard {
pb::SessionExpirationShard {
entries: value
.entries()
.iter()
.map(|entry| pb::SessionExpirationEntry {
expires_at_ms: entry.expires_at_ms(),
kind: EnumValue::Known(match entry.kind() {
ExpirationKind::Family => pb::SessionExpirationKind::Family,
ExpirationKind::BearerSession => pb::SessionExpirationKind::Bearer,
}),
id: entry.id().as_str().to_owned(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
})
.collect(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn expiration_from_wire(
value: pb::SessionExpirationShard,
) -> Result<ExpirationShard, StateError> {
value
.entries
.into_iter()
.map(|entry| {
let kind = match entry.kind {
EnumValue::Known(pb::SessionExpirationKind::Family) => ExpirationKind::Family,
EnumValue::Known(pb::SessionExpirationKind::Bearer) => {
ExpirationKind::BearerSession
}
EnumValue::Known(pb::SessionExpirationKind::Unspecified)
| EnumValue::Unknown(_) => {
return Err(malformed(
"expiration_kind",
"an expiration entry names a known nonzero kind",
));
}
};
Ok(ExpirationEntry::new(
entry.expires_at_ms,
kind,
SessionId::new(entry.id),
))
})
.collect::<Result<Vec<_>, _>>()
.map(ExpirationShard::new)
}
pub(crate) fn record_to_wire(value: SessionRecord) -> pb::StateBearerRecord {
pb::StateBearerRecord {
revoked: value.is_revoked(),
minted_epoch: value.minted_epoch().get(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) const fn record_from_wire(value: &pb::StateBearerRecord) -> SessionRecord {
SessionRecord::new(value.revoked, AuthorizationEpoch::new(value.minted_epoch))
}
#[allow(clippy::too_many_lines)]
pub(crate) fn operation_to_wire(value: &SessionOperation) -> pb::SessionOperation {
use pb::__buffa::oneof::session_operation::Operation;
let operation = match value {
SessionOperation::CreateFamily {
family,
family_expected,
expiration,
expiration_expected,
} => Operation::from(pb::CreateSessionFamily {
family: buffa::MessageField::some(family_to_wire(family)),
family_expected: buffa::MessageField::some(expected_to_wire(*family_expected)),
expiration: buffa::MessageField::some(expiration_to_wire(expiration)),
expiration_expected: buffa::MessageField::some(expected_to_wire(*expiration_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
SessionOperation::RotateFamily {
presented_generation,
now_ms,
family,
family_expected,
} => Operation::from(pb::RotateSessionFamily {
presented_generation: *presented_generation,
now_ms: *now_ms,
family: buffa::MessageField::some(family_to_wire(family)),
family_expected: buffa::MessageField::some(expected_to_wire(*family_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
SessionOperation::TombstoneReuse {
presented_generation,
now_ms,
family,
family_expected,
} => Operation::from(pb::TombstoneSessionReuse {
presented_generation: *presented_generation,
now_ms: *now_ms,
family: buffa::MessageField::some(family_to_wire(family)),
family_expected: buffa::MessageField::some(expected_to_wire(*family_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
SessionOperation::RevokeFamily {
now_ms,
reason,
family,
family_expected,
} => Operation::from(pb::RevokeSessionFamily {
now_ms: *now_ms,
reason: reason.clone(),
family: buffa::MessageField::some(family_to_wire(family)),
family_expected: buffa::MessageField::some(expected_to_wire(*family_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
SessionOperation::ReapFamily {
now_ms,
family,
family_expected,
expiration,
expiration_expected,
} => Operation::from(pb::ReapSessionFamily {
now_ms: *now_ms,
family: buffa::MessageField::some(family_to_wire(family)),
family_expected: buffa::MessageField::some(expected_to_wire(*family_expected)),
expiration: buffa::MessageField::some(expiration_to_wire(expiration)),
expiration_expected: buffa::MessageField::some(expected_to_wire(*expiration_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
SessionOperation::MintBearer {
principal,
session,
record,
expires_at_ms,
session_expected,
epoch_expected,
expiration,
expiration_expected,
} => Operation::from(pb::MintBearerSession {
principal: buffa::MessageField::some(principal_to_wire(principal)),
session: session.as_str().to_owned(),
record: buffa::MessageField::some(record_to_wire(*record)),
expires_at_ms: *expires_at_ms,
session_expected: buffa::MessageField::some(expected_to_wire(*session_expected)),
epoch_expected: buffa::MessageField::some(expected_to_wire(*epoch_expected)),
expiration: buffa::MessageField::some(expiration_to_wire(expiration)),
expiration_expected: buffa::MessageField::some(expected_to_wire(*expiration_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
SessionOperation::RevokeBearer {
session,
record,
session_expected,
} => Operation::from(pb::RevokeBearerSession {
session: session.as_str().to_owned(),
record: buffa::MessageField::some(record_to_wire(*record)),
session_expected: buffa::MessageField::some(expected_to_wire(*session_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
SessionOperation::BumpAuthorizationEpoch {
principal,
epoch,
epoch_expected,
} => Operation::from(pb::BumpSessionAuthorizationEpoch {
principal: buffa::MessageField::some(principal_to_wire(principal)),
epoch: epoch.get(),
epoch_expected: buffa::MessageField::some(expected_to_wire(*epoch_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
SessionOperation::ReapBearer {
now_ms,
session,
session_expected,
expiration,
expiration_expected,
} => Operation::from(pb::ReapBearerSession {
now_ms: *now_ms,
session: session.as_str().to_owned(),
session_expected: buffa::MessageField::some(expected_to_wire(*session_expected)),
expiration: buffa::MessageField::some(expiration_to_wire(expiration)),
expiration_expected: buffa::MessageField::some(expected_to_wire(*expiration_expected)),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}),
};
pb::SessionOperation {
operation: Some(operation),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
#[allow(clippy::too_many_lines)]
pub(crate) fn operation_from_wire(
value: pb::SessionOperation,
) -> Result<SessionOperation, StateError> {
use pb::__buffa::oneof::session_operation::Operation;
let expected = |field, reason, value| expected_from_wire(required(field, reason, value)?);
Ok(match value.operation {
Some(Operation::CreateFamily(value)) => SessionOperation::CreateFamily {
family: family_from_wire(required("family", "create carries a family", value.family)?)?,
family_expected: expected(
"family_expected",
"create carries the family premise",
value.family_expected,
)?,
expiration: expiration_from_wire(required(
"expiration",
"create carries its resulting expiry shard",
value.expiration,
)?)?,
expiration_expected: expected(
"expiration_expected",
"create carries the prior expiry premise",
value.expiration_expected,
)?,
},
Some(Operation::RotateFamily(value)) => SessionOperation::RotateFamily {
presented_generation: value.presented_generation,
now_ms: value.now_ms,
family: family_from_wire(required(
"family",
"rotate carries the resulting family",
value.family,
)?)?,
family_expected: expected(
"family_expected",
"rotate carries the family premise",
value.family_expected,
)?,
},
Some(Operation::TombstoneReuse(value)) => SessionOperation::TombstoneReuse {
presented_generation: value.presented_generation,
now_ms: value.now_ms,
family: family_from_wire(required(
"family",
"reuse carries the tombstone",
value.family,
)?)?,
family_expected: expected(
"family_expected",
"reuse carries the family premise",
value.family_expected,
)?,
},
Some(Operation::RevokeFamily(value)) => SessionOperation::RevokeFamily {
now_ms: value.now_ms,
reason: value.reason,
family: family_from_wire(required(
"family",
"revoke carries the tombstone",
value.family,
)?)?,
family_expected: expected(
"family_expected",
"revoke carries the family premise",
value.family_expected,
)?,
},
Some(Operation::ReapFamily(value)) => SessionOperation::ReapFamily {
now_ms: value.now_ms,
family: family_from_wire(required(
"family",
"reap carries the removed family",
value.family,
)?)?,
family_expected: expected(
"family_expected",
"reap carries the family premise",
value.family_expected,
)?,
expiration: expiration_from_wire(required(
"expiration",
"reap carries the resulting expiry shard",
value.expiration,
)?)?,
expiration_expected: expected(
"expiration_expected",
"reap carries the expiry premise",
value.expiration_expected,
)?,
},
Some(Operation::MintBearer(value)) => SessionOperation::MintBearer {
principal: principal_from_wire(required(
"principal",
"mint carries its principal",
value.principal,
)?)?,
session: SessionId::new(value.session),
record: record_from_wire(&required(
"record",
"mint carries its record",
value.record,
)?),
expires_at_ms: value.expires_at_ms,
session_expected: expected(
"session_expected",
"mint carries the bearer premise",
value.session_expected,
)?,
epoch_expected: expected(
"epoch_expected",
"mint carries the epoch premise",
value.epoch_expected,
)?,
expiration: expiration_from_wire(required(
"expiration",
"mint carries the resulting expiry shard",
value.expiration,
)?)?,
expiration_expected: expected(
"expiration_expected",
"mint carries the expiry premise",
value.expiration_expected,
)?,
},
Some(Operation::RevokeBearer(value)) => SessionOperation::RevokeBearer {
session: SessionId::new(value.session),
record: record_from_wire(&required(
"record",
"revoke carries its record",
value.record,
)?),
session_expected: expected(
"session_expected",
"revoke carries the bearer premise",
value.session_expected,
)?,
},
Some(Operation::BumpEpoch(value)) => SessionOperation::BumpAuthorizationEpoch {
principal: principal_from_wire(required(
"principal",
"epoch bump carries its principal",
value.principal,
)?)?,
epoch: AuthorizationEpoch::new(value.epoch),
epoch_expected: expected(
"epoch_expected",
"epoch bump carries its premise",
value.epoch_expected,
)?,
},
Some(Operation::ReapBearer(value)) => SessionOperation::ReapBearer {
now_ms: value.now_ms,
session: SessionId::new(value.session),
session_expected: expected(
"session_expected",
"reap carries the bearer premise",
value.session_expected,
)?,
expiration: expiration_from_wire(required(
"expiration",
"reap carries the resulting expiry shard",
value.expiration,
)?)?,
expiration_expected: expected(
"expiration_expected",
"reap carries the expiry premise",
value.expiration_expected,
)?,
},
None => {
return Err(malformed(
"operation",
"a session command names one operation",
));
}
})
}
pub(crate) fn metadata_to_wire(command: &SessionCommand) -> pb::SessionCommandMetadata {
let value = command.metadata();
pb::SessionCommandMetadata {
command_id: value.command_id().as_str().to_owned(),
namespace: value.scope().namespace().as_str().to_owned(),
purpose: value.envelope().purpose().as_str().to_owned(),
command_audience: value.envelope().audience().as_str().to_owned(),
digest: value.digest().as_bytes().to_vec(),
__buffa_unknown_fields: buffa::UnknownFields::default(),
}
}
pub(crate) fn command_from_wire(
metadata: pb::SessionCommandMetadata,
operation: pb::SessionOperation,
) -> Result<SessionCommand, StateError> {
let namespace = NamespaceId::new(metadata.namespace);
let digest = ContentDigest::from_bytes(fixed_bytes::<{ ContentDigest::LEN }>(
"digest",
&metadata.digest,
)?);
Ok(SessionCommand::new(
CommandMetadata::new(
CommandId::new(metadata.command_id),
polyc_state::versioned::family(),
digest,
session_scope(&namespace),
CommandEnvelope::new(
Purpose::new(metadata.purpose),
Audience::new(metadata.command_audience),
ResourceBounds::new(MAX_TRANSACTION_PAYLOAD_BYTES, MAX_MUTATIONS_PER_TRANSACTION),
),
),
operation_from_wire(operation)?,
))
}
#[cfg(test)]
mod tests {
use super::*;
use polyc_state::{revision::Revision, versioned::EntryExpectation};
#[test]
fn roots_principals_and_operations_round_trip_and_missing_oneof_fails_closed() {
for principal in [
SessionPrincipal::Persona(PersonaId::new("p")),
SessionPrincipal::Wallet("0xabc".into()),
] {
assert_eq!(
principal_from_wire(principal_to_wire(&principal)).unwrap(),
principal
);
}
let family = SessionFamily::initial(
SessionId::new("f"),
SessionRoot::PersonaCredential {
persona: PersonaId::new("p"),
},
1,
);
let operation = SessionOperation::RotateFamily {
presented_generation: 1,
now_ms: 2,
family,
family_expected: EntryExpectation::Revision(Revision::new(1)),
};
assert_eq!(
operation_from_wire(operation_to_wire(&operation)).unwrap(),
operation
);
assert!(
operation_from_wire(pb::SessionOperation {
operation: None,
__buffa_unknown_fields: buffa::UnknownFields::default()
})
.is_err()
);
}
}