aboutsummaryrefslogtreecommitdiff
path: root/src/api/s3_bucket.rs
blob: 24ec6b98436903530401a5ba2f25b5e4e74a1a23 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use std::collections::HashMap;
use std::sync::Arc;

use hyper::{Body, Response};

use garage_model::garage::Garage;
use garage_model::key_table::Key;
use garage_table::util::EmptyKey;
use garage_util::crdt::*;
use garage_util::time::*;

use crate::error::*;
use crate::s3_xml;

pub fn handle_get_bucket_location(garage: Arc<Garage>) -> Result<Response<Body>, Error> {
	let loc = s3_xml::LocationConstraint {
		xmlns: (),
		region: garage.config.s3_api.s3_region.to_string(),
	};
	let xml = s3_xml::to_xml_with_header(&loc)?;

	Ok(Response::builder()
		.header("Content-Type", "application/xml")
		.body(Body::from(xml.into_bytes()))?)
}

pub fn handle_get_bucket_versioning() -> Result<Response<Body>, Error> {
	let versioning = s3_xml::VersioningConfiguration {
		xmlns: (),
		status: None,
	};

	let xml = s3_xml::to_xml_with_header(&versioning)?;

	Ok(Response::builder()
		.header("Content-Type", "application/xml")
		.body(Body::from(xml.into_bytes()))?)
}

pub async fn handle_list_buckets(garage: &Garage, api_key: &Key) -> Result<Response<Body>, Error> {
	let key_state = api_key.state.as_option().ok_or_internal_error(
		"Key should not be in deleted state at this point (internal error)",
	)?;

	// Collect buckets user has access to
	let ids = api_key
		.state
		.as_option()
		.unwrap()
		.authorized_buckets
		.items()
		.iter()
		.filter(|(_, perms)| perms.allow_read || perms.allow_write || perms.allow_owner)
		.map(|(id, _)| *id)
		.collect::<Vec<_>>();

	let mut buckets_by_id = HashMap::new();
	let mut aliases = HashMap::new();

	for bucket_id in ids.iter() {
		let bucket = garage.bucket_table.get(bucket_id, &EmptyKey).await?;
		if let Some(bucket) = bucket {
			if let Deletable::Present(param) = bucket.state {
				for (alias, _, active) in param.aliases.items() {
					if *active {
						let alias_ent = garage.bucket_alias_table.get(&EmptyKey, alias).await?;
						if let Some(alias_ent) = alias_ent {
							if let Some(alias_bucket) = alias_ent.state.get() {
								if alias_bucket == bucket_id {
									aliases.insert(alias_ent.name().to_string(), *bucket_id);
								}
							}
						}
					}
				}
				buckets_by_id.insert(bucket_id, param);
			}
		}
	}

	for (alias, _, id_opt) in key_state.local_aliases.items() {
		if let Some(id) = id_opt {
			aliases.insert(alias.clone(), *id);
		}
	}

	// Generate response
	let list_buckets = s3_xml::ListAllMyBucketsResult {
		owner: s3_xml::Owner {
			display_name: s3_xml::Value(api_key.name.get().to_string()),
			id: s3_xml::Value(api_key.key_id.to_string()),
		},
		buckets: s3_xml::BucketList {
			entries: aliases
				.iter()
				.filter_map(|(name, id)| buckets_by_id.get(id).map(|p| (name, id, p)))
				.map(|(name, _id, param)| s3_xml::Bucket {
					creation_date: s3_xml::Value(msec_to_rfc3339(param.creation_date)),
					name: s3_xml::Value(name.to_string()),
				})
				.collect(),
		},
	};

	let xml = s3_xml::to_xml_with_header(&list_buckets)?;
	trace!("xml: {}", xml);

	Ok(Response::builder()
		.header("Content-Type", "application/xml")
		.body(Body::from(xml))?)
}