mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
Merge remote-tracking branch 'origin/pr/399' into aether-rust-pioneer
# Conflicts: # crates/aether-data/src/lifecycle/bootstrap/postgres.rs # crates/aether-data/src/lifecycle/migrate/tests.rs
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE management_tokens
|
||||
ADD COLUMN permissions TEXT NULL AFTER allowed_ips;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE public.management_tokens
|
||||
ADD COLUMN IF NOT EXISTS permissions json;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE management_tokens
|
||||
ADD COLUMN permissions TEXT;
|
||||
|
||||
@@ -117,6 +117,7 @@ CREATE TABLE IF NOT EXISTS management_tokens (
|
||||
`token_hash` VARCHAR(255) NOT NULL,
|
||||
`token_prefix` VARCHAR(64),
|
||||
`allowed_ips` JSON,
|
||||
`permissions` JSON,
|
||||
`expires_at` BIGINT,
|
||||
`last_used_at` BIGINT,
|
||||
`last_used_ip` VARCHAR(255),
|
||||
|
||||
@@ -122,6 +122,7 @@ CREATE TABLE IF NOT EXISTS public.management_tokens (
|
||||
token_hash character varying(255) NOT NULL,
|
||||
token_prefix character varying(64),
|
||||
allowed_ips jsonb,
|
||||
permissions jsonb,
|
||||
expires_at bigint,
|
||||
last_used_at bigint,
|
||||
last_used_ip character varying(255),
|
||||
|
||||
@@ -112,6 +112,7 @@ CREATE TABLE IF NOT EXISTS management_tokens (
|
||||
token_hash TEXT NOT NULL,
|
||||
token_prefix TEXT,
|
||||
allowed_ips TEXT,
|
||||
permissions TEXT,
|
||||
expires_at INTEGER,
|
||||
last_used_at INTEGER,
|
||||
last_used_ip TEXT,
|
||||
|
||||
@@ -499,6 +499,11 @@ name = "allowed_ips"
|
||||
type = "json"
|
||||
nullable = true
|
||||
|
||||
[[table.management_tokens.columns]]
|
||||
name = "permissions"
|
||||
type = "json"
|
||||
nullable = true
|
||||
|
||||
[[table.management_tokens.columns]]
|
||||
name = "expires_at"
|
||||
type = "unix_seconds"
|
||||
|
||||
@@ -7,7 +7,7 @@ use tracing::info;
|
||||
// Generated by build.rs from schema/bootstrap/postgres.
|
||||
pub(crate) static EMPTY_DATABASE_SNAPSHOT_SQL: &str =
|
||||
include_str!(concat!(env!("OUT_DIR"), "/empty_database_snapshot.sql"));
|
||||
pub(crate) const EMPTY_DATABASE_SNAPSHOT_CUTOFF_VERSION: i64 = 20260507000000;
|
||||
pub(crate) const EMPTY_DATABASE_SNAPSHOT_CUTOFF_VERSION: i64 = 20260507120000;
|
||||
|
||||
const PUBLIC_BASE_TABLE_COUNT_SQL: &str = r#"
|
||||
SELECT COUNT(*)::BIGINT
|
||||
|
||||
@@ -292,6 +292,7 @@ fn empty_database_snapshot_covers_current_cutoff_versions() {
|
||||
20260505000000,
|
||||
20260505130000,
|
||||
20260507000000,
|
||||
20260507120000,
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -497,7 +498,7 @@ fn mysql_and_sqlite_migrations_do_not_use_postgres_jsonb() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mysql_and_sqlite_migrations_are_baseline_only_until_enabled() {
|
||||
fn mysql_and_sqlite_migrations_include_enabled_incrementals() {
|
||||
let mysql_versions = super::mysql::MIGRATOR
|
||||
.iter()
|
||||
.filter(|migration| migration.migration_type.is_up_migration())
|
||||
@@ -509,8 +510,8 @@ fn mysql_and_sqlite_migrations_are_baseline_only_until_enabled() {
|
||||
.map(|migration| migration.version)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(mysql_versions, vec![20260403000000]);
|
||||
assert_eq!(sqlite_versions, vec![20260403000000]);
|
||||
assert_eq!(mysql_versions, vec![20260403000000, 20260507120000]);
|
||||
assert_eq!(sqlite_versions, vec![20260403000000, 20260507120000]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1012,6 +1013,7 @@ fn pending_migrations_from_applied_skips_versions_already_applied() {
|
||||
20260505000000,
|
||||
20260505130000,
|
||||
20260507000000,
|
||||
20260507120000,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -152,6 +152,7 @@ impl ManagementTokenWriteRepository for InMemoryManagementTokenRepository {
|
||||
record.token_prefix.clone(),
|
||||
record.allowed_ips.clone(),
|
||||
)
|
||||
.with_permissions(record.permissions.clone())
|
||||
.with_runtime_fields(record.expires_at_unix_secs, None, None, 0, record.is_active)
|
||||
.with_timestamps(now, now);
|
||||
items.push(StoredManagementTokenWithUser::new(
|
||||
@@ -205,6 +206,10 @@ impl ManagementTokenWriteRepository for InMemoryManagementTokenRepository {
|
||||
items[index].token.allowed_ips = Some(allowed_ips.clone());
|
||||
}
|
||||
|
||||
if let Some(permissions) = &record.permissions {
|
||||
items[index].token.permissions = Some(permissions.clone());
|
||||
}
|
||||
|
||||
if record.clear_expires_at {
|
||||
items[index].token.expires_at_unix_secs = None;
|
||||
} else if let Some(expires_at_unix_secs) = record.expires_at_unix_secs {
|
||||
@@ -371,12 +376,17 @@ mod tests {
|
||||
name: "created".to_string(),
|
||||
description: Some("created token".to_string()),
|
||||
allowed_ips: Some(serde_json::json!(["127.0.0.1"])),
|
||||
permissions: Some(serde_json::json!(["admin:usage:read"])),
|
||||
expires_at_unix_secs: Some(1_800_000_000),
|
||||
is_active: true,
|
||||
})
|
||||
.await
|
||||
.expect("create should succeed");
|
||||
assert_eq!(created.name, "created");
|
||||
assert_eq!(
|
||||
created.permissions,
|
||||
Some(serde_json::json!(["admin:usage:read"]))
|
||||
);
|
||||
|
||||
let updated = repository
|
||||
.update_management_token(&UpdateManagementTokenRecord {
|
||||
@@ -386,6 +396,7 @@ mod tests {
|
||||
clear_description: true,
|
||||
allowed_ips: Some(serde_json::json!(["10.0.0.1"])),
|
||||
clear_allowed_ips: false,
|
||||
permissions: Some(serde_json::json!(["admin:usage:read", "admin:usage:write"])),
|
||||
expires_at_unix_secs: None,
|
||||
clear_expires_at: true,
|
||||
is_active: Some(false),
|
||||
@@ -396,6 +407,10 @@ mod tests {
|
||||
assert_eq!(updated.name, "renamed");
|
||||
assert_eq!(updated.description, None);
|
||||
assert_eq!(updated.allowed_ips, Some(serde_json::json!(["10.0.0.1"])));
|
||||
assert_eq!(
|
||||
updated.permissions,
|
||||
Some(serde_json::json!(["admin:usage:read", "admin:usage:write"]))
|
||||
);
|
||||
assert_eq!(updated.expires_at_unix_secs, None);
|
||||
assert!(!updated.is_active);
|
||||
|
||||
@@ -416,6 +431,10 @@ mod tests {
|
||||
.expect("lookup by hash should succeed")
|
||||
.expect("token should exist");
|
||||
assert_eq!(by_hash.token.id, "token-3");
|
||||
assert_eq!(
|
||||
by_hash.token.permissions,
|
||||
Some(serde_json::json!(["admin:usage:read", "admin:usage:write"]))
|
||||
);
|
||||
|
||||
let used = repository
|
||||
.record_management_token_usage("token-3", Some("127.0.0.1"))
|
||||
|
||||
@@ -42,6 +42,7 @@ SELECT
|
||||
description,
|
||||
token_prefix,
|
||||
allowed_ips,
|
||||
permissions,
|
||||
expires_at AS expires_at_unix_secs,
|
||||
last_used_at AS last_used_at_unix_secs,
|
||||
last_used_ip,
|
||||
@@ -62,6 +63,7 @@ SELECT
|
||||
mt.description,
|
||||
mt.token_prefix,
|
||||
mt.allowed_ips,
|
||||
mt.permissions,
|
||||
mt.expires_at AS expires_at_unix_secs,
|
||||
mt.last_used_at AS last_used_at_unix_secs,
|
||||
mt.last_used_ip,
|
||||
@@ -96,6 +98,7 @@ SELECT
|
||||
mt.description,
|
||||
mt.token_prefix,
|
||||
mt.allowed_ips,
|
||||
mt.permissions,
|
||||
mt.expires_at AS expires_at_unix_secs,
|
||||
mt.last_used_at AS last_used_at_unix_secs,
|
||||
mt.last_used_ip,
|
||||
@@ -121,6 +124,7 @@ SELECT
|
||||
mt.description,
|
||||
mt.token_prefix,
|
||||
mt.allowed_ips,
|
||||
mt.permissions,
|
||||
mt.expires_at AS expires_at_unix_secs,
|
||||
mt.last_used_at AS last_used_at_unix_secs,
|
||||
mt.last_used_ip,
|
||||
@@ -211,8 +215,8 @@ impl ManagementTokenWriteRepository for MysqlManagementTokenRepository {
|
||||
r#"
|
||||
INSERT INTO management_tokens (
|
||||
id, user_id, token_hash, token_prefix, name, description, allowed_ips,
|
||||
expires_at, is_active, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
permissions, expires_at, is_active, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"#,
|
||||
)
|
||||
.bind(&record.id)
|
||||
@@ -222,6 +226,7 @@ INSERT INTO management_tokens (
|
||||
.bind(&record.name)
|
||||
.bind(record.description.as_deref())
|
||||
.bind(json_to_string(record.allowed_ips.as_ref())?)
|
||||
.bind(json_to_string(record.permissions.as_ref())?)
|
||||
.bind(
|
||||
record
|
||||
.expires_at_unix_secs
|
||||
@@ -262,6 +267,7 @@ INSERT INTO management_tokens (
|
||||
} else {
|
||||
record.allowed_ips.as_ref().or(current.allowed_ips.as_ref())
|
||||
};
|
||||
let permissions = record.permissions.as_ref().or(current.permissions.as_ref());
|
||||
let expires_at = if record.clear_expires_at {
|
||||
None
|
||||
} else {
|
||||
@@ -276,6 +282,7 @@ UPDATE management_tokens
|
||||
SET name = ?,
|
||||
description = ?,
|
||||
allowed_ips = ?,
|
||||
permissions = ?,
|
||||
expires_at = ?,
|
||||
is_active = ?,
|
||||
updated_at = ?
|
||||
@@ -285,6 +292,7 @@ WHERE id = ?
|
||||
.bind(name)
|
||||
.bind(description)
|
||||
.bind(json_to_string(allowed_ips)?)
|
||||
.bind(json_to_string(permissions)?)
|
||||
.bind(expires_at.and_then(|value| i64::try_from(value).ok()))
|
||||
.bind(is_active)
|
||||
.bind(now as i64)
|
||||
@@ -438,6 +446,7 @@ fn map_token_row(row: &MySqlRow) -> Result<StoredManagementToken, DataLayerError
|
||||
row.try_get("token_prefix").map_sql_err()?,
|
||||
json_from_string(row.try_get("allowed_ips").map_sql_err()?)?,
|
||||
)
|
||||
.with_permissions(json_from_string(row.try_get("permissions").map_sql_err()?)?)
|
||||
.with_runtime_fields(
|
||||
optional_unix_secs(row.try_get("expires_at_unix_secs").map_sql_err()?),
|
||||
optional_unix_secs(row.try_get("last_used_at_unix_secs").map_sql_err()?),
|
||||
|
||||
@@ -18,6 +18,7 @@ SELECT
|
||||
mt.description,
|
||||
mt.token_prefix,
|
||||
mt.allowed_ips,
|
||||
mt.permissions,
|
||||
EXTRACT(EPOCH FROM mt.expires_at)::bigint AS expires_at_unix_secs,
|
||||
EXTRACT(EPOCH FROM mt.last_used_at)::bigint AS last_used_at_unix_secs,
|
||||
mt.last_used_ip,
|
||||
@@ -53,6 +54,7 @@ SELECT
|
||||
mt.description,
|
||||
mt.token_prefix,
|
||||
mt.allowed_ips,
|
||||
mt.permissions,
|
||||
EXTRACT(EPOCH FROM mt.expires_at)::bigint AS expires_at_unix_secs,
|
||||
EXTRACT(EPOCH FROM mt.last_used_at)::bigint AS last_used_at_unix_secs,
|
||||
mt.last_used_ip,
|
||||
@@ -78,6 +80,7 @@ SELECT
|
||||
mt.description,
|
||||
mt.token_prefix,
|
||||
mt.allowed_ips,
|
||||
mt.permissions,
|
||||
EXTRACT(EPOCH FROM mt.expires_at)::bigint AS expires_at_unix_secs,
|
||||
EXTRACT(EPOCH FROM mt.last_used_at)::bigint AS last_used_at_unix_secs,
|
||||
mt.last_used_ip,
|
||||
@@ -109,6 +112,7 @@ INSERT INTO management_tokens (
|
||||
name,
|
||||
description,
|
||||
allowed_ips,
|
||||
permissions,
|
||||
expires_at,
|
||||
is_active
|
||||
)
|
||||
@@ -120,11 +124,12 @@ VALUES (
|
||||
$5,
|
||||
$6,
|
||||
$7,
|
||||
$8,
|
||||
CASE
|
||||
WHEN $8::bigint IS NULL THEN NULL
|
||||
ELSE to_timestamp($8::double precision)
|
||||
WHEN $9::bigint IS NULL THEN NULL
|
||||
ELSE to_timestamp($9::double precision)
|
||||
END,
|
||||
$9
|
||||
$10
|
||||
)
|
||||
RETURNING
|
||||
id,
|
||||
@@ -133,6 +138,7 @@ RETURNING
|
||||
description,
|
||||
token_prefix,
|
||||
allowed_ips,
|
||||
permissions,
|
||||
EXTRACT(EPOCH FROM expires_at)::bigint AS expires_at_unix_secs,
|
||||
EXTRACT(EPOCH FROM last_used_at)::bigint AS last_used_at_unix_secs,
|
||||
last_used_ip,
|
||||
@@ -155,12 +161,13 @@ SET name = COALESCE($2, name),
|
||||
WHEN $6::json IS NULL THEN allowed_ips
|
||||
ELSE $6
|
||||
END,
|
||||
permissions = COALESCE($7::json, permissions),
|
||||
expires_at = CASE
|
||||
WHEN $7 THEN NULL
|
||||
WHEN $8::bigint IS NULL THEN expires_at
|
||||
ELSE to_timestamp($8::double precision)
|
||||
WHEN $8 THEN NULL
|
||||
WHEN $9::bigint IS NULL THEN expires_at
|
||||
ELSE to_timestamp($9::double precision)
|
||||
END,
|
||||
is_active = COALESCE($9, is_active),
|
||||
is_active = COALESCE($10, is_active),
|
||||
updated_at = NOW()
|
||||
WHERE id = $1
|
||||
RETURNING
|
||||
@@ -170,6 +177,7 @@ RETURNING
|
||||
description,
|
||||
token_prefix,
|
||||
allowed_ips,
|
||||
permissions,
|
||||
EXTRACT(EPOCH FROM expires_at)::bigint AS expires_at_unix_secs,
|
||||
EXTRACT(EPOCH FROM last_used_at)::bigint AS last_used_at_unix_secs,
|
||||
last_used_ip,
|
||||
@@ -191,6 +199,7 @@ RETURNING
|
||||
description,
|
||||
token_prefix,
|
||||
allowed_ips,
|
||||
permissions,
|
||||
EXTRACT(EPOCH FROM expires_at)::bigint AS expires_at_unix_secs,
|
||||
EXTRACT(EPOCH FROM last_used_at)::bigint AS last_used_at_unix_secs,
|
||||
last_used_ip,
|
||||
@@ -213,6 +222,7 @@ RETURNING
|
||||
description,
|
||||
token_prefix,
|
||||
allowed_ips,
|
||||
permissions,
|
||||
EXTRACT(EPOCH FROM expires_at)::bigint AS expires_at_unix_secs,
|
||||
EXTRACT(EPOCH FROM last_used_at)::bigint AS last_used_at_unix_secs,
|
||||
last_used_ip,
|
||||
@@ -236,6 +246,7 @@ RETURNING
|
||||
description,
|
||||
token_prefix,
|
||||
allowed_ips,
|
||||
permissions,
|
||||
EXTRACT(EPOCH FROM expires_at)::bigint AS expires_at_unix_secs,
|
||||
EXTRACT(EPOCH FROM last_used_at)::bigint AS last_used_at_unix_secs,
|
||||
last_used_ip,
|
||||
@@ -327,6 +338,7 @@ impl ManagementTokenWriteRepository for SqlxManagementTokenRepository {
|
||||
.bind(&record.name)
|
||||
.bind(record.description.as_deref())
|
||||
.bind(record.allowed_ips.as_ref())
|
||||
.bind(record.permissions.as_ref())
|
||||
.bind(
|
||||
record
|
||||
.expires_at_unix_secs
|
||||
@@ -351,6 +363,7 @@ impl ManagementTokenWriteRepository for SqlxManagementTokenRepository {
|
||||
.bind(record.description.as_deref())
|
||||
.bind(record.clear_allowed_ips)
|
||||
.bind(record.allowed_ips.as_ref())
|
||||
.bind(record.permissions.as_ref())
|
||||
.bind(record.clear_expires_at)
|
||||
.bind(
|
||||
record
|
||||
@@ -458,6 +471,7 @@ fn map_token_row(row: &PgRow) -> Result<StoredManagementToken, DataLayerError> {
|
||||
row.try_get("token_prefix").map_postgres_err()?,
|
||||
row.try_get("allowed_ips").map_postgres_err()?,
|
||||
)
|
||||
.with_permissions(row.try_get("permissions").map_postgres_err()?)
|
||||
.with_runtime_fields(
|
||||
optional_unix_secs(row.try_get("expires_at_unix_secs").map_postgres_err()?),
|
||||
optional_unix_secs(row.try_get("last_used_at_unix_secs").map_postgres_err()?),
|
||||
|
||||
@@ -41,6 +41,7 @@ SELECT
|
||||
description,
|
||||
token_prefix,
|
||||
allowed_ips,
|
||||
permissions,
|
||||
expires_at AS expires_at_unix_secs,
|
||||
last_used_at AS last_used_at_unix_secs,
|
||||
last_used_ip,
|
||||
@@ -61,6 +62,7 @@ SELECT
|
||||
mt.description,
|
||||
mt.token_prefix,
|
||||
mt.allowed_ips,
|
||||
mt.permissions,
|
||||
mt.expires_at AS expires_at_unix_secs,
|
||||
mt.last_used_at AS last_used_at_unix_secs,
|
||||
mt.last_used_ip,
|
||||
@@ -95,6 +97,7 @@ SELECT
|
||||
mt.description,
|
||||
mt.token_prefix,
|
||||
mt.allowed_ips,
|
||||
mt.permissions,
|
||||
mt.expires_at AS expires_at_unix_secs,
|
||||
mt.last_used_at AS last_used_at_unix_secs,
|
||||
mt.last_used_ip,
|
||||
@@ -120,6 +123,7 @@ SELECT
|
||||
mt.description,
|
||||
mt.token_prefix,
|
||||
mt.allowed_ips,
|
||||
mt.permissions,
|
||||
mt.expires_at AS expires_at_unix_secs,
|
||||
mt.last_used_at AS last_used_at_unix_secs,
|
||||
mt.last_used_ip,
|
||||
@@ -210,8 +214,8 @@ impl ManagementTokenWriteRepository for SqliteManagementTokenRepository {
|
||||
r#"
|
||||
INSERT INTO management_tokens (
|
||||
id, user_id, token_hash, token_prefix, name, description, allowed_ips,
|
||||
expires_at, is_active, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
permissions, expires_at, is_active, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"#,
|
||||
)
|
||||
.bind(&record.id)
|
||||
@@ -221,6 +225,7 @@ INSERT INTO management_tokens (
|
||||
.bind(&record.name)
|
||||
.bind(record.description.as_deref())
|
||||
.bind(json_to_string(record.allowed_ips.as_ref())?)
|
||||
.bind(json_to_string(record.permissions.as_ref())?)
|
||||
.bind(
|
||||
record
|
||||
.expires_at_unix_secs
|
||||
@@ -261,6 +266,7 @@ INSERT INTO management_tokens (
|
||||
} else {
|
||||
record.allowed_ips.as_ref().or(current.allowed_ips.as_ref())
|
||||
};
|
||||
let permissions = record.permissions.as_ref().or(current.permissions.as_ref());
|
||||
let expires_at = if record.clear_expires_at {
|
||||
None
|
||||
} else {
|
||||
@@ -275,6 +281,7 @@ UPDATE management_tokens
|
||||
SET name = ?,
|
||||
description = ?,
|
||||
allowed_ips = ?,
|
||||
permissions = ?,
|
||||
expires_at = ?,
|
||||
is_active = ?,
|
||||
updated_at = ?
|
||||
@@ -284,6 +291,7 @@ WHERE id = ?
|
||||
.bind(name)
|
||||
.bind(description)
|
||||
.bind(json_to_string(allowed_ips)?)
|
||||
.bind(json_to_string(permissions)?)
|
||||
.bind(expires_at.and_then(|value| i64::try_from(value).ok()))
|
||||
.bind(is_active)
|
||||
.bind(now as i64)
|
||||
@@ -435,6 +443,7 @@ fn map_token_row(row: &SqliteRow) -> Result<StoredManagementToken, DataLayerErro
|
||||
row.try_get("token_prefix").map_sql_err()?,
|
||||
json_from_string(row.try_get("allowed_ips").map_sql_err()?)?,
|
||||
)
|
||||
.with_permissions(json_from_string(row.try_get("permissions").map_sql_err()?)?)
|
||||
.with_runtime_fields(
|
||||
optional_unix_secs(row.try_get("expires_at_unix_secs").map_sql_err()?),
|
||||
optional_unix_secs(row.try_get("last_used_at_unix_secs").map_sql_err()?),
|
||||
@@ -516,12 +525,17 @@ VALUES ('user-1', 'user-1@example.com', 'user-1', 'admin', 1, 1, 1)
|
||||
name: "primary".to_string(),
|
||||
description: Some("primary token".to_string()),
|
||||
allowed_ips: Some(serde_json::json!(["127.0.0.1"])),
|
||||
permissions: Some(serde_json::json!(["admin:usage:read"])),
|
||||
expires_at_unix_secs: Some(1_800_000_000),
|
||||
is_active: true,
|
||||
})
|
||||
.await
|
||||
.expect("token should create");
|
||||
assert_eq!(created.name, "primary");
|
||||
assert_eq!(
|
||||
created.permissions,
|
||||
Some(serde_json::json!(["admin:usage:read"]))
|
||||
);
|
||||
|
||||
let page = repository
|
||||
.list_management_tokens(&ManagementTokenListQuery {
|
||||
@@ -550,6 +564,7 @@ VALUES ('user-1', 'user-1@example.com', 'user-1', 'admin', 1, 1, 1)
|
||||
clear_description: true,
|
||||
allowed_ips: Some(serde_json::json!(["10.0.0.1"])),
|
||||
clear_allowed_ips: false,
|
||||
permissions: Some(serde_json::json!(["admin:usage:read", "admin:usage:write"])),
|
||||
expires_at_unix_secs: None,
|
||||
clear_expires_at: true,
|
||||
is_active: Some(false),
|
||||
@@ -560,6 +575,10 @@ VALUES ('user-1', 'user-1@example.com', 'user-1', 'admin', 1, 1, 1)
|
||||
assert_eq!(updated.name, "renamed");
|
||||
assert!(!updated.is_active);
|
||||
assert_eq!(updated.description, None);
|
||||
assert_eq!(
|
||||
updated.permissions,
|
||||
Some(serde_json::json!(["admin:usage:read", "admin:usage:write"]))
|
||||
);
|
||||
assert_eq!(updated.expires_at_unix_secs, None);
|
||||
|
||||
let toggled = repository
|
||||
|
||||
@@ -47,6 +47,7 @@ pub struct StoredManagementToken {
|
||||
pub description: Option<String>,
|
||||
pub token_prefix: Option<String>,
|
||||
pub allowed_ips: Option<serde_json::Value>,
|
||||
pub permissions: Option<serde_json::Value>,
|
||||
pub expires_at_unix_secs: Option<u64>,
|
||||
pub last_used_at_unix_secs: Option<u64>,
|
||||
pub last_used_ip: Option<String>,
|
||||
@@ -80,6 +81,7 @@ impl StoredManagementToken {
|
||||
description: None,
|
||||
token_prefix: None,
|
||||
allowed_ips: None,
|
||||
permissions: None,
|
||||
expires_at_unix_secs: None,
|
||||
last_used_at_unix_secs: None,
|
||||
last_used_ip: None,
|
||||
@@ -102,6 +104,11 @@ impl StoredManagementToken {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_permissions(mut self, permissions: Option<serde_json::Value>) -> Self {
|
||||
self.permissions = permissions;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_runtime_fields(
|
||||
mut self,
|
||||
expires_at_unix_secs: Option<u64>,
|
||||
@@ -166,6 +173,7 @@ pub struct CreateManagementTokenRecord {
|
||||
pub name: String,
|
||||
pub description: Option<String>,
|
||||
pub allowed_ips: Option<serde_json::Value>,
|
||||
pub permissions: Option<serde_json::Value>,
|
||||
pub expires_at_unix_secs: Option<u64>,
|
||||
pub is_active: bool,
|
||||
}
|
||||
@@ -214,6 +222,7 @@ impl CreateManagementTokenRecord {
|
||||
));
|
||||
}
|
||||
}
|
||||
validate_management_token_permissions(self.permissions.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -226,6 +235,7 @@ pub struct UpdateManagementTokenRecord {
|
||||
pub clear_description: bool,
|
||||
pub allowed_ips: Option<serde_json::Value>,
|
||||
pub clear_allowed_ips: bool,
|
||||
pub permissions: Option<serde_json::Value>,
|
||||
pub expires_at_unix_secs: Option<u64>,
|
||||
pub clear_expires_at: bool,
|
||||
pub is_active: Option<bool>,
|
||||
@@ -262,10 +272,35 @@ impl UpdateManagementTokenRecord {
|
||||
));
|
||||
}
|
||||
}
|
||||
validate_management_token_permissions(self.permissions.as_ref())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_management_token_permissions(
|
||||
permissions: Option<&serde_json::Value>,
|
||||
) -> Result<(), crate::DataLayerError> {
|
||||
let Some(permissions) = permissions else {
|
||||
return Ok(());
|
||||
};
|
||||
let Some(items) = permissions.as_array() else {
|
||||
return Err(crate::DataLayerError::InvalidInput(
|
||||
"permissions must be an array".to_string(),
|
||||
));
|
||||
};
|
||||
if items.is_empty() {
|
||||
return Err(crate::DataLayerError::InvalidInput(
|
||||
"permissions must not be empty".to_string(),
|
||||
));
|
||||
}
|
||||
if items.iter().any(|value| value.as_str().is_none()) {
|
||||
return Err(crate::DataLayerError::InvalidInput(
|
||||
"permissions must contain only strings".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RegenerateManagementTokenSecret {
|
||||
pub token_id: String,
|
||||
|
||||
Reference in New Issue
Block a user