use std::{collections::HashMap, fmt, path::Path, str::FromStr};
use rpki::crypto::{KeyIdentifier, PublicKey};
use crate::{
commons::{
actor::Actor,
api::CommandSummary,
crypto::SignerHandle,
error::Error,
eventsourcing::{Aggregate, AggregateStore, CommandDetails, SentCommand, StoredEvent, WithStorableDetails},
KrillResult,
},
constants::{ACTOR_DEF_KRILL, SIGNERS_DIR},
};
type InitSignerInfoEvent = StoredEvent<InitSignerInfoDetails>;
impl InitSignerInfoEvent {
pub fn init(
id: &SignerHandle,
signer_name: &str,
signer_info: &str,
public_key: &PublicKey,
private_key_internal_id: &str,
) -> Self {
StoredEvent::new(
id,
0,
InitSignerInfoDetails {
signer_name: signer_name.to_string(),
signer_info: signer_info.to_string(),
signer_identity: SignerIdentity {
public_key: public_key.clone(),
private_key_internal_id: private_key_internal_id.to_string(),
},
},
)
}
}
#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
struct InitSignerInfoDetails {
pub signer_name: String,
pub signer_info: String,
pub signer_identity: SignerIdentity,
}
impl fmt::Display for InitSignerInfoDetails {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Signer info initialized with name '{}'", self.signer_name)
}
}
type SignerInfoEvent = StoredEvent<SignerInfoEventDetails>;
#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
enum SignerInfoEventDetails {
KeyAdded(KeyIdentifier, String),
KeyRemoved(KeyIdentifier),
SignerNameChanged(String),
SignerInfoChanged(String),
}
impl SignerInfoEvent {
pub fn key_added(si: &SignerInfo, key_id: KeyIdentifier, internal_key_id: String) -> Self {
StoredEvent::new(
si.id(),
si.version,
SignerInfoEventDetails::KeyAdded(key_id, internal_key_id),
)
}
pub fn key_removed(si: &SignerInfo, key_id: KeyIdentifier) -> Self {
StoredEvent::new(si.id(), si.version, SignerInfoEventDetails::KeyRemoved(key_id))
}
pub fn signer_name_changed(si: &SignerInfo, signer_name: String) -> Self {
StoredEvent::new(
si.id(),
si.version,
SignerInfoEventDetails::SignerNameChanged(signer_name),
)
}
pub fn signer_info_changed(si: &SignerInfo, signer_info: String) -> Self {
StoredEvent::new(
si.id(),
si.version,
SignerInfoEventDetails::SignerInfoChanged(signer_info),
)
}
}
impl fmt::Display for SignerInfoEventDetails {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SignerInfoEventDetails::KeyAdded(key_id, internal_key_id) => write!(
f,
"added key with key id '{}' and internal key id '{}'",
key_id, internal_key_id
),
SignerInfoEventDetails::KeyRemoved(key_id) => write!(f, "removed key with key id '{}'", key_id),
SignerInfoEventDetails::SignerNameChanged(signer_name) => {
write!(f, "signer name changed to '{}'", signer_name)
}
SignerInfoEventDetails::SignerInfoChanged(signer_info) => {
write!(f, "signer info changed to '{}'", signer_info)
}
}
}
}
type SignerInfoCommand = SentCommand<SignerInfoCommandDetails>;
#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
enum SignerInfoCommandDetails {
AddKey(KeyIdentifier, String),
RemoveKey(KeyIdentifier),
ChangeSignerName(String),
ChangeSignerInfo(String),
}
impl fmt::Display for SignerInfoCommandDetails {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SignerInfoCommandDetails::AddKey(key_id, internal_key_id) => write!(
f,
"Add key with key id '{}' and internal key id '{}'",
key_id, internal_key_id
),
SignerInfoCommandDetails::RemoveKey(key_id) => write!(f, "Remove key with key id '{}'", key_id),
SignerInfoCommandDetails::ChangeSignerName(signer_name) => {
write!(f, "Change signer name to '{}'", signer_name)
}
SignerInfoCommandDetails::ChangeSignerInfo(signer_info) => {
write!(f, "Change signer info to '{}'", signer_info)
}
}
}
}
impl WithStorableDetails for SignerInfoCommandDetails {
fn summary(&self) -> CommandSummary {
match self {
SignerInfoCommandDetails::AddKey(key_id, internal_key_id) => CommandSummary::new("signer-add-key", &self)
.with_arg("key_id", key_id)
.with_arg("internal_key_id", internal_key_id),
SignerInfoCommandDetails::RemoveKey(key_id) => {
CommandSummary::new("signer-remove-key", &self).with_arg("key_id", key_id)
}
SignerInfoCommandDetails::ChangeSignerName(signer_name) => {
CommandSummary::new("signer-change-name", &self).with_arg("signer_name", signer_name)
}
SignerInfoCommandDetails::ChangeSignerInfo(signer_info) => {
CommandSummary::new("signer-change-info", &self).with_arg("signer_info", signer_info)
}
}
}
}
impl CommandDetails for SignerInfoCommandDetails {
type Event = SignerInfoEvent;
type StorableDetails = Self;
fn store(&self) -> Self::StorableDetails {
self.clone()
}
}
impl SignerInfoCommand {
pub fn add_key(id: &SignerHandle, version: Option<u64>, key_id: &KeyIdentifier, internal_key_id: &str) -> Self {
let details = SignerInfoCommandDetails::AddKey(*key_id, internal_key_id.to_string());
let actor = Actor::test_from_def(ACTOR_DEF_KRILL);
Self::new(id, version, details, &actor)
}
pub fn remove_key(id: &SignerHandle, version: Option<u64>, key_id: &KeyIdentifier) -> Self {
let details = SignerInfoCommandDetails::RemoveKey(*key_id);
let actor = Actor::test_from_def(ACTOR_DEF_KRILL);
Self::new(id, version, details, &actor)
}
pub fn change_signer_name(id: &SignerHandle, version: Option<u64>, signer_name: &str) -> Self {
let details = SignerInfoCommandDetails::ChangeSignerName(signer_name.to_string());
let actor = Actor::test_from_def(ACTOR_DEF_KRILL);
Self::new(id, version, details, &actor)
}
pub fn change_signer_info(id: &SignerHandle, version: Option<u64>, signer_info: &str) -> Self {
let details = SignerInfoCommandDetails::ChangeSignerInfo(signer_info.to_string());
let actor = Actor::test_from_def(ACTOR_DEF_KRILL);
Self::new(id, version, details, &actor)
}
}
#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
struct SignerIdentity {
public_key: PublicKey,
private_key_internal_id: String,
}
#[derive(Clone, Deserialize, Serialize)]
struct SignerInfo {
id: SignerHandle,
version: u64,
signer_name: String,
signer_info: String,
signer_identity: SignerIdentity,
keys: HashMap<KeyIdentifier, String>,
}
impl SignerInfo {
pub fn id(&self) -> &SignerHandle {
&self.id
}
}
impl Aggregate for SignerInfo {
type Command = SignerInfoCommand;
type StorableCommandDetails = SignerInfoCommandDetails;
type Event = SignerInfoEvent;
type InitEvent = InitSignerInfoEvent;
type Error = Error;
fn init(event: InitSignerInfoEvent) -> Result<Self, Self::Error> {
let (id, _version, init) = event.unpack();
Ok(SignerInfo {
id,
version: 1,
signer_name: init.signer_name,
signer_info: init.signer_info,
signer_identity: init.signer_identity,
keys: HashMap::new(),
})
}
fn version(&self) -> u64 {
self.version
}
fn apply(&mut self, event: SignerInfoEvent) {
match event.into_details() {
SignerInfoEventDetails::KeyAdded(key_id, internal_key_id) => {
self.keys.insert(key_id, internal_key_id);
}
SignerInfoEventDetails::KeyRemoved(key_id) => {
let _ = self.keys.remove(&key_id);
}
SignerInfoEventDetails::SignerNameChanged(signer_name) => {
self.signer_name = signer_name;
}
SignerInfoEventDetails::SignerInfoChanged(signer_info) => {
self.signer_info = signer_info;
}
}
self.version += 1;
}
fn process_command(&self, command: Self::Command) -> Result<Vec<SignerInfoEvent>, Self::Error> {
match command.into_details() {
SignerInfoCommandDetails::AddKey(key_id, internal_key_id) => {
let event = SignerInfoEvent::key_added(self, key_id, internal_key_id);
Ok(vec![event])
}
SignerInfoCommandDetails::RemoveKey(key_id) => {
let event = SignerInfoEvent::key_removed(self, key_id);
Ok(vec![event])
}
SignerInfoCommandDetails::ChangeSignerName(signer_name) => {
if signer_name != self.signer_name {
let event = SignerInfoEvent::signer_name_changed(self, signer_name);
Ok(vec![event])
} else {
Ok(vec![])
}
}
SignerInfoCommandDetails::ChangeSignerInfo(signer_info) => {
if signer_info != self.signer_info {
let event = SignerInfoEvent::signer_info_changed(self, signer_info);
Ok(vec![event])
} else {
Ok(vec![])
}
}
}
}
}
pub struct SignerMapper {
store: AggregateStore<SignerInfo>,
}
impl std::fmt::Debug for SignerMapper {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SignerMapper").finish()
}
}
impl SignerMapper {
pub fn build(work_dir: &Path) -> KrillResult<SignerMapper> {
let store = AggregateStore::<SignerInfo>::disk(work_dir, SIGNERS_DIR)?;
Ok(SignerMapper { store })
}
pub fn add_signer(
&self,
signer_name: &str,
signer_info: &str,
public_key: &PublicKey,
private_key_internal_id: &str,
) -> KrillResult<SignerHandle> {
let signer_handle = SignerHandle::from_str(&uuid::Uuid::new_v4().to_string())
.map_err(|err| Error::SignerError(format!("Generated UUID is not a valid signer handle: {}", err)))?;
let init = InitSignerInfoEvent::init(
&signer_handle,
signer_name,
signer_info,
public_key,
private_key_internal_id,
);
self.store.add(init)?;
Ok(signer_handle)
}
pub fn _remove_signer(&self, signer_handle: &SignerHandle) -> KrillResult<()> {
self.store.drop_aggregate(signer_handle)?;
Ok(())
}
pub fn get_signer_name(&self, signer_handle: &SignerHandle) -> KrillResult<String> {
Ok(self.store.get_latest(signer_handle)?.signer_name.clone())
}
pub fn change_signer_name(&self, signer_handle: &SignerHandle, signer_name: &str) -> KrillResult<()> {
let cmd = SignerInfoCommand::change_signer_name(signer_handle, None, signer_name);
self.store.command(cmd)?;
Ok(())
}
pub fn get_signer_public_key(&self, signer_handle: &SignerHandle) -> KrillResult<PublicKey> {
Ok(self.store.get_latest(signer_handle)?.signer_identity.public_key.clone())
}
pub fn get_signer_private_key_internal_id(&self, signer_handle: &SignerHandle) -> KrillResult<String> {
Ok(self
.store
.get_latest(signer_handle)?
.signer_identity
.private_key_internal_id
.clone())
}
pub fn change_signer_info(&self, signer_handle: &SignerHandle, signer_info: &str) -> KrillResult<()> {
let cmd = SignerInfoCommand::change_signer_info(signer_handle, None, signer_info);
self.store.command(cmd)?;
Ok(())
}
pub fn add_key(
&self,
signer_handle: &SignerHandle,
key_id: &KeyIdentifier,
internal_key_id: &str,
) -> KrillResult<()> {
let cmd = SignerInfoCommand::add_key(signer_handle, None, key_id, internal_key_id);
self.store.command(cmd)?;
Ok(())
}
pub fn remove_key(&self, signer_handle: &SignerHandle, key_id: &KeyIdentifier) -> KrillResult<()> {
let cmd = SignerInfoCommand::remove_key(signer_handle, None, key_id);
self.store.command(cmd)?;
Ok(())
}
pub fn get_key(&self, signer_handle: &SignerHandle, key_id: &KeyIdentifier) -> KrillResult<String> {
self.store
.get_latest(signer_handle)?
.keys
.get(key_id)
.cloned()
.ok_or_else(|| Error::SignerError(format!("Key with key id '{}' not found", key_id)))
}
pub fn get_signer_handles(&self) -> KrillResult<Vec<SignerHandle>> {
self.store.list().map_err(Error::AggregateStoreError)
}
pub fn get_signer_for_key(&self, key_id: &KeyIdentifier) -> KrillResult<SignerHandle> {
for signer_handle in self.store.list()? {
let signer_info = self.store.get_latest(&signer_handle)?;
if signer_info.keys.contains_key(key_id) {
return Ok(signer_handle);
}
}
Err(Error::SignerError(format!("No signer owns key id '{}'", key_id)))
}
}