diff options
author | Alex Auvolat <alex@adnab.me> | 2022-05-11 10:27:40 +0200 |
---|---|---|
committer | Alex Auvolat <alex@adnab.me> | 2022-05-11 10:27:40 +0200 |
commit | f97a7845e9e9ab68c3b8afc0d1091765ed11439c (patch) | |
tree | a3be49b225bf902e3ed4c8cacb849faa84aa106c | |
parent | bb6ec9ebd979c168091c7b00cc8b97da4a1a8dc9 (diff) | |
download | garage-f97a7845e9e9ab68c3b8afc0d1091765ed11439c.tar.gz garage-f97a7845e9e9ab68c3b8afc0d1091765ed11439c.zip |
Add API access key admin endpoints
-rw-r--r-- | doc/drafts/admin-api.md | 49 | ||||
-rw-r--r-- | src/api/admin/api_server.rs | 2 | ||||
-rw-r--r-- | src/api/admin/router.rs | 64 | ||||
-rw-r--r-- | src/api/router_macros.rs | 23 | ||||
-rw-r--r-- | src/api/s3/router.rs | 3 |
5 files changed, 117 insertions, 24 deletions
diff --git a/doc/drafts/admin-api.md b/doc/drafts/admin-api.md index ab24e18f..baf87e61 100644 --- a/doc/drafts/admin-api.md +++ b/doc/drafts/admin-api.md @@ -209,3 +209,52 @@ Similarly to the CLI, the body must include the incremented version number, which MUST be 1 + the value of the currently existing layout in the cluster. + +## Access key operations + +### ListKeys `GET /key` + +Returns all API access keys in the cluster. + +Example response: + +```json +#TODO +``` + +### CreateKey `POST /key` + +Creates a new API access key. + +Request body format: + +```json +{ + "name": "NameOfMyKey" +} +``` + +### GetKeyInfo `GET /key?id=<acces key id>` + +Returns information about the requested API access key. + +Example response: + +```json +#TODO +``` + +### DeleteKey `DELETE /key?id=<acces key id>` + +Deletes an API access key. + +### UpdateKey `POST /key?id=<acces key id>` + +Updates information about the specified API access key. + +Request body format: + +```json +#TODO +``` + diff --git a/src/api/admin/api_server.rs b/src/api/admin/api_server.rs index d008f10a..3ae9f591 100644 --- a/src/api/admin/api_server.rs +++ b/src/api/admin/api_server.rs @@ -129,12 +129,10 @@ impl ApiHandler for AdminApiServer { Endpoint::UpdateClusterLayout => handle_update_cluster_layout(&self.garage, req).await, Endpoint::ApplyClusterLayout => handle_apply_cluster_layout(&self.garage, req).await, Endpoint::RevertClusterLayout => handle_revert_cluster_layout(&self.garage, req).await, - /* _ => Err(Error::NotImplemented(format!( "Admin endpoint {} not implemented yet", endpoint.name() ))), - */ } } } diff --git a/src/api/admin/router.rs b/src/api/admin/router.rs index 714af1e8..7ff34aaa 100644 --- a/src/api/admin/router.rs +++ b/src/api/admin/router.rs @@ -1,8 +1,9 @@ -use crate::error::*; +use std::borrow::Cow; use hyper::{Method, Request}; -use crate::router_macros::router_match; +use crate::error::*; +use crate::router_macros::*; pub enum Authorization { MetricsToken, @@ -21,6 +22,17 @@ pub enum Endpoint { UpdateClusterLayout, ApplyClusterLayout, RevertClusterLayout, + ListKeys, + CreateKey, + GetKeyInfo { + id: String, + }, + DeleteKey { + id: String, + }, + UpdateKey { + id: String, + }, }} impl Endpoint { @@ -28,24 +40,32 @@ impl Endpoint { /// possibly extracted from the Host header. /// Returns Self plus bucket name, if endpoint is not Endpoint::ListBuckets pub fn from_request<T>(req: &Request<T>) -> Result<Self, Error> { - let path = req.uri().path(); - - use Endpoint::*; - let res = match (req.method(), path) { - (&Method::OPTIONS, _) => Options, - (&Method::GET, "/metrics") => Metrics, - (&Method::GET, "/status") => GetClusterStatus, - (&Method::GET, "/layout") => GetClusterLayout, - (&Method::POST, "/layout") => UpdateClusterLayout, - (&Method::POST, "/layout/apply") => ApplyClusterLayout, - (&Method::POST, "/layout/revert") => RevertClusterLayout, - (m, p) => { - return Err(Error::BadRequest(format!( - "Unknown API endpoint: {} {}", - m, p - ))) - } - }; + let uri = req.uri(); + let path = uri.path(); + let query = uri.query(); + + let mut query = QueryParameters::from_query(query.unwrap_or_default())?; + + let res = router_match!(@gen_path_parser (req.method(), path, query) [ + OPTIONS _ => Options, + GET "/metrics" => Metrics, + GET "/status" => GetClusterStatus, + // Layout endpoints + GET "/layout" => GetClusterLayout, + POST "/layout" => UpdateClusterLayout, + POST "/layout/apply" => ApplyClusterLayout, + POST "/layout/revert" => RevertClusterLayout, + // API key endpoints + GET "/key" if id => GetKeyInfo (query::id), + POST "/key" if id => UpdateKey (query::id), + POST "/key" => CreateKey, + DELETE "/key" if id => DeleteKey (query::id), + GET "/key" => ListKeys, + ]); + + if let Some(message) = query.nonempty_message() { + debug!("Unused query parameter: {}", message) + } Ok(res) } @@ -57,3 +77,7 @@ impl Endpoint { } } } + +generateQueryParameters! { + "id" => id +} diff --git a/src/api/router_macros.rs b/src/api/router_macros.rs index 8471407c..a3e885e6 100644 --- a/src/api/router_macros.rs +++ b/src/api/router_macros.rs @@ -23,6 +23,29 @@ macro_rules! router_match { _ => None } }}; + (@gen_path_parser ($method:expr, $reqpath:expr, $query:expr) + [ + $($meth:ident $path:pat $(if $required:ident)? => $api:ident $(($($conv:ident :: $param:ident),*))?,)* + ]) => {{ + { + use Endpoint::*; + match ($method, $reqpath) { + $( + (&Method::$meth, $path) if true $(&& $query.$required.is_some())? => $api { + $($( + $param: router_match!(@@parse_param $query, $conv, $param), + )*)? + }, + )* + (m, p) => { + return Err(Error::BadRequest(format!( + "Unknown API endpoint: {} {}", + m, p + ))) + } + } + } + }}; (@gen_parser ($keyword:expr, $key:ident, $query:expr, $header:expr), key: [$($kw_k:ident $(if $required_k:ident)? $(header $header_k:expr)? => $api_k:ident $(($($conv_k:ident :: $param_k:ident),*))?,)*], no_key: [$($kw_nk:ident $(if $required_nk:ident)? $(if_header $header_nk:expr)? => $api_nk:ident $(($($conv_nk:ident :: $param_nk:ident),*))?,)*]) => {{ diff --git a/src/api/s3/router.rs b/src/api/s3/router.rs index 0525c649..446ceb54 100644 --- a/src/api/s3/router.rs +++ b/src/api/s3/router.rs @@ -1,10 +1,9 @@ -use crate::error::{Error, OkOrBadRequest}; - use std::borrow::Cow; use hyper::header::HeaderValue; use hyper::{HeaderMap, Method, Request}; +use crate::error::{Error, OkOrBadRequest}; use crate::helpers::Authorization; use crate::router_macros::{generateQueryParameters, router_match}; |