more complete admin API #298
6 changed files with 23 additions and 29 deletions
|
@ -62,7 +62,7 @@ impl Endpoint {
|
||||||
.unwrap_or((path.to_owned(), ""));
|
.unwrap_or((path.to_owned(), ""));
|
||||||
|
|
||||||
if bucket.is_empty() {
|
if bucket.is_empty() {
|
||||||
return Err(Error::bad_request("Missing bucket name".to_owned()));
|
return Err(Error::bad_request("Missing bucket name"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if *req.method() == Method::OPTIONS {
|
if *req.method() == Method::OPTIONS {
|
||||||
|
@ -83,7 +83,7 @@ impl Endpoint {
|
||||||
Method::PUT => Self::from_put(partition_key, &mut query)?,
|
Method::PUT => Self::from_put(partition_key, &mut query)?,
|
||||||
Method::DELETE => Self::from_delete(partition_key, &mut query)?,
|
Method::DELETE => Self::from_delete(partition_key, &mut query)?,
|
||||||
_ if req.method() == method_search => Self::from_search(partition_key, &mut query)?,
|
_ if req.method() == method_search => Self::from_search(partition_key, &mut query)?,
|
||||||
_ => return Err(Error::bad_request("Unknown method".to_owned())),
|
_ => return Err(Error::bad_request("Unknown method")),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(message) = query.nonempty_message() {
|
if let Some(message) = query.nonempty_message() {
|
||||||
|
|
|
@ -594,7 +594,7 @@ impl ListObjectsQuery {
|
||||||
.ok_or_bad_request("Invalid continuation token")?,
|
.ok_or_bad_request("Invalid continuation token")?,
|
||||||
)?,
|
)?,
|
||||||
}),
|
}),
|
||||||
_ => Err(Error::bad_request("Invalid continuation token".to_string())),
|
_ => Err(Error::bad_request("Invalid continuation token")),
|
||||||
},
|
},
|
||||||
|
|
||||||
// StartAfter has defined semantics in the spec:
|
// StartAfter has defined semantics in the spec:
|
||||||
|
|
|
@ -47,9 +47,7 @@ pub async fn handle_post_object(
|
||||||
let field = if let Some(field) = multipart.next_field().await? {
|
let field = if let Some(field) = multipart.next_field().await? {
|
||||||
field
|
field
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::bad_request(
|
return Err(Error::bad_request("Request did not contain a file"));
|
||||||
"Request did not contain a file".to_owned(),
|
|
||||||
));
|
|
||||||
};
|
};
|
||||||
let name: HeaderName = if let Some(Ok(name)) = field.name().map(TryInto::try_into) {
|
let name: HeaderName = if let Some(Ok(name)) = field.name().map(TryInto::try_into) {
|
||||||
name
|
name
|
||||||
|
@ -66,7 +64,7 @@ pub async fn handle_post_object(
|
||||||
"acl" => {
|
"acl" => {
|
||||||
if params.insert("x-amz-acl", content).is_some() {
|
if params.insert("x-amz-acl", content).is_some() {
|
||||||
return Err(Error::bad_request(
|
return Err(Error::bad_request(
|
||||||
"Field 'acl' provided more than one time".to_string(),
|
"Field 'acl' provided more than one time",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,9 +141,7 @@ pub async fn handle_post_object(
|
||||||
.ok_or_bad_request("Invalid expiration date")?
|
.ok_or_bad_request("Invalid expiration date")?
|
||||||
.into();
|
.into();
|
||||||
if Utc::now() - expiration > Duration::zero() {
|
if Utc::now() - expiration > Duration::zero() {
|
||||||
return Err(Error::bad_request(
|
return Err(Error::bad_request("Expiration date is in the paste"));
|
||||||
"Expiration date is in the paste".to_string(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut conditions = decoded_policy.into_conditions()?;
|
let mut conditions = decoded_policy.into_conditions()?;
|
||||||
|
@ -324,7 +320,7 @@ impl Policy {
|
||||||
match condition {
|
match condition {
|
||||||
PolicyCondition::Equal(map) => {
|
PolicyCondition::Equal(map) => {
|
||||||
if map.len() != 1 {
|
if map.len() != 1 {
|
||||||
return Err(Error::bad_request("Invalid policy item".to_owned()));
|
return Err(Error::bad_request("Invalid policy item"));
|
||||||
}
|
}
|
||||||
let (mut k, v) = map.into_iter().next().expect("size was verified");
|
let (mut k, v) = map.into_iter().next().expect("size was verified");
|
||||||
k.make_ascii_lowercase();
|
k.make_ascii_lowercase();
|
||||||
|
@ -332,7 +328,7 @@ impl Policy {
|
||||||
}
|
}
|
||||||
PolicyCondition::OtherOp([cond, mut key, value]) => {
|
PolicyCondition::OtherOp([cond, mut key, value]) => {
|
||||||
if key.remove(0) != '$' {
|
if key.remove(0) != '$' {
|
||||||
return Err(Error::bad_request("Invalid policy item".to_owned()));
|
return Err(Error::bad_request("Invalid policy item"));
|
||||||
}
|
}
|
||||||
key.make_ascii_lowercase();
|
key.make_ascii_lowercase();
|
||||||
match cond.as_str() {
|
match cond.as_str() {
|
||||||
|
@ -345,7 +341,7 @@ impl Policy {
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(Operation::StartsWith(value));
|
.push(Operation::StartsWith(value));
|
||||||
}
|
}
|
||||||
_ => return Err(Error::bad_request("Invalid policy item".to_owned())),
|
_ => return Err(Error::bad_request("Invalid policy item")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PolicyCondition::SizeRange(key, min, max) => {
|
PolicyCondition::SizeRange(key, min, max) => {
|
||||||
|
@ -353,7 +349,7 @@ impl Policy {
|
||||||
length.0 = length.0.max(min);
|
length.0 = length.0.max(min);
|
||||||
length.1 = length.1.min(max);
|
length.1 = length.1.min(max);
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::bad_request("Invalid policy item".to_owned()));
|
return Err(Error::bad_request("Invalid policy item"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -419,14 +415,14 @@ where
|
||||||
// optimization to fail early when we know before the end it's too long
|
// optimization to fail early when we know before the end it's too long
|
||||||
if self.length.end() < &self.read {
|
if self.length.end() < &self.read {
|
||||||
return Poll::Ready(Some(Err(Error::bad_request(
|
return Poll::Ready(Some(Err(Error::bad_request(
|
||||||
"File size does not match policy".to_owned(),
|
"File size does not match policy",
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Poll::Ready(None) => {
|
Poll::Ready(None) => {
|
||||||
if !self.length.contains(&self.read) {
|
if !self.length.contains(&self.read) {
|
||||||
return Poll::Ready(Some(Err(Error::bad_request(
|
return Poll::Ready(Some(Err(Error::bad_request(
|
||||||
"File size does not match policy".to_owned(),
|
"File size does not match policy",
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ fn ensure_checksum_matches(
|
||||||
if let Some(expected_sha256) = content_sha256 {
|
if let Some(expected_sha256) = content_sha256 {
|
||||||
if expected_sha256 != data_sha256sum {
|
if expected_sha256 != data_sha256sum {
|
||||||
return Err(Error::bad_request(
|
return Err(Error::bad_request(
|
||||||
"Unable to validate x-amz-content-sha256".to_string(),
|
"Unable to validate x-amz-content-sha256",
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
trace!("Successfully validated x-amz-content-sha256");
|
trace!("Successfully validated x-amz-content-sha256");
|
||||||
|
@ -192,9 +192,7 @@ fn ensure_checksum_matches(
|
||||||
}
|
}
|
||||||
if let Some(expected_md5) = content_md5 {
|
if let Some(expected_md5) = content_md5 {
|
||||||
if expected_md5.trim_matches('"') != base64::encode(data_md5sum) {
|
if expected_md5.trim_matches('"') != base64::encode(data_md5sum) {
|
||||||
return Err(Error::bad_request(
|
return Err(Error::bad_request("Unable to validate content-md5"));
|
||||||
"Unable to validate content-md5".to_string(),
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
trace!("Successfully validated content-md5");
|
trace!("Successfully validated content-md5");
|
||||||
}
|
}
|
||||||
|
@ -513,7 +511,7 @@ pub async fn handle_complete_multipart_upload(
|
||||||
|
|
||||||
let version = version.ok_or(Error::NoSuchKey)?;
|
let version = version.ok_or(Error::NoSuchKey)?;
|
||||||
if version.blocks.is_empty() {
|
if version.blocks.is_empty() {
|
||||||
return Err(Error::bad_request("No data was uploaded".to_string()));
|
return Err(Error::bad_request("No data was uploaded"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let headers = match object_version.state {
|
let headers = match object_version.state {
|
||||||
|
|
|
@ -342,7 +342,7 @@ impl Endpoint {
|
||||||
Method::POST => Self::from_post(key, &mut query)?,
|
Method::POST => Self::from_post(key, &mut query)?,
|
||||||
Method::PUT => Self::from_put(key, &mut query, req.headers())?,
|
Method::PUT => Self::from_put(key, &mut query, req.headers())?,
|
||||||
Method::DELETE => Self::from_delete(key, &mut query)?,
|
Method::DELETE => Self::from_delete(key, &mut query)?,
|
||||||
_ => return Err(Error::bad_request("Unknown method".to_owned())),
|
_ => return Err(Error::bad_request("Unknown method")),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(message) = query.nonempty_message() {
|
if let Some(message) = query.nonempty_message() {
|
||||||
|
|
|
@ -170,7 +170,7 @@ impl WebsiteConfiguration {
|
||||||
|| self.routing_rules.is_some())
|
|| self.routing_rules.is_some())
|
||||||
{
|
{
|
||||||
return Err(Error::bad_request(
|
return Err(Error::bad_request(
|
||||||
"Bad XML: can't have RedirectAllRequestsTo and other fields".to_owned(),
|
"Bad XML: can't have RedirectAllRequestsTo and other fields",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(ref ed) = self.error_document {
|
if let Some(ref ed) = self.error_document {
|
||||||
|
@ -216,7 +216,7 @@ impl Key {
|
||||||
pub fn validate(&self) -> Result<(), Error> {
|
pub fn validate(&self) -> Result<(), Error> {
|
||||||
if self.key.0.is_empty() {
|
if self.key.0.is_empty() {
|
||||||
Err(Error::bad_request(
|
Err(Error::bad_request(
|
||||||
"Bad XML: error document specified but empty".to_owned(),
|
"Bad XML: error document specified but empty",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -228,7 +228,7 @@ impl Suffix {
|
||||||
pub fn validate(&self) -> Result<(), Error> {
|
pub fn validate(&self) -> Result<(), Error> {
|
||||||
if self.suffix.0.is_empty() | self.suffix.0.contains('/') {
|
if self.suffix.0.is_empty() | self.suffix.0.contains('/') {
|
||||||
Err(Error::bad_request(
|
Err(Error::bad_request(
|
||||||
"Bad XML: index document is empty or contains /".to_owned(),
|
"Bad XML: index document is empty or contains /",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -240,7 +240,7 @@ impl Target {
|
||||||
pub fn validate(&self) -> Result<(), Error> {
|
pub fn validate(&self) -> Result<(), Error> {
|
||||||
if let Some(ref protocol) = self.protocol {
|
if let Some(ref protocol) = self.protocol {
|
||||||
if protocol.0 != "http" && protocol.0 != "https" {
|
if protocol.0 != "http" && protocol.0 != "https" {
|
||||||
return Err(Error::bad_request("Bad XML: invalid protocol".to_owned()));
|
return Err(Error::bad_request("Bad XML: invalid protocol"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -263,18 +263,18 @@ impl Redirect {
|
||||||
if self.replace_prefix.is_some() {
|
if self.replace_prefix.is_some() {
|
||||||
if self.replace_full.is_some() {
|
if self.replace_full.is_some() {
|
||||||
return Err(Error::bad_request(
|
return Err(Error::bad_request(
|
||||||
"Bad XML: both ReplaceKeyPrefixWith and ReplaceKeyWith are set".to_owned(),
|
"Bad XML: both ReplaceKeyPrefixWith and ReplaceKeyWith are set",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if !has_prefix {
|
if !has_prefix {
|
||||||
return Err(Error::bad_request(
|
return Err(Error::bad_request(
|
||||||
"Bad XML: ReplaceKeyPrefixWith is set, but KeyPrefixEquals isn't".to_owned(),
|
"Bad XML: ReplaceKeyPrefixWith is set, but KeyPrefixEquals isn't",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(ref protocol) = self.protocol {
|
if let Some(ref protocol) = self.protocol {
|
||||||
if protocol.0 != "http" && protocol.0 != "https" {
|
if protocol.0 != "http" && protocol.0 != "https" {
|
||||||
return Err(Error::bad_request("Bad XML: invalid protocol".to_owned()));
|
return Err(Error::bad_request("Bad XML: invalid protocol"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO there are probably more invalide cases, but which ones?
|
// TODO there are probably more invalide cases, but which ones?
|
||||||
|
|
Loading…
Reference in a new issue