add creation date to admin api tokens

This commit is contained in:
Alex Auvolat 2025-03-12 09:52:39 +01:00
parent 325f79012c
commit 88b4623bf1
5 changed files with 37 additions and 8 deletions

View file

@ -2048,6 +2048,14 @@
"scope"
],
"properties": {
"created": {
"type": [
"string",
"null"
],
"format": "date-time",
"description": "Creation date"
},
"expiration": {
"type": [
"string",

View file

@ -41,6 +41,7 @@ impl RequestHandler for ListAdminTokensRequest {
0,
GetAdminTokenInfoResponse {
id: None,
created: None,
name: "admin_token (from daemon configuration)".into(),
expiration: None,
expired: false,
@ -54,6 +55,7 @@ impl RequestHandler for ListAdminTokensRequest {
1,
GetAdminTokenInfoResponse {
id: None,
created: None,
name: "metrics_token (from daemon configuration)".into(),
expiration: None,
expired: false,
@ -180,6 +182,10 @@ fn admin_token_info_results(token: &AdminApiToken, now: u64) -> GetAdminTokenInf
GetAdminTokenInfoResponse {
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")

View file

@ -314,6 +314,8 @@ pub struct GetAdminTokenInfoRequest {
pub struct GetAdminTokenInfoResponse {
/// Identifier of the admin token (which is also a prefix of the full bearer token)
pub id: Option<String>,
/// Creation date
pub created: Option<chrono::DateTime<chrono::Utc>>,
/// Name of the admin API token
pub name: String,
/// Expiration time and date, formatted according to RFC 3339

View file

@ -1,6 +1,6 @@
use format_table::format_table;
use chrono::Utc;
use chrono::{Local, Utc};
use garage_util::error::*;
@ -30,11 +30,15 @@ impl Cli {
}
pub async fn cmd_list_admin_tokens(&self) -> Result<(), Error> {
let list = self.api_request(ListAdminTokensRequest).await?;
let mut list = self.api_request(ListAdminTokensRequest).await?;
let mut table = vec!["ID\tNAME\tEXPIRATION\tSCOPE".to_string()];
list.0.sort_by_key(|x| x.created);
let mut table = vec!["ID\tCREATED\tNAME\tEXPIRATION\tSCOPE".to_string()];
for tok in list.0.iter() {
let scope = if tok.scope.len() > 1 {
let scope = if tok.expired {
String::new()
} else if tok.scope.len() > 1 {
format!("[{}]", tok.scope.len())
} else {
tok.scope.get(0).cloned().unwrap_or_default()
@ -43,12 +47,15 @@ impl Cli {
"expired".to_string()
} else {
tok.expiration
.map(|x| x.to_string())
.map(|x| x.with_timezone(&Local).to_string())
.unwrap_or("never".into())
};
table.push(format!(
"{}\t{}\t{}\t{}\t",
"{}\t{}\t{}\t{}\t{}",
tok.id.as_deref().unwrap_or("-"),
tok.created
.map(|x| x.with_timezone(&Local).date_naive().to_string())
.unwrap_or("-".into()),
tok.name,
exp,
scope,
@ -209,8 +216,9 @@ impl Cli {
fn print_token_info(token: &GetAdminTokenInfoResponse) {
format_table(vec![
format!("ID:\t{}", token.id.as_deref().unwrap_or("-")),
format!("ID:\t{}", token.id.as_ref().unwrap()),
format!("Name:\t{}", token.name),
format!("Created:\t{}", token.created.unwrap().with_timezone(&Local)),
format!(
"Validity:\t{}",
token.expired.then_some("EXPIRED").unwrap_or("valid")
@ -219,7 +227,7 @@ fn print_token_info(token: &GetAdminTokenInfoResponse) {
"Expiration:\t{}",
token
.expiration
.map(|x| x.to_string())
.map(|x| x.with_timezone(&Local).to_string())
.unwrap_or("never".into())
),
format!("Scope:\t{}", token.scope.to_vec().join(", ")),

View file

@ -1,6 +1,7 @@
use base64::prelude::*;
use garage_util::crdt::{self, Crdt};
use garage_util::time::now_msec;
use garage_table::{EmptyKey, Entry, TableSchema};
@ -24,6 +25,9 @@ mod v2 {
#[derive(PartialEq, Eq, Clone, Debug, Serialize, Deserialize)]
pub struct AdminApiTokenParams {
/// Creation date
pub created: u64,
/// The entire API token hashed as a password
pub token_hash: String,
@ -91,6 +95,7 @@ impl AdminApiToken {
let ret = AdminApiToken {
prefix,
state: crdt::Deletable::present(AdminApiTokenParams {
created: now_msec(),
token_hash: hashed_token,
name: crdt::Lww::new(name.to_string()),
expiration: crdt::Lww::new(None),