From a8766626403c310d80139ab0660221f5d5aa4f22 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Sun, 2 May 2021 22:30:56 +0200 Subject: S3 API: support ListBuckets --- src/api/Cargo.toml | 2 ++ src/api/api_server.rs | 6 ++-- src/api/error.rs | 6 ++++ src/api/s3_bucket.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/api/Cargo.toml b/src/api/Cargo.toml index 0b824ca3..b9fc4bfc 100644 --- a/src/api/Cargo.toml +++ b/src/api/Cargo.toml @@ -38,4 +38,6 @@ http-range = "0.1" hyper = "0.14" percent-encoding = "2.1.0" roxmltree = "0.14" +serde = { version = "1.0", features = ["derive"] } +quick-xml = { version = "0.21", features = [ "serialize" ] } url = "2.1" diff --git a/src/api/api_server.rs b/src/api/api_server.rs index ab8bd736..8a51b851 100644 --- a/src/api/api_server.rs +++ b/src/api/api_server.rs @@ -81,10 +81,12 @@ async fn handler( async fn handler_inner(garage: Arc, req: Request) -> Result, Error> { let path = req.uri().path().to_string(); let path = percent_encoding::percent_decode_str(&path).decode_utf8()?; + let (api_key, content_sha256) = check_signature(&garage, &req).await?; + if path == "/" { + return handle_list_buckets(&api_key); + } let (bucket, key) = parse_bucket_key(&path)?; - - let (api_key, content_sha256) = check_signature(&garage, &req).await?; let allowed = match req.method() { &Method::HEAD | &Method::GET => api_key.allow_read(&bucket), _ => api_key.allow_write(&bucket), diff --git a/src/api/error.rs b/src/api/error.rs index a3cdfdbd..bd340fa6 100644 --- a/src/api/error.rs +++ b/src/api/error.rs @@ -72,6 +72,12 @@ impl From for Error { } } +impl From for Error { + fn from(err: quick_xml::de::DeError) -> Self { + Self::InvalidXML(format!("{}", err)) + } +} + impl Error { /// Get the HTTP status code that best represents the meaning of the error for the client pub fn http_status_code(&self) -> StatusCode { diff --git a/src/api/s3_bucket.rs b/src/api/s3_bucket.rs index cbefd005..d1a4425a 100644 --- a/src/api/s3_bucket.rs +++ b/src/api/s3_bucket.rs @@ -2,11 +2,62 @@ use std::fmt::Write; use std::sync::Arc; use hyper::{Body, Response}; +use quick_xml::se::to_string; +use serde::Serialize; use garage_model::garage::Garage; +use garage_model::key_table::Key; +use garage_util::time::*; use crate::error::*; +#[derive(Debug, Serialize, PartialEq)] +struct CreationDate { + #[serde(rename = "$value")] + pub body: String, +} +#[derive(Debug, Serialize, PartialEq)] +struct Name { + #[serde(rename = "$value")] + pub body: String, +} +#[derive(Debug, Serialize, PartialEq)] +struct Bucket { + #[serde(rename = "CreationDate")] + pub creation_date: CreationDate, + #[serde(rename = "Name")] + pub name: Name, +} +#[derive(Debug, Serialize, PartialEq)] +struct DisplayName { + #[serde(rename = "$value")] + pub body: String, +} +#[derive(Debug, Serialize, PartialEq)] +struct ID { + #[serde(rename = "$value")] + pub body: String, +} +#[derive(Debug, Serialize, PartialEq)] +struct Owner { + #[serde(rename = "DisplayName")] + display_name: DisplayName, + #[serde(rename = "ID")] + id: ID, +} +#[derive(Debug, Serialize, PartialEq)] +struct BucketList { + #[serde(rename = "Bucket")] + pub entries: Vec, +} +#[derive(Debug, Serialize, PartialEq)] +struct ListAllMyBucketsResult { + #[serde(rename = "Buckets")] + buckets: BucketList, + #[serde(rename = "Owner")] + owner: Owner, +} + pub fn handle_get_bucket_location(garage: Arc) -> Result, Error> { let mut xml = String::new(); @@ -22,3 +73,39 @@ pub fn handle_get_bucket_location(garage: Arc) -> Result, .header("Content-Type", "application/xml") .body(Body::from(xml.into_bytes()))?) } + +pub fn handle_list_buckets(api_key: &Key) -> Result, Error> { + let list_buckets = ListAllMyBucketsResult { + owner: Owner { + display_name: DisplayName { + body: api_key.name.get().to_string(), + }, + id: ID { + body: api_key.key_id.to_string(), + }, + }, + buckets: BucketList { + entries: api_key + .authorized_buckets + .items() + .iter() + .map(|(name, ts, _)| Bucket { + creation_date: CreationDate { + body: msec_to_rfc3339(*ts), + }, + name: Name { + body: name.to_string(), + }, + }) + .collect(), + }, + }; + + let mut xml = r#""#.to_string(); + xml.push_str(&to_string(&list_buckets)?); + trace!("xml: {}", xml); + + Ok(Response::builder() + .header("Content-Type", "application/xml") + .body(Body::from(xml))?) +} -- cgit v1.2.3