ockam_command 0.117.0

End-to-end encryption and mutual authentication for distributed applications.
Documentation
use clap::{Args, Subcommand};
use colorful::core::StrMarker;
use colorful::Colorful;
use serde_json::json;

pub(crate) use issue::IssueCommand;
use ockam::identity::models::{CredentialAndPurposeKey, CredentialSchemaIdentifier};
use ockam::identity::{Identifier, TimestampInSeconds};
use ockam_core::compat::collections::HashMap;
pub(crate) use store::StoreCommand;
pub(crate) use verify::VerifyCommand;

use crate::credential::list::ListCommand;
use crate::error::Error;
use crate::output::Output;
use crate::{CommandGlobalOpts, Result};

pub(crate) mod issue;
pub(crate) mod list;
pub(crate) mod store;
pub(crate) mod verify;

/// Manage Credentials
#[derive(Clone, Debug, Args)]
#[command(arg_required_else_help = true, subcommand_required = true)]
pub struct CredentialCommand {
    #[command(subcommand)]
    subcommand: CredentialSubcommand,
}

#[derive(Clone, Debug, Subcommand)]
pub enum CredentialSubcommand {
    #[command(display_order = 900)]
    List(ListCommand),
    Issue(IssueCommand),
    Store(StoreCommand),
    Verify(VerifyCommand),
}

impl CredentialSubcommand {
    pub fn name(&self) -> String {
        match &self {
            CredentialSubcommand::List(c) => c.name(),
            CredentialSubcommand::Issue(c) => c.name(),
            CredentialSubcommand::Store(c) => c.name(),
            CredentialSubcommand::Verify(c) => c.name(),
        }
    }
}

impl CredentialCommand {
    pub fn run(self, opts: CommandGlobalOpts) -> miette::Result<()> {
        match self.subcommand {
            CredentialSubcommand::List(c) => c.run(opts),
            CredentialSubcommand::Issue(c) => c.run(opts),
            CredentialSubcommand::Store(c) => c.run(opts),
            CredentialSubcommand::Verify(c) => c.run(opts),
        }
    }

    pub fn name(&self) -> String {
        self.subcommand.name()
    }
}

pub struct CredentialOutput {
    credential: String,
    subject: Identifier,
    issuer: Identifier,
    created_at: TimestampInSeconds,
    expires_at: TimestampInSeconds,
    is_verified: bool,
    schema: CredentialSchemaIdentifier,
    attributes: HashMap<String, String>,
}

impl CredentialOutput {
    pub fn from_credential(credential: CredentialAndPurposeKey, is_verified: bool) -> Result<Self> {
        let str = hex::encode(credential.encode_as_cbor_bytes()?);
        let credential_data = credential.credential.get_credential_data()?;
        let purpose_key_data = credential.purpose_key_attestation.get_attestation_data()?;

        let subject = credential_data.subject.ok_or(Error::InternalError {
            error_message: "credential subject is missing".to_str(),
            exit_code: 1,
        })?;

        let mut attributes = HashMap::<String, String>::default();
        for (k, v) in &credential_data.subject_attributes.map {
            match (
                String::from_utf8(k.as_slice().to_vec()),
                String::from_utf8(v.as_slice().to_vec()),
            ) {
                (Ok(k), Ok(v)) => _ = attributes.insert(k, v),
                _ => continue,
            }
        }

        let s = Self {
            credential: str,
            subject,
            issuer: purpose_key_data.subject,
            created_at: credential_data.created_at,
            expires_at: credential_data.expires_at,
            is_verified,
            schema: credential_data.subject_attributes.schema,
            attributes,
        };

        Ok(s)
    }
}

impl Output for CredentialOutput {
    fn output(&self) -> Result<String> {
        let is_verified = if self.is_verified {
            "✔︎".light_green()
        } else {
            "".light_red()
        };

        let attributes = json!(self.attributes).to_string();

        let output = format!(
            "Credential:\n\
            \tsubject:     {subject}\n\
            \tissuer:      {issuer}\n\
            \tis_verified: {is_verified}\n\
            \tcreated_at:  {created_at}\n\
            \texpires_at:  {expires_at}\n\
            \tschema:      {schema}\n\
            \tattributes:  {attributes}\n\
            \tbinary:      {credential}",
            subject = self.subject,
            issuer = self.issuer,
            is_verified = is_verified,
            created_at = self.created_at.0,
            expires_at = self.expires_at.0,
            schema = self.schema.0,
            attributes = attributes,
            credential = self.credential
        );

        Ok(output)
    }
}