more complete admin API #298
14 changed files with 203 additions and 13 deletions
|
@ -12,7 +12,7 @@ use garage_util::error::Error as GarageError;
|
|||
|
||||
use garage_model::garage::Garage;
|
||||
|
||||
use crate::s3::error::*;
|
||||
use crate::k2v::error::*;
|
||||
use crate::generic_server::*;
|
||||
|
||||
use crate::signature::payload::check_payload_signature;
|
||||
|
@ -84,7 +84,8 @@ impl ApiHandler for K2VApiServer {
|
|||
|
||||
// The OPTIONS method is procesed early, before we even check for an API key
|
||||
if let Endpoint::Options = endpoint {
|
||||
return handle_options_s3api(garage, &req, Some(bucket_name)).await;
|
||||
return Ok(handle_options_s3api(garage, &req, Some(bucket_name)).await
|
||||
.ok_or_bad_request("Error handling OPTIONS")?);
|
||||
}
|
||||
|
||||
let (api_key, mut content_sha256) = check_payload_signature(&garage, "k2v", &req).await?;
|
||||
|
@ -126,7 +127,8 @@ impl ApiHandler for K2VApiServer {
|
|||
// are always preflighted, i.e. the browser should make
|
||||
// an OPTIONS call before to check it is allowed
|
||||
let matching_cors_rule = match *req.method() {
|
||||
Method::GET | Method::HEAD | Method::POST => find_matching_cors_rule(&bucket, &req)?,
|
||||
Method::GET | Method::HEAD | Method::POST => find_matching_cors_rule(&bucket, &req)
|
||||
.ok_or_internal_error("Error looking up CORS rule")?,
|
||||
_ => None,
|
||||
};
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ use garage_model::garage::Garage;
|
|||
use garage_model::k2v::causality::*;
|
||||
use garage_model::k2v::item_table::*;
|
||||
|
||||
use crate::s3::error::*;
|
||||
use crate::k2v::error::*;
|
||||
use crate::helpers::*;
|
||||
use crate::k2v::range::read_range;
|
||||
|
||||
|
|
118
src/api/k2v/error.rs
Normal file
118
src/api/k2v/error.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
use err_derive::Error;
|
||||
use hyper::header::HeaderValue;
|
||||
use hyper::{Body, HeaderMap, StatusCode};
|
||||
|
||||
use garage_model::helper::error::Error as HelperError;
|
||||
|
||||
use crate::common_error::CommonError;
|
||||
pub use crate::common_error::{OkOrBadRequest, OkOrInternalError};
|
||||
use crate::generic_server::ApiError;
|
||||
use crate::signature::error::Error as SignatureError;
|
||||
|
||||
/// Errors of this crate
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(display = "{}", _0)]
|
||||
/// Error from common error
|
||||
CommonError(CommonError),
|
||||
|
||||
// Category: cannot process
|
||||
/// No proper api key was used, or the signature was invalid
|
||||
#[error(display = "Forbidden: {}", _0)]
|
||||
Forbidden(String),
|
||||
|
||||
/// Authorization Header Malformed
|
||||
#[error(display = "Authorization header malformed, expected scope: {}", _0)]
|
||||
AuthorizationHeaderMalformed(String),
|
||||
|
||||
/// The object requested don't exists
|
||||
#[error(display = "Key not found")]
|
||||
NoSuchKey,
|
||||
|
||||
/// The bucket requested don't exists
|
||||
#[error(display = "Bucket not found")]
|
||||
NoSuchBucket,
|
||||
|
||||
/// Some base64 encoded data was badly encoded
|
||||
#[error(display = "Invalid base64: {}", _0)]
|
||||
InvalidBase64(#[error(source)] base64::DecodeError),
|
||||
|
||||
/// The client sent a header with invalid value
|
||||
#[error(display = "Invalid header value: {}", _0)]
|
||||
InvalidHeader(#[error(source)] hyper::header::ToStrError),
|
||||
|
||||
/// The client asked for an invalid return format (invalid Accept header)
|
||||
#[error(display = "Not acceptable: {}", _0)]
|
||||
NotAcceptable(String),
|
||||
|
||||
/// The request contained an invalid UTF-8 sequence in its path or in other parameters
|
||||
#[error(display = "Invalid UTF-8: {}", _0)]
|
||||
InvalidUtf8Str(#[error(source)] std::str::Utf8Error),
|
||||
}
|
||||
|
||||
impl<T> From<T> for Error
|
||||
where
|
||||
CommonError: From<T>,
|
||||
{
|
||||
fn from(err: T) -> Self {
|
||||
Error::CommonError(CommonError::from(err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HelperError> for Error {
|
||||
fn from(err: HelperError) -> Self {
|
||||
match err {
|
||||
HelperError::Internal(i) => Self::CommonError(CommonError::InternalError(i)),
|
||||
HelperError::BadRequest(b) => Self::CommonError(CommonError::BadRequest(b)),
|
||||
e => Self::CommonError(CommonError::BadRequest(format!("{}", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SignatureError> for Error {
|
||||
fn from(err: SignatureError) -> Self {
|
||||
match err {
|
||||
SignatureError::CommonError(c) => Self::CommonError(c),
|
||||
SignatureError::AuthorizationHeaderMalformed(c) => Self::AuthorizationHeaderMalformed(c),
|
||||
SignatureError::Forbidden(f) => Self::Forbidden(f),
|
||||
SignatureError::InvalidUtf8Str(i) => Self::InvalidUtf8Str(i),
|
||||
SignatureError::InvalidHeader(h) => Self::InvalidHeader(h),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
//pub fn internal_error<M: ToString>(msg: M) -> Self {
|
||||
// Self::CommonError(CommonError::InternalError(GarageError::Message(
|
||||
// msg.to_string(),
|
||||
// )))
|
||||
//}
|
||||
|
||||
pub fn bad_request<M: ToString>(msg: M) -> Self {
|
||||
Self::CommonError(CommonError::BadRequest(msg.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ApiError for Error {
|
||||
/// Get the HTTP status code that best represents the meaning of the error for the client
|
||||
fn http_status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
Error::CommonError(c) => c.http_status_code(),
|
||||
Error::NoSuchKey | Error::NoSuchBucket => StatusCode::NOT_FOUND,
|
||||
Error::Forbidden(_) => StatusCode::FORBIDDEN,
|
||||
Error::NotAcceptable(_) => StatusCode::NOT_ACCEPTABLE,
|
||||
_ => StatusCode::BAD_REQUEST,
|
||||
}
|
||||
}
|
||||
|
||||
fn add_http_headers(&self, _header_map: &mut HeaderMap<HeaderValue>) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
fn http_body(&self, garage_region: &str, path: &str) -> Body {
|
||||
Body::from(format!(
|
||||
"ERROR: {}\n\ngarage region: {}\npath: {}",
|
||||
self, garage_region, path
|
||||
))
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ use garage_table::util::*;
|
|||
use garage_model::garage::Garage;
|
||||
use garage_model::k2v::counter_table::{BYTES, CONFLICTS, ENTRIES, VALUES};
|
||||
|
||||
use crate::s3::error::*;
|
||||
use crate::k2v::error::*;
|
||||
use crate::k2v::range::read_range;
|
||||
|
||||
pub async fn handle_read_index(
|
||||
|
|
|
@ -10,7 +10,7 @@ use garage_model::garage::Garage;
|
|||
use garage_model::k2v::causality::*;
|
||||
use garage_model::k2v::item_table::*;
|
||||
|
||||
use crate::s3::error::*;
|
||||
use crate::k2v::error::*;
|
||||
|
||||
pub const X_GARAGE_CAUSALITY_TOKEN: &str = "X-Garage-Causality-Token";
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
pub mod api_server;
|
||||
mod error;
|
||||
mod router;
|
||||
|
||||
mod batch;
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::sync::Arc;
|
|||
use garage_table::replication::TableShardedReplication;
|
||||
use garage_table::*;
|
||||
|
||||
use crate::s3::error::*;
|
||||
use crate::k2v::error::*;
|
||||
use crate::helpers::key_after_prefix;
|
||||
|
||||
/// Read range in a Garage table.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::s3::error::*;
|
||||
use crate::k2v::error::*;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
|
|
|
@ -119,7 +119,8 @@ impl ApiHandler for S3ApiServer {
|
|||
return handle_post_object(garage, req, bucket_name.unwrap()).await;
|
||||
}
|
||||
if let Endpoint::Options = endpoint {
|
||||
return handle_options_s3api(garage, &req, bucket_name).await;
|
||||
return handle_options_s3api(garage, &req, bucket_name).await
|
||||
.map_err(Error::from);
|
||||
}
|
||||
|
||||
let (api_key, mut content_sha256) = check_payload_signature(&garage, "s3", &req).await?;
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::common_error::CommonError;
|
|||
pub use crate::common_error::{OkOrBadRequest, OkOrInternalError};
|
||||
use crate::generic_server::ApiError;
|
||||
use crate::s3::xml as s3_xml;
|
||||
use crate::signature::error::Error as SignatureError;
|
||||
|
||||
/// Errors of this crate
|
||||
#[derive(Debug, Error)]
|
||||
|
@ -134,6 +135,18 @@ impl From<HelperError> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<SignatureError> for Error {
|
||||
fn from(err: SignatureError) -> Self {
|
||||
match err {
|
||||
SignatureError::CommonError(c) => Self::CommonError(c),
|
||||
SignatureError::AuthorizationHeaderMalformed(c) => Self::AuthorizationHeaderMalformed(c),
|
||||
SignatureError::Forbidden(f) => Self::Forbidden(f),
|
||||
SignatureError::InvalidUtf8Str(i) => Self::InvalidUtf8Str(i),
|
||||
SignatureError::InvalidHeader(h) => Self::InvalidHeader(h),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<multer::Error> for Error {
|
||||
fn from(err: multer::Error) -> Self {
|
||||
Self::bad_request(err)
|
||||
|
|
54
src/api/signature/error.rs
Normal file
54
src/api/signature/error.rs
Normal file
|
@ -0,0 +1,54 @@
|
|||
use err_derive::Error;
|
||||
|
||||
use garage_util::error::Error as GarageError;
|
||||
|
||||
use crate::common_error::CommonError;
|
||||
pub use crate::common_error::{OkOrBadRequest, OkOrInternalError};
|
||||
|
||||
/// Errors of this crate
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(display = "{}", _0)]
|
||||
/// Error from common error
|
||||
CommonError(CommonError),
|
||||
|
||||
/// Authorization Header Malformed
|
||||
#[error(display = "Authorization header malformed, expected scope: {}", _0)]
|
||||
AuthorizationHeaderMalformed(String),
|
||||
|
||||
/// No proper api key was used, or the signature was invalid
|
||||
#[error(display = "Forbidden: {}", _0)]
|
||||
Forbidden(String),
|
||||
|
||||
// Category: bad request
|
||||
/// The request contained an invalid UTF-8 sequence in its path or in other parameters
|
||||
#[error(display = "Invalid UTF-8: {}", _0)]
|
||||
InvalidUtf8Str(#[error(source)] std::str::Utf8Error),
|
||||
|
||||
/// The client sent a header with invalid value
|
||||
#[error(display = "Invalid header value: {}", _0)]
|
||||
InvalidHeader(#[error(source)] hyper::header::ToStrError),
|
||||
}
|
||||
|
||||
impl<T> From<T> for Error
|
||||
where
|
||||
CommonError: From<T>,
|
||||
{
|
||||
fn from(err: T) -> Self {
|
||||
Error::CommonError(CommonError::from(err))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Error {
|
||||
pub fn internal_error<M: ToString>(msg: M) -> Self {
|
||||
Self::CommonError(CommonError::InternalError(GarageError::Message(
|
||||
msg.to_string(),
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn bad_request<M: ToString>(msg: M) -> Self {
|
||||
Self::CommonError(CommonError::BadRequest(msg.to_string()))
|
||||
}
|
||||
}
|
||||
|
|
@ -4,11 +4,12 @@ use sha2::Sha256;
|
|||
|
||||
use garage_util::data::{sha256sum, Hash};
|
||||
|
||||
use crate::s3::error::*;
|
||||
|
||||
pub mod error;
|
||||
pub mod payload;
|
||||
pub mod streaming;
|
||||
|
||||
use error::*;
|
||||
|
||||
pub const SHORT_DATE: &str = "%Y%m%d";
|
||||
pub const LONG_DATETIME: &str = "%Y%m%dT%H%M%SZ";
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ use super::LONG_DATETIME;
|
|||
use super::{compute_scope, signing_hmac};
|
||||
|
||||
use crate::encoding::uri_encode;
|
||||
use crate::s3::error::*;
|
||||
use crate::signature::error::*;
|
||||
|
||||
pub async fn check_payload_signature(
|
||||
garage: &Garage,
|
||||
|
|
|
@ -12,7 +12,7 @@ use garage_util::data::Hash;
|
|||
|
||||
use super::{compute_scope, sha256sum, HmacSha256, LONG_DATETIME};
|
||||
|
||||
use crate::s3::error::*;
|
||||
use crate::signature::error::*;
|
||||
|
||||
pub fn parse_streaming_body(
|
||||
api_key: &Key,
|
||||
|
|
Loading…
Reference in a new issue