ockam_command 0.104.0

End-to-end encryption and mutual authentication for distributed applications.
Documentation
mod create;
mod list;
mod revoke;
mod show;

pub use create::CreateCommand;
pub use list::ListCommand;
pub use show::ShowCommand;

use clap::{Args, Subcommand};
use miette::{miette, Context, IntoDiagnostic};

use ockam_api::cli_state::{ProjectConfigCompact, StateDirTrait, StateItemTrait};
use ockam_api::cloud::ProjectNode;
use ockam_api::config::lookup::ProjectLookup;
use ockam_api::nodes::Credentials;
use ockam_api::nodes::InMemoryNode;

use crate::identity::get_identity_name;

use crate::util::api::{CloudOpts, TrustContextOpts};
use crate::CommandGlobalOpts;

use self::revoke::RevokeCommand;

#[derive(Clone, Debug, Args)]
#[command(arg_required_else_help = true, subcommand_required = true)]
pub struct LeaseCommand {
    #[command(subcommand)]
    subcommand: LeaseSubcommand,

    #[command(flatten)]
    cloud_opts: CloudOpts,

    #[command(flatten)]
    trust_context_opts: TrustContextOpts,
}

#[derive(Clone, Debug, Subcommand)]
pub enum LeaseSubcommand {
    Create(CreateCommand),
    List(ListCommand),
    Show(ShowCommand),
    Revoke(RevokeCommand),
}

impl LeaseCommand {
    pub fn run(self, options: CommandGlobalOpts) {
        match self.subcommand {
            LeaseSubcommand::Create(c) => c.run(options, self.cloud_opts, self.trust_context_opts),
            LeaseSubcommand::List(c) => c.run(options, self.cloud_opts, self.trust_context_opts),
            LeaseSubcommand::Show(c) => c.run(options, self.cloud_opts, self.trust_context_opts),
            LeaseSubcommand::Revoke(c) => c.run(options, self.cloud_opts, self.trust_context_opts),
        }
    }
}

async fn authenticate(
    ctx: &ockam_node::Context,
    opts: &CommandGlobalOpts,
    cloud_opts: &CloudOpts,
    trust_opts: &TrustContextOpts,
) -> miette::Result<ProjectNode> {
    let trust_context_config = trust_opts.to_config(&opts.state)?.build();
    let node = InMemoryNode::start_with_trust_context(
        ctx,
        &opts.state,
        trust_opts.project_path.as_ref(),
        trust_context_config,
    )
    .await?;
    let identity = get_identity_name(&opts.state, &cloud_opts.identity);
    let project_info = retrieve_project_info(opts, trust_opts).await?;
    let project_authority = project_info
        .authority
        .as_ref()
        .ok_or(miette!("Project Authority is required"))?;
    let project_identifier = project_info
        .identity_id
        .ok_or(miette!("Project identifier is required"))?;
    let project_addr = project_info
        .node_route
        .ok_or(miette!("Project route is required"))?;

    let authority_node = node
        .create_authority_client(
            project_authority.identity_id(),
            project_authority.address(),
            Some(identity.clone()),
        )
        .await?;

    authority_node
        .authenticate(ctx, Some(identity.clone()))
        .await?;
    node.create_project_client(&project_identifier, &project_addr, Some(identity.clone()))
        .await
}

async fn retrieve_project_info(
    opts: &CommandGlobalOpts,
    trust_context_opts: &TrustContextOpts,
) -> miette::Result<ProjectLookup> {
    let project_path = match &trust_context_opts.project_path {
        Some(p) => p.clone(),
        None => {
            let default_project = opts
                .state
                .projects
                .default()
                .context("A default project or project parameter is required")?;

            default_project.path().clone()
        }
    };
    // Read (okta and authority) project parameters from project.json
    let s = tokio::fs::read_to_string(project_path)
        .await
        .into_diagnostic()?;
    let proj_info: ProjectConfigCompact = serde_json::from_str(&s).into_diagnostic()?;
    ProjectLookup::from_project(&(&proj_info).into())
        .await
        .into_diagnostic()
}