remote_secret.rs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. //! Example for creating a new Threema ID.
  2. #![expect(unused_crate_dependencies, reason = "Example triggered false positive")]
  3. #![expect(clippy::print_stdout, reason = "Examples are allowed to print")]
  4. use clap::{Parser, Subcommand};
  5. use data_encoding::HEXLOWER;
  6. use libthreema::{
  7. cli::{MinimalIdentityConfig, MinimalIdentityConfigOptions},
  8. common::keys::{RemoteSecretAuthenticationToken, RemoteSecretHash},
  9. https::cli::https_client_builder,
  10. remote_secret::{
  11. monitor::{
  12. RemoteSecretMonitorContext, RemoteSecretMonitorInstruction, RemoteSecretMonitorProtocol,
  13. RemoteSecretMonitorResponse,
  14. },
  15. setup::{
  16. RemoteSecretSetupContext, RemoteSecretSetupResponse,
  17. create::{RemoteSecretCreateLoop, RemoteSecretCreateResult, RemoteSecretCreateTask},
  18. delete::{RemoteSecretDeleteLoop, RemoteSecretDeleteTask},
  19. },
  20. },
  21. utils::logging::init_stderr_logging,
  22. };
  23. use tokio::time;
  24. use tracing::{Level, debug, info};
  25. #[derive(Parser)]
  26. #[command()]
  27. struct RemoteSecretCommand {
  28. #[command(flatten)]
  29. config: MinimalIdentityConfigOptions,
  30. #[command(subcommand)]
  31. command: Commands,
  32. }
  33. #[derive(Subcommand)]
  34. enum Commands {
  35. Create,
  36. Delete {
  37. #[arg(long, value_parser = RemoteSecretAuthenticationToken::from_hex)]
  38. remote_secret_authentication_token: RemoteSecretAuthenticationToken,
  39. },
  40. Monitor {
  41. #[arg(long, value_parser = RemoteSecretAuthenticationToken::from_hex)]
  42. remote_secret_authentication_token: RemoteSecretAuthenticationToken,
  43. #[arg(long, value_parser = RemoteSecretHash::from_hex)]
  44. remote_secret_hash: RemoteSecretHash,
  45. },
  46. }
  47. async fn run_create_remote_secret(
  48. http_client: reqwest::Client,
  49. context: RemoteSecretSetupContext,
  50. ) -> anyhow::Result<RemoteSecretCreateResult> {
  51. let mut task = RemoteSecretCreateTask::new(context);
  52. loop {
  53. match task.poll()? {
  54. RemoteSecretCreateLoop::Instruction(instruction) => {
  55. let result = instruction.request.send(&http_client).await;
  56. task.response(RemoteSecretSetupResponse { result })?;
  57. },
  58. RemoteSecretCreateLoop::Done(result) => return Ok(result),
  59. }
  60. }
  61. }
  62. async fn run_delete_remote_secret(
  63. http_client: reqwest::Client,
  64. context: RemoteSecretSetupContext,
  65. remote_secret_authentication_token: RemoteSecretAuthenticationToken,
  66. ) -> anyhow::Result<()> {
  67. let mut task = RemoteSecretDeleteTask::new(context, remote_secret_authentication_token);
  68. loop {
  69. match task.poll()? {
  70. RemoteSecretDeleteLoop::Instruction(instruction) => {
  71. let result = instruction.request.send(&http_client).await;
  72. task.response(RemoteSecretSetupResponse { result })?;
  73. },
  74. RemoteSecretDeleteLoop::Done(()) => return Ok(()),
  75. }
  76. }
  77. }
  78. async fn run_monitor_remote_secret(
  79. http_client: reqwest::Client,
  80. context: RemoteSecretMonitorContext,
  81. ) -> anyhow::Result<()> {
  82. let mut protocol = RemoteSecretMonitorProtocol::new(context);
  83. loop {
  84. match protocol.poll()? {
  85. RemoteSecretMonitorInstruction::Request(https_request) => {
  86. let result = https_request.send(&http_client).await;
  87. protocol.response(RemoteSecretMonitorResponse { result })?;
  88. },
  89. RemoteSecretMonitorInstruction::Schedule {
  90. timeout,
  91. remote_secret,
  92. } => {
  93. if let Some(remote_secret) = remote_secret {
  94. info!(
  95. remote_secret = HEXLOWER.encode(&remote_secret.0),
  96. remote_secret_hash = ?remote_secret.derive_hash(),
  97. "Retrieved remote secret",
  98. );
  99. }
  100. debug!(?timeout, "Scheduling another poll");
  101. time::sleep(timeout).await;
  102. },
  103. }
  104. }
  105. }
  106. #[tokio::main]
  107. async fn main() -> anyhow::Result<()> {
  108. // Configure logging
  109. init_stderr_logging(Level::TRACE);
  110. // Create HTTP client
  111. let http_client = https_client_builder().build()?;
  112. // Parse arguments for command
  113. let arguments = RemoteSecretCommand::parse();
  114. let config = MinimalIdentityConfig::from_options(&http_client, arguments.config).await?;
  115. match arguments.command {
  116. Commands::Create => {
  117. let context = config.remote_secret_setup_context()?;
  118. let result = run_create_remote_secret(http_client, context).await?;
  119. println!(
  120. "--remote-secret-authentication-token {}",
  121. HEXLOWER.encode(&result.remote_secret_authentication_token.0)
  122. );
  123. println!(
  124. "--remote-secret-hash {}",
  125. HEXLOWER.encode(&result.remote_secret.derive_hash().0)
  126. );
  127. },
  128. Commands::Delete {
  129. remote_secret_authentication_token,
  130. } => {
  131. let context = config.remote_secret_setup_context()?;
  132. run_delete_remote_secret(http_client, context, remote_secret_authentication_token).await?;
  133. },
  134. Commands::Monitor {
  135. remote_secret_authentication_token,
  136. remote_secret_hash,
  137. } => {
  138. let context =
  139. config.remote_secret_monitor_context(remote_secret_authentication_token, remote_secret_hash);
  140. run_monitor_remote_secret(http_client, context).await?;
  141. },
  142. }
  143. Ok(())
  144. }
  145. #[test]
  146. fn verify_cli() {
  147. use clap::CommandFactory;
  148. RemoteSecretCommand::command().debug_assert();
  149. }