mirror of
https://git.deuxfleurs.fr/Deuxfleurs/garage.git
synced 2026-05-05 06:46:09 +02:00
parent
f04af18193
commit
7bbb3ff9cf
@ -869,6 +869,40 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/IntrospectAdminToken": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Admin API token"
|
||||
],
|
||||
"description": "\nReturn information about the calling admin API token.\n ",
|
||||
"operationId": "IntrospectAdminToken",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "admin_token",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Information about the admin token",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/IntrospectAdminTokenResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v2/GetNodeInfo": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@ -2644,6 +2678,54 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"IntrospectAdminTokenResponse": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"expired",
|
||||
"scope"
|
||||
],
|
||||
"properties": {
|
||||
"created": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date-time",
|
||||
"description": "Creation date"
|
||||
},
|
||||
"expiration": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date-time",
|
||||
"description": "Expiration time and date, formatted according to RFC 3339"
|
||||
},
|
||||
"expired": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this admin token is expired already"
|
||||
},
|
||||
"id": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"description": "Identifier of the admin token (which is also a prefix of the full bearer token)"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the admin API token"
|
||||
},
|
||||
"scope": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"description": "Scope of the admin API token, a list of admin endpoint names (such as\n`GetClusterStatus`, etc), or the special value `*` to allow all\nadmin endpoints"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ImportKeyRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
||||
@ -175,6 +175,79 @@ impl RequestHandler for DeleteAdminTokenRequest {
|
||||
}
|
||||
}
|
||||
|
||||
impl RequestHandler for IntrospectAdminTokenRequest {
|
||||
type Response = IntrospectAdminTokenResponse;
|
||||
|
||||
async fn handle(
|
||||
self,
|
||||
garage: &Arc<Garage>,
|
||||
_admin: &Admin,
|
||||
) -> Result<IntrospectAdminTokenResponse, Error> {
|
||||
let now = now_msec();
|
||||
|
||||
if garage
|
||||
.config
|
||||
.admin
|
||||
.metrics_token
|
||||
.as_ref()
|
||||
.is_some_and(|s| s == &self.admin_token)
|
||||
{
|
||||
return Ok(IntrospectAdminTokenResponse {
|
||||
id: None,
|
||||
created: None,
|
||||
name: "metrics_token (from daemon configuration)".into(),
|
||||
expiration: None,
|
||||
expired: false,
|
||||
scope: vec!["Metrics".into()],
|
||||
});
|
||||
}
|
||||
|
||||
if garage
|
||||
.config
|
||||
.admin
|
||||
.admin_token
|
||||
.as_ref()
|
||||
.is_some_and(|s| s == &self.admin_token)
|
||||
{
|
||||
return Ok(IntrospectAdminTokenResponse {
|
||||
id: None,
|
||||
created: None,
|
||||
name: "admin_token (from daemon configuration)".into(),
|
||||
expiration: None,
|
||||
expired: false,
|
||||
scope: vec!["*".into()],
|
||||
});
|
||||
}
|
||||
|
||||
let (prefix, _) = self.admin_token.split_once('.').unwrap();
|
||||
|
||||
let candidates = garage
|
||||
.admin_token_table
|
||||
.get_range(
|
||||
&EmptyKey,
|
||||
None,
|
||||
Some(KeyFilter::MatchesAndNotDeleted(
|
||||
prefix.clone().parse().unwrap(),
|
||||
)),
|
||||
10,
|
||||
EnumerationOrder::Forward,
|
||||
)
|
||||
.await?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
if candidates.len() != 1 {
|
||||
return Err(Error::bad_request(format!(
|
||||
"{} matching admin tokens",
|
||||
candidates.len()
|
||||
)));
|
||||
}
|
||||
Ok(my_admin_token_info_results(
|
||||
&candidates.into_iter().next().unwrap(),
|
||||
now,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// ---- helpers ----
|
||||
|
||||
fn admin_token_info_results(token: &AdminApiToken, now: u64) -> GetAdminTokenInfoResponse {
|
||||
@ -233,3 +306,21 @@ fn apply_token_updates(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn my_admin_token_info_results(token: &AdminApiToken, now: u64) -> IntrospectAdminTokenResponse {
|
||||
let params = token.params().unwrap();
|
||||
|
||||
IntrospectAdminTokenResponse {
|
||||
id: Some(token.prefix.clone()),
|
||||
created: Some(
|
||||
DateTime::from_timestamp_millis(params.created as i64)
|
||||
.expect("invalid timestamp stored in db"),
|
||||
),
|
||||
name: params.name.get().to_string(),
|
||||
expiration: params.expiration.get().map(|x| {
|
||||
DateTime::from_timestamp_millis(x as i64).expect("invalid timestamp stored in db")
|
||||
}),
|
||||
expired: params.is_expired(now),
|
||||
scope: params.scope.get().0.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +56,7 @@ admin_endpoints![
|
||||
CreateAdminToken,
|
||||
UpdateAdminToken,
|
||||
DeleteAdminToken,
|
||||
IntrospectAdminToken,
|
||||
|
||||
// Layout operations
|
||||
GetClusterLayout,
|
||||
@ -391,6 +392,29 @@ pub struct DeleteAdminTokenRequest {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DeleteAdminTokenResponse;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, IntoParams)]
|
||||
pub struct IntrospectAdminTokenRequest {
|
||||
pub admin_token: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct IntrospectAdminTokenResponse {
|
||||
/// Identifier of the admin token (which is also a prefix of the full bearer token)
|
||||
pub id: Option<String>,
|
||||
/// Creation date
|
||||
pub created: Option<DateTime<Utc>>,
|
||||
/// Name of the admin API token
|
||||
pub name: String,
|
||||
/// Expiration time and date, formatted according to RFC 3339
|
||||
pub expiration: Option<DateTime<Utc>>,
|
||||
/// Whether this admin token is expired already
|
||||
pub expired: bool,
|
||||
/// Scope of the admin API token, a list of admin endpoint names (such as
|
||||
/// `GetClusterStatus`, etc), or the special value `*` to allow all
|
||||
/// admin endpoints
|
||||
pub scope: Vec<String>,
|
||||
}
|
||||
// **********************************************
|
||||
// Layout operations
|
||||
// **********************************************
|
||||
|
||||
@ -191,6 +191,20 @@ fn UpdateAdminToken() -> () {}
|
||||
)]
|
||||
fn DeleteAdminToken() -> () {}
|
||||
|
||||
#[utoipa::path(get,
|
||||
path = "/v2/IntrospectAdminToken",
|
||||
tag = "Admin API token",
|
||||
description = "
|
||||
Return information about the calling admin API token.
|
||||
",
|
||||
params(IntrospectAdminTokenRequest),
|
||||
responses(
|
||||
(status = 200, description = "Information about the admin token", body = IntrospectAdminTokenResponse),
|
||||
(status = 500, description = "Internal server error")
|
||||
),
|
||||
)]
|
||||
fn IntrospectAdminToken() -> () {}
|
||||
|
||||
// **********************************************
|
||||
// Layout operations
|
||||
// **********************************************
|
||||
@ -872,6 +886,7 @@ impl Modify for SecurityAddon {
|
||||
CreateAdminToken,
|
||||
UpdateAdminToken,
|
||||
DeleteAdminToken,
|
||||
IntrospectAdminToken,
|
||||
// Layout operations
|
||||
GetClusterLayout,
|
||||
GetClusterLayoutHistory,
|
||||
|
||||
@ -40,6 +40,7 @@ impl AdminApiRequest {
|
||||
POST CreateAdminToken (body),
|
||||
POST UpdateAdminToken (body_field, query::id),
|
||||
POST DeleteAdminToken (query::id),
|
||||
GET IntrospectAdminToken (admin_token),
|
||||
// Layout endpoints
|
||||
GET GetClusterLayout (),
|
||||
GET GetClusterLayoutHistory (),
|
||||
|
||||
@ -83,6 +83,21 @@ macro_rules! router_match {
|
||||
parse_json_body::< [<$api Request>], _, Error>($req).await?
|
||||
})
|
||||
}};
|
||||
(@@gen_parse_request $api:ident, (admin_token), $query: expr, $req:expr) => {{
|
||||
paste!({
|
||||
let auth_header = $req.headers()
|
||||
.get(hyper::header::AUTHORIZATION)
|
||||
.ok_or_else(|| Error::bad_request("Missing Authorization header"))?
|
||||
.to_str()
|
||||
.map_err(|_| Error::bad_request("Invalid Authorization header"))?;
|
||||
|
||||
let admin_token = auth_header.strip_prefix("Bearer ")
|
||||
.ok_or_else(|| Error::bad_request("Authorization header must be Bearer token"))?
|
||||
.to_string();
|
||||
|
||||
[< $api Request >] { admin_token }
|
||||
})
|
||||
}};
|
||||
(@@gen_parse_request $api:ident, (body_field, $($conv:ident $(($conv_arg:expr))? :: $param:ident),*), $query: expr, $req:expr)
|
||||
=>
|
||||
{{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user