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 garage_model::garage::Garage;
|
||||||
|
|
||||||
use crate::s3::error::*;
|
use crate::k2v::error::*;
|
||||||
use crate::generic_server::*;
|
use crate::generic_server::*;
|
||||||
|
|
||||||
use crate::signature::payload::check_payload_signature;
|
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
|
// The OPTIONS method is procesed early, before we even check for an API key
|
||||||
if let Endpoint::Options = endpoint {
|
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?;
|
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
|
// are always preflighted, i.e. the browser should make
|
||||||
// an OPTIONS call before to check it is allowed
|
// an OPTIONS call before to check it is allowed
|
||||||
let matching_cors_rule = match *req.method() {
|
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,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ use garage_model::garage::Garage;
|
||||||
use garage_model::k2v::causality::*;
|
use garage_model::k2v::causality::*;
|
||||||
use garage_model::k2v::item_table::*;
|
use garage_model::k2v::item_table::*;
|
||||||
|
|
||||||
use crate::s3::error::*;
|
use crate::k2v::error::*;
|
||||||
use crate::helpers::*;
|
use crate::helpers::*;
|
||||||
use crate::k2v::range::read_range;
|
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::garage::Garage;
|
||||||
use garage_model::k2v::counter_table::{BYTES, CONFLICTS, ENTRIES, VALUES};
|
use garage_model::k2v::counter_table::{BYTES, CONFLICTS, ENTRIES, VALUES};
|
||||||
|
|
||||||
use crate::s3::error::*;
|
use crate::k2v::error::*;
|
||||||
use crate::k2v::range::read_range;
|
use crate::k2v::range::read_range;
|
||||||
|
|
||||||
pub async fn handle_read_index(
|
pub async fn handle_read_index(
|
||||||
|
|
|
@ -10,7 +10,7 @@ use garage_model::garage::Garage;
|
||||||
use garage_model::k2v::causality::*;
|
use garage_model::k2v::causality::*;
|
||||||
use garage_model::k2v::item_table::*;
|
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";
|
pub const X_GARAGE_CAUSALITY_TOKEN: &str = "X-Garage-Causality-Token";
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod api_server;
|
pub mod api_server;
|
||||||
|
mod error;
|
||||||
mod router;
|
mod router;
|
||||||
|
|
||||||
mod batch;
|
mod batch;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::sync::Arc;
|
||||||
use garage_table::replication::TableShardedReplication;
|
use garage_table::replication::TableShardedReplication;
|
||||||
use garage_table::*;
|
use garage_table::*;
|
||||||
|
|
||||||
use crate::s3::error::*;
|
use crate::k2v::error::*;
|
||||||
use crate::helpers::key_after_prefix;
|
use crate::helpers::key_after_prefix;
|
||||||
|
|
||||||
/// Read range in a Garage table.
|
/// Read range in a Garage table.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::s3::error::*;
|
use crate::k2v::error::*;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,8 @@ impl ApiHandler for S3ApiServer {
|
||||||
return handle_post_object(garage, req, bucket_name.unwrap()).await;
|
return handle_post_object(garage, req, bucket_name.unwrap()).await;
|
||||||
}
|
}
|
||||||
if let Endpoint::Options = endpoint {
|
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?;
|
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};
|
pub use crate::common_error::{OkOrBadRequest, OkOrInternalError};
|
||||||
use crate::generic_server::ApiError;
|
use crate::generic_server::ApiError;
|
||||||
use crate::s3::xml as s3_xml;
|
use crate::s3::xml as s3_xml;
|
||||||
|
use crate::signature::error::Error as SignatureError;
|
||||||
|
|
||||||
/// Errors of this crate
|
/// Errors of this crate
|
||||||
#[derive(Debug, Error)]
|
#[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 {
|
impl From<multer::Error> for Error {
|
||||||
fn from(err: multer::Error) -> Self {
|
fn from(err: multer::Error) -> Self {
|
||||||
Self::bad_request(err)
|
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 garage_util::data::{sha256sum, Hash};
|
||||||
|
|
||||||
use crate::s3::error::*;
|
pub mod error;
|
||||||
|
|
||||||
pub mod payload;
|
pub mod payload;
|
||||||
pub mod streaming;
|
pub mod streaming;
|
||||||
|
|
||||||
|
use error::*;
|
||||||
|
|
||||||
pub const SHORT_DATE: &str = "%Y%m%d";
|
pub const SHORT_DATE: &str = "%Y%m%d";
|
||||||
pub const LONG_DATETIME: &str = "%Y%m%dT%H%M%SZ";
|
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 super::{compute_scope, signing_hmac};
|
||||||
|
|
||||||
use crate::encoding::uri_encode;
|
use crate::encoding::uri_encode;
|
||||||
use crate::s3::error::*;
|
use crate::signature::error::*;
|
||||||
|
|
||||||
pub async fn check_payload_signature(
|
pub async fn check_payload_signature(
|
||||||
garage: &Garage,
|
garage: &Garage,
|
||||||
|
|
|
@ -12,7 +12,7 @@ use garage_util::data::Hash;
|
||||||
|
|
||||||
use super::{compute_scope, sha256sum, HmacSha256, LONG_DATETIME};
|
use super::{compute_scope, sha256sum, HmacSha256, LONG_DATETIME};
|
||||||
|
|
||||||
use crate::s3::error::*;
|
use crate::signature::error::*;
|
||||||
|
|
||||||
pub fn parse_streaming_body(
|
pub fn parse_streaming_body(
|
||||||
api_key: &Key,
|
api_key: &Key,
|
||||||
|
|
Loading…
Reference in a new issue