aboutsummaryrefslogblamecommitdiff
path: root/src/garage/admin/key.rs
blob: 908986fa876e7e1780b4633928ffeb6bb08d3278 (plain) (tree)










































                                                                                                  

                                                                                        



                                                                      




                                                                                            






































































                                                                                              













                                                                                                                                                                                                                                                                                   



                                                                                                                                                                                                       
 
























                                                                                              
use std::collections::HashMap;

use garage_table::*;

use garage_model::helper::error::Error;
use garage_model::key_table::*;

use crate::cli::*;

use super::*;

impl AdminRpcHandler {
	pub(super) async fn handle_key_cmd(&self, cmd: &KeyOperation) -> Result<AdminRpc, Error> {
		match cmd {
			KeyOperation::List => self.handle_list_keys().await,
			KeyOperation::Info(query) => self.handle_key_info(query).await,
			KeyOperation::Create(query) => self.handle_create_key(query).await,
			KeyOperation::Rename(query) => self.handle_rename_key(query).await,
			KeyOperation::Delete(query) => self.handle_delete_key(query).await,
			KeyOperation::Allow(query) => self.handle_allow_key(query).await,
			KeyOperation::Deny(query) => self.handle_deny_key(query).await,
			KeyOperation::Import(query) => self.handle_import_key(query).await,
		}
	}

	async fn handle_list_keys(&self) -> Result<AdminRpc, Error> {
		let key_ids = self
			.garage
			.key_table
			.get_range(
				&EmptyKey,
				None,
				Some(KeyFilter::Deleted(DeletedFilter::NotDeleted)),
				10000,
				EnumerationOrder::Forward,
			)
			.await?
			.iter()
			.map(|k| (k.key_id.to_string(), k.params().unwrap().name.get().clone()))
			.collect::<Vec<_>>();
		Ok(AdminRpc::KeyList(key_ids))
	}

	async fn handle_key_info(&self, query: &KeyInfoOpt) -> Result<AdminRpc, Error> {
		let mut key = self
			.garage
			.key_helper()
			.get_existing_matching_key(&query.key_pattern)
			.await?;

		if !query.show_secret {
			key.state.as_option_mut().unwrap().secret_key = "(redacted)".into();
		}

		self.key_info_result(key).await
	}

	async fn handle_create_key(&self, query: &KeyNewOpt) -> Result<AdminRpc, Error> {
		let key = Key::new(&query.name);
		self.garage.key_table.insert(&key).await?;
		self.key_info_result(key).await
	}

	async fn handle_rename_key(&self, query: &KeyRenameOpt) -> Result<AdminRpc, Error> {
		let mut key = self
			.garage
			.key_helper()
			.get_existing_matching_key(&query.key_pattern)
			.await?;
		key.params_mut()
			.unwrap()
			.name
			.update(query.new_name.clone());
		self.garage.key_table.insert(&key).await?;
		self.key_info_result(key).await
	}

	async fn handle_delete_key(&self, query: &KeyDeleteOpt) -> Result<AdminRpc, Error> {
		let key_helper = self.garage.key_helper();

		let mut key = key_helper
			.get_existing_matching_key(&query.key_pattern)
			.await?;

		if !query.yes {
			return Err(Error::BadRequest(
				"Add --yes flag to really perform this operation".to_string(),
			));
		}

		key_helper.delete_key(&mut key).await?;

		Ok(AdminRpc::Ok(format!(
			"Key {} was deleted successfully.",
			key.key_id
		)))
	}

	async fn handle_allow_key(&self, query: &KeyPermOpt) -> Result<AdminRpc, Error> {
		let mut key = self
			.garage
			.key_helper()
			.get_existing_matching_key(&query.key_pattern)
			.await?;
		if query.create_bucket {
			key.params_mut().unwrap().allow_create_bucket.update(true);
		}
		self.garage.key_table.insert(&key).await?;
		self.key_info_result(key).await
	}

	async fn handle_deny_key(&self, query: &KeyPermOpt) -> Result<AdminRpc, Error> {
		let mut key = self
			.garage
			.key_helper()
			.get_existing_matching_key(&query.key_pattern)
			.await?;
		if query.create_bucket {
			key.params_mut().unwrap().allow_create_bucket.update(false);
		}
		self.garage.key_table.insert(&key).await?;
		self.key_info_result(key).await
	}

	async fn handle_import_key(&self, query: &KeyImportOpt) -> Result<AdminRpc, Error> {
		if !query.yes {
			return Err(Error::BadRequest("This command is intended to re-import keys that were previously generated by Garage. If you want to create a new key, use `garage key new` instead. Add the --yes flag if you really want to re-import a key.".to_string()));
		}

		if query.key_id.len() != 26
			|| &query.key_id[..2] != "GK"
			|| hex::decode(&query.key_id[2..]).is_err()
		{
			return Err(Error::BadRequest(format!("The specified key ID is not a valid Garage key ID (starts with `GK`, followed by 12 hex-encoded bytes)")));
		}
		if query.secret_key.len() != 64 || hex::decode(&query.secret_key).is_err() {
			return Err(Error::BadRequest(format!("The specified secret key is not a valid Garage secret key (composed of 32 hex-encoded bytes)")));
		}

		let prev_key = self.garage.key_table.get(&EmptyKey, &query.key_id).await?;
		if prev_key.is_some() {
			return Err(Error::BadRequest(format!("Key {} already exists in data store. Even if it is deleted, we can't let you create a new key with the same ID. Sorry.", query.key_id)));
		}

		let imported_key = Key::import(&query.key_id, &query.secret_key, &query.name);
		self.garage.key_table.insert(&imported_key).await?;

		self.key_info_result(imported_key).await
	}

	async fn key_info_result(&self, key: Key) -> Result<AdminRpc, Error> {
		let mut relevant_buckets = HashMap::new();

		for (id, _) in key
			.state
			.as_option()
			.unwrap()
			.authorized_buckets
			.items()
			.iter()
		{
			if let Some(b) = self.garage.bucket_table.get(&EmptyKey, id).await? {
				relevant_buckets.insert(*id, b);
			}
		}

		Ok(AdminRpc::KeyInfo(key, relevant_buckets))
	}
}