From cc708717d91e1d3a87befca5d0a0ef7920b3589c Mon Sep 17 00:00:00 2001 From: Berend Sliedrecht <61358536+blu3beri@users.noreply.github.com> Date: Fri, 3 Jun 2022 12:34:04 +0200 Subject: [PATCH] feat: multitenancy create and remove wallet (#184) Signed-off-by: Berend Sliedrecht Co-authored-by: Jean-Louis Leysens --- crates/agent/src/error.rs | 2 +- crates/agent/src/modules/mod.rs | 3 ++ crates/agent/src/modules/multitenancy.rs | 37 +++++++++++++ crates/cli/src/cli.rs | 4 ++ crates/cli/src/help_strings.rs | 10 ++++ crates/cli/src/modules/mod.rs | 3 ++ crates/cli/src/modules/multitenancy.rs | 54 +++++++++++++++++++ crates/cli/src/register.rs | 11 ++++ .../cloudagent-python/src/agent_python/mod.rs | 3 ++ .../src/agent_python/multitenancy.rs | 29 ++++++++++ crates/cloudagent-python/src/cloud_agent.rs | 2 +- docker/docker-compose.acapy.yml | 2 +- 12 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 crates/agent/src/modules/multitenancy.rs create mode 100644 crates/cli/src/modules/multitenancy.rs create mode 100644 crates/cloudagent-python/src/agent_python/multitenancy.rs diff --git a/crates/agent/src/error.rs b/crates/agent/src/error.rs index cdc363a9..126b1bf9 100644 --- a/crates/agent/src/error.rs +++ b/crates/agent/src/error.rs @@ -43,7 +43,7 @@ impl Display for Error { match self { Error::AuthorizationFailed => write!(f, "Failed to authorize. Api-key or authorization token is either wrong or missing."), Error::UnableToParseResponse => write!(f, "Unable to parse the response from the server. Is the cloudagent the correct version?"), - Error::UrlDoesNotExist => write!(f, "Path does not exist on agent URL. This can happen when querying by id and the id is not valid."), + Error::UrlDoesNotExist => write!(f, "Path does not exist on agent URL. This can happen when querying by id and the id is not valid or when attempting to use a feature that is not supported on the cloudagent."), Error::UnknownResponseStatusCode(msg) => write!(f, "Received unknown status code from the server. Agent URL is likely incorrect. If the agent URL is correct, please report this error at https://github.com/animo/agent-cli/issues/new \nAdditional info: {}", msg), Error::InternalServerError(status) => write!(f, "Internal Server Error (status code: {})!", status), Error::UnreachableUrl => write!(f, "Provided url is unreachable. Is the provided agent URL valid?"), diff --git a/crates/agent/src/modules/mod.rs b/crates/agent/src/modules/mod.rs index 446d14d3..3baa5de3 100644 --- a/crates/agent/src/modules/mod.rs +++ b/crates/agent/src/modules/mod.rs @@ -18,3 +18,6 @@ pub mod proof; /// Schema module for a generic cloudagent pub mod schema; + +/// Multitenancy module for a generic cloudagent +pub mod multitenancy; diff --git a/crates/agent/src/modules/multitenancy.rs b/crates/agent/src/modules/multitenancy.rs new file mode 100644 index 00000000..377b2b5b --- /dev/null +++ b/crates/agent/src/modules/multitenancy.rs @@ -0,0 +1,37 @@ +use async_trait::async_trait; + +use crate::error::Result; +use serde::Deserialize; +use serde::Serialize; +use serde_json::Value; + +/// Response of the `create` endpoint on the multitenancy module +#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct MultitenancyCreateResponse { + /// Timestamp of when the subwallet was created + pub created_at: String, + /// The mode of the key management (managed, unmanaged) + pub key_management_mode: String, + + /// More wallet information + pub settings: Value, + + /// JWT + pub token: String, + + /// Timestamp of when the last update happened to the wallet + pub updated_at: String, + + /// The wallet id + pub wallet_id: String, +} + +/// Multitenancy module for a generic cloudagent +#[async_trait] +pub trait MultitenancyModule { + /// Create a new subwallet + async fn create(&self) -> Result; + + /// Remove a subwallet + async fn remove(&self, wallet_id: String) -> Result<()>; +} diff --git a/crates/cli/src/cli.rs b/crates/cli/src/cli.rs index 3d403a80..0c3a231a 100644 --- a/crates/cli/src/cli.rs +++ b/crates/cli/src/cli.rs @@ -5,6 +5,7 @@ use clap::{Parser, Subcommand}; use crate::help_strings::HelpStrings; use crate::modules::automation::AutomationOptions; +use crate::modules::multitenancy::MultitenancyOptions; use crate::modules::{ basic_message::BasicMessageOptions, configuration::ConfigurationOptions, connection::ConnectionOptions, credential::CredentialOptions, @@ -83,4 +84,7 @@ pub enum Commands { /// Proof subcommands Proof(ProofOptions), + + /// Multitenancy subcommnads + Multitenancy(MultitenancyOptions), } diff --git a/crates/cli/src/help_strings.rs b/crates/cli/src/help_strings.rs index 548f8048..e8654f8e 100644 --- a/crates/cli/src/help_strings.rs +++ b/crates/cli/src/help_strings.rs @@ -89,6 +89,12 @@ pub enum HelpStrings { ProofRequestAttribute, ProofRequestConnectionId, + // Multitenancy + Multitenancy, + MultitenancyCreate, + MultitenancyRemove, + MultitenancyRemoveWalletId, + // Automate Automation, AutomationCredentialOffer, @@ -200,6 +206,10 @@ impl HelpStrings { HelpStrings::AutomationCredentialOfferNoQr => "Do not show a QR code", HelpStrings::AutomationCredentialOfferSelf => "Offer a credential to self", HelpStrings::AutomationCredentialOfferTimeout=> "Timeout in seconds", + HelpStrings::Multitenancy => "foo", + HelpStrings::MultitenancyCreate => "foo", + HelpStrings::MultitenancyRemove => "foo", + HelpStrings::MultitenancyRemoveWalletId => "foo", } } } diff --git a/crates/cli/src/modules/mod.rs b/crates/cli/src/modules/mod.rs index b467c062..f5bf4edd 100644 --- a/crates/cli/src/modules/mod.rs +++ b/crates/cli/src/modules/mod.rs @@ -24,3 +24,6 @@ pub mod proof; /// Module for schemas pub mod schema; + +/// Module for multitenancy +pub mod multitenancy; diff --git a/crates/cli/src/modules/multitenancy.rs b/crates/cli/src/modules/multitenancy.rs new file mode 100644 index 00000000..603160ea --- /dev/null +++ b/crates/cli/src/modules/multitenancy.rs @@ -0,0 +1,54 @@ +use crate::{ + error::Result, + help_strings::HelpStrings, + utils::loader::{Loader, LoaderVariant}, +}; +use agent::modules::multitenancy::MultitenancyModule; +use clap::{Args, Subcommand}; + +/// Credential Definition options and flags +#[derive(Args)] +#[clap(about = "CRUD functionality with multitenancy wallets")] +pub struct MultitenancyOptions { + /// All the subcommands of the multitenancy cli + #[clap(subcommand)] + pub commands: MultitenancySubcommands, +} + +/// Credential Definition subcommands +#[derive(Subcommand, Debug)] +pub enum MultitenancySubcommands { + /// Create a subwallet + #[clap(about = HelpStrings::MultitenancyCreate)] + Create {}, + + /// Remove a subwallet + #[clap(about = HelpStrings::MultitenancyRemove)] + Remove { + /// List a single credential definition by id + #[clap(long, short, help = HelpStrings::MultitenancyRemoveWalletId)] + wallet_id: String, + }, +} + +/// Subcommand multitenancy parser +pub async fn parse_multitenancy_args( + options: &MultitenancyOptions, + agent: impl MultitenancyModule, +) -> Result<()> { + let loader = Loader::start(LoaderVariant::default()); + + match &options.commands { + MultitenancySubcommands::Create {} => agent.create().await.map(|response| { + loader.stop(); + copy!("{}", response.wallet_id); + log!("{}", response.wallet_id); + }), + MultitenancySubcommands::Remove { wallet_id } => { + agent.remove(wallet_id.to_owned()).await?; + loader.stop(); + log!("Successfully removed wallet with id: {}", wallet_id); + Ok(()) + } + } +} diff --git a/crates/cli/src/register.rs b/crates/cli/src/register.rs index 57754a6b..30c0cdbe 100644 --- a/crates/cli/src/register.rs +++ b/crates/cli/src/register.rs @@ -9,6 +9,7 @@ use crate::modules::basic_message::parse_basic_message_args; use crate::modules::configuration::parse_configuration_args; use crate::modules::credential::parse_credentials_args; use crate::modules::credential_definition::parse_credential_definition_args; +use crate::modules::multitenancy::parse_multitenancy_args; use crate::modules::proof::parse_proof_args; use crate::modules::{ connection::parse_connection_args, feature::parse_features_args, schema::parse_schema_args, @@ -105,6 +106,16 @@ pub async fn register() -> Result<()> { )?; parse_proof_args(&options.commands, agent).await } + Commands::Multitenancy(options) => { + let agent = initialize_agent_from_cli( + cli.config, + cli.environment, + cli.agent_url, + cli.api_key, + cli.token, + )?; + parse_multitenancy_args(options, agent).await + } Commands::Automate(options) => { let agent = initialize_agent_from_cli( cli.config, diff --git a/crates/cloudagent-python/src/agent_python/mod.rs b/crates/cloudagent-python/src/agent_python/mod.rs index 4e401bf9..262176a9 100644 --- a/crates/cloudagent-python/src/agent_python/mod.rs +++ b/crates/cloudagent-python/src/agent_python/mod.rs @@ -21,3 +21,6 @@ mod proof; /// Module for schemas definitions specific for an Aries cloudagent Python mod schema; + +/// Module for multitenancy specific for an Aries cloudagent Python +mod multitenancy; diff --git a/crates/cloudagent-python/src/agent_python/multitenancy.rs b/crates/cloudagent-python/src/agent_python/multitenancy.rs new file mode 100644 index 00000000..f11fdb68 --- /dev/null +++ b/crates/cloudagent-python/src/agent_python/multitenancy.rs @@ -0,0 +1,29 @@ +use super::agent::CloudAgentPython; +use agent::modules::multitenancy::MultitenancyModule; +use agent::{error::Result, modules::multitenancy::MultitenancyCreateResponse}; +use async_trait::async_trait; +use serde_json::{json, Value}; + +#[async_trait] +impl MultitenancyModule for CloudAgentPython { + /// TODO: this only returns the wallet id for now + async fn create(&self) -> Result { + let url = self + .cloud_agent + .create_url(vec!["multitenancy", "wallet"])?; + + self.cloud_agent + .post::(url, None, Some(json!({}))) + .await + } + + async fn remove(&self, wallet_id: String) -> Result<()> { + let url = + self.cloud_agent + .create_url(vec!["multitenancy", "wallet", &wallet_id, "remove"])?; + + self.cloud_agent.post::(url, None, None).await?; + + Ok(()) + } +} diff --git a/crates/cloudagent-python/src/cloud_agent.rs b/crates/cloudagent-python/src/cloud_agent.rs index d5f4b2a0..7948b8db 100644 --- a/crates/cloudagent-python/src/cloud_agent.rs +++ b/crates/cloudagent-python/src/cloud_agent.rs @@ -10,7 +10,7 @@ pub struct CloudAgent { /// admin Api key for the cloudagent pub api_key: Option, - /// Authorization token for a mutli tenancy agent + /// Authorization token for a multi tenancy agent pub auth_token: Option, } diff --git a/docker/docker-compose.acapy.yml b/docker/docker-compose.acapy.yml index 28e88596..6fbe97b0 100644 --- a/docker/docker-compose.acapy.yml +++ b/docker/docker-compose.acapy.yml @@ -37,4 +37,4 @@ services: --admin-insecure-mode \ --label 'tester_agent' \ --log-level 'info' ", - ] \ No newline at end of file + ]