aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2022-05-11 10:27:40 +0200
committerAlex Auvolat <alex@adnab.me>2022-05-11 10:27:40 +0200
commitf97a7845e9e9ab68c3b8afc0d1091765ed11439c (patch)
treea3be49b225bf902e3ed4c8cacb849faa84aa106c
parentbb6ec9ebd979c168091c7b00cc8b97da4a1a8dc9 (diff)
downloadgarage-f97a7845e9e9ab68c3b8afc0d1091765ed11439c.tar.gz
garage-f97a7845e9e9ab68c3b8afc0d1091765ed11439c.zip
Add API access key admin endpoints
-rw-r--r--doc/drafts/admin-api.md49
-rw-r--r--src/api/admin/api_server.rs2
-rw-r--r--src/api/admin/router.rs64
-rw-r--r--src/api/router_macros.rs23
-rw-r--r--src/api/s3/router.rs3
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};