aboutsummaryrefslogtreecommitdiff
path: root/src/api
diff options
context:
space:
mode:
authorQuentin Dufour <quentin@deuxfleurs.fr>2021-05-02 22:30:56 +0200
committerQuentin Dufour <quentin@deuxfleurs.fr>2021-05-02 22:30:56 +0200
commita8766626403c310d80139ab0660221f5d5aa4f22 (patch)
treed77d49f3c94ededa95200c1855ab422a19699fb9 /src/api
parentef4d6e782a67bd422489d643e8807eaf7fb853c4 (diff)
downloadgarage-feature/s3/list-buckets.tar.gz
garage-feature/s3/list-buckets.zip
S3 API: support ListBucketsfeature/s3/list-buckets
Diffstat (limited to 'src/api')
-rw-r--r--src/api/Cargo.toml2
-rw-r--r--src/api/api_server.rs6
-rw-r--r--src/api/error.rs6
-rw-r--r--src/api/s3_bucket.rs87
4 files changed, 99 insertions, 2 deletions
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<Garage>, req: Request<Body>) -> Result<Response<Body>, 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<roxmltree::Error> for Error {
}
}
+impl From<quick_xml::de::DeError> 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<Bucket>,
+}
+#[derive(Debug, Serialize, PartialEq)]
+struct ListAllMyBucketsResult {
+ #[serde(rename = "Buckets")]
+ buckets: BucketList,
+ #[serde(rename = "Owner")]
+ owner: Owner,
+}
+
pub fn handle_get_bucket_location(garage: Arc<Garage>) -> Result<Response<Body>, Error> {
let mut xml = String::new();
@@ -22,3 +73,39 @@ pub fn handle_get_bucket_location(garage: Arc<Garage>) -> Result<Response<Body>,
.header("Content-Type", "application/xml")
.body(Body::from(xml.into_bytes()))?)
}
+
+pub fn handle_list_buckets(api_key: &Key) -> Result<Response<Body>, 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#"<?xml version="1.0" encoding="UTF-8"?>"#.to_string();
+ xml.push_str(&to_string(&list_buckets)?);
+ trace!("xml: {}", xml);
+
+ Ok(Response::builder()
+ .header("Content-Type", "application/xml")
+ .body(Body::from(xml))?)
+}