example_secret_storage/
main.rs1use anyhow::Result;
2use clap::{Parser, Subcommand};
3use matrix_sdk::{
4 authentication::matrix::MatrixSession,
5 encryption::secret_storage::SecretStore,
6 ruma::{events::secret::request::SecretName, OwnedDeviceId, OwnedUserId},
7 AuthSession, Client, SessionMeta, SessionTokens,
8};
9use url::Url;
10
11#[derive(Parser, Debug)]
17struct Cli {
18 #[clap(value_parser)]
20 homeserver: Url,
21
22 #[clap(value_parser)]
24 user_id: OwnedUserId,
25
26 #[clap(value_parser)]
28 device_id: OwnedDeviceId,
29
30 #[clap(value_parser)]
32 access_token: String,
33
34 #[clap(short, long)]
36 proxy: Option<Url>,
37
38 #[clap(short, long, action)]
40 verbose: bool,
41
42 #[clap(long, action)]
44 secret_store_key: String,
45
46 #[command(subcommand)]
48 command: Commands,
49}
50
51#[derive(Debug, Subcommand)]
52enum Commands {
53 GetSecret { secret_name: SecretName },
55 SetSecret { secret_name: SecretName, secret: String },
57 ImportKnownSecrets,
65}
66
67async fn get_secret(secret_store: SecretStore, secret_name: SecretName) -> Result<()> {
68 let secret = secret_store.get_secret(secret_name.to_owned()).await?;
69
70 if let Some(secret) = secret {
71 println!("Secret: {secret}");
72 } else {
73 println!("No secret with the name {secret_name} found")
74 }
75
76 Ok(())
77}
78
79async fn set_secret(
80 secret_store: SecretStore,
81 secret_name: SecretName,
82 secret: &str,
83) -> Result<()> {
84 secret_store.put_secret(secret_name.to_owned(), secret).await?;
85
86 println!("Secret {secret_name} was successfully encrypted and stored on the homeserver");
87
88 Ok(())
89}
90
91async fn import_known_secrets(client: Client, secret_store: SecretStore) -> Result<()> {
92 secret_store.import_secrets().await?;
93
94 let status = client
95 .encryption()
96 .cross_signing_status()
97 .await
98 .expect("We should be able to get our cross-signing status");
99
100 if status.is_complete() {
101 println!("Successfully imported all the cross-signing keys");
102 } else {
103 eprintln!("Couldn't import all the cross-signing keys: {status:?}");
104 }
105
106 Ok(())
107}
108
109async fn restore_client(cli: &Cli) -> Result<Client> {
110 let builder = Client::builder().homeserver_url(&cli.homeserver);
111
112 let builder = if let Some(proxy) = cli.proxy.as_ref() { builder.proxy(proxy) } else { builder };
113 let client = builder.build().await?;
114
115 let session = AuthSession::Matrix(MatrixSession {
117 meta: SessionMeta { user_id: cli.user_id.to_owned(), device_id: cli.device_id.to_owned() },
118 tokens: SessionTokens { access_token: cli.access_token.to_owned(), refresh_token: None },
119 });
120
121 client.restore_session(session).await?;
122
123 Ok(client)
124}
125
126#[tokio::main]
127async fn main() -> Result<()> {
128 let cli = Cli::parse();
129
130 if cli.verbose {
131 tracing_subscriber::fmt::init();
132 }
133
134 let client = restore_client(&cli).await?;
135 let secret_store =
136 client.encryption().secret_storage().open_secret_store(&cli.secret_store_key).await?;
137
138 match cli.command {
139 Commands::GetSecret { secret_name } => get_secret(secret_store, secret_name).await,
140 Commands::SetSecret { secret_name, secret } => {
141 set_secret(secret_store, secret_name, &secret).await
142 }
143 Commands::ImportKnownSecrets => import_known_secrets(client, secret_store).await,
144 }
145}