diff --git a/doc/api/garage-admin-v2.json b/doc/api/garage-admin-v2.json index 97de3a71..f3310256 100644 --- a/doc/api/garage-admin-v2.json +++ b/doc/api/garage-admin-v2.json @@ -225,6 +225,40 @@ } } }, + "/v2/CreateAdminToken": { + "post": { + "tags": [ + "Admin API token" + ], + "description": "Creates a new admin API token", + "operationId": "CreateAdminToken", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAdminTokenRequestBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Admin token has been created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateAdminTokenResponse" + } + } + } + }, + "500": { + "description": "Internal server error" + } + } + } + }, "/v2/CreateBucket": { "post": { "tags": [ @@ -325,6 +359,31 @@ } } }, + "/v2/DeleteAdminToken": { + "post": { + "tags": [ + "Admin API token" + ], + "description": "Delete an admin API token from the cluster, revoking all its permissions.", + "operationId": "DeleteAdminToken", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Admin API token ID", + "required": true + } + ], + "responses": { + "200": { + "description": "Admin token has been deleted" + }, + "500": { + "description": "Internal server error" + } + } + } + }, "/v2/DeleteBucket": { "post": { "tags": [ @@ -415,6 +474,44 @@ } } }, + "/v2/GetAdminTokenInfo": { + "get": { + "tags": [ + "Admin API token" + ], + "description": "\nReturn information about a specific admin API token.\nYou can search by specifying the exact token identifier (`id`) or by specifying a pattern (`search`).\n ", + "operationId": "GetAdminTokenInfo", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Admin API token ID", + "required": true + }, + { + "name": "search", + "in": "path", + "description": "Partial token ID or name to search for", + "required": true + } + ], + "responses": { + "200": { + "description": "Information about the admin token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetAdminTokenInfoResponse" + } + } + } + }, + "500": { + "description": "Internal server error" + } + } + } + }, "/v2/GetBlockInfo": { "post": { "tags": [ @@ -886,6 +983,30 @@ } } }, + "/v2/ListAdminTokens": { + "get": { + "tags": [ + "Admin API token" + ], + "description": "Returns all admin API tokens in the cluster.", + "operationId": "ListAdminTokens", + "responses": { + "200": { + "description": "Returns info about all admin API tokens", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListAdminTokensResponse" + } + } + } + }, + "500": { + "description": "Internal server error" + } + } + } + }, "/v2/ListBlockErrors": { "get": { "tags": [ @@ -1216,6 +1337,48 @@ } } }, + "/v2/UpdateAdminToken": { + "post": { + "tags": [ + "Admin API token" + ], + "description": "\nUpdates information about the specified admin API token.\n ", + "operationId": "UpdateAdminToken", + "parameters": [ + { + "name": "id", + "in": "path", + "description": "Admin API token ID", + "required": true + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAdminTokenRequestBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Admin token has been updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateAdminTokenResponse" + } + } + } + }, + "500": { + "description": "Internal server error" + } + } + } + }, "/v2/UpdateBucket": { "post": { "tags": [ @@ -1775,6 +1938,25 @@ } } }, + "CreateAdminTokenResponse": { + "allOf": [ + { + "$ref": "#/components/schemas/GetAdminTokenInfoResponse" + }, + { + "type": "object", + "required": [ + "secretToken" + ], + "properties": { + "secretToken": { + "type": "string", + "description": "The secret bearer token. **CAUTION:** This token will be shown only\nONCE, so this value MUST be remembered somewhere, or the token\nwill be unusable." + } + } + } + ] + }, "CreateBucketLocalAlias": { "type": "object", "required": [ @@ -1858,6 +2040,43 @@ } } }, + "GetAdminTokenInfoResponse": { + "type": "object", + "required": [ + "id", + "name", + "expired", + "scope" + ], + "properties": { + "expiration": { + "type": [ + "string", + "null" + ], + "description": "Expiration time and date, formatted according to RFC 3339" + }, + "expired": { + "type": "boolean", + "description": "Whether this admin token is expired already" + }, + "id": { + "type": "string", + "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" + } + } + }, "GetBucketInfoKey": { "type": "object", "required": [ @@ -2325,6 +2544,12 @@ } } }, + "ListAdminTokensResponse": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GetAdminTokenInfoResponse" + } + }, "ListBucketsResponse": { "type": "array", "items": { @@ -3404,6 +3629,38 @@ "cancel" ] }, + "UpdateAdminTokenRequestBody": { + "type": "object", + "properties": { + "expiration": { + "type": [ + "string", + "null" + ], + "description": "Expiration time and date, formatted according to RFC 3339" + }, + "name": { + "type": [ + "string", + "null" + ], + "description": "Name of the admin API token" + }, + "scope": { + "type": [ + "array", + "null" + ], + "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. **WARNING:** Granting a scope of `CreateAdminToken` or\n`UpdateAdminToken` trivially allows for privilege escalation, and is thus\nfunctionnally equivalent to granting a scope of `*`." + } + } + }, + "UpdateAdminTokenResponse": { + "$ref": "#/components/schemas/GetAdminTokenInfoResponse" + }, "UpdateBucketRequestBody": { "type": "object", "properties": { diff --git a/src/api/admin/api.rs b/src/api/admin/api.rs index 13b2c3b1..f002efad 100644 --- a/src/api/admin/api.rs +++ b/src/api/admin/api.rs @@ -298,15 +298,9 @@ pub struct ConnectNodeResponse { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ListAdminTokensRequest; -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct ListAdminTokensResponse(pub Vec); -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ListAdminTokensResponseItem { - pub id: String, - pub name: String, -} - // ---- GetAdminTokenInfo ---- #[derive(Debug, Clone, Serialize, Deserialize)] @@ -315,13 +309,21 @@ pub struct GetAdminTokenInfoRequest { pub search: Option, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct GetAdminTokenInfoResponse { + /// Identifier of the admin token (which is also a prefix of the full bearer token) pub id: String, + /// Name of the admin API token pub name: String, + /// Expiration time and date, formatted according to RFC 3339 + #[schema(value_type = Option)] pub expiration: Option>, + /// 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, } @@ -330,9 +332,12 @@ pub struct GetAdminTokenInfoResponse { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CreateAdminTokenRequest(pub UpdateAdminTokenRequestBody); -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct CreateAdminTokenResponse { + /// The secret bearer token. **CAUTION:** This token will be shown only + /// ONCE, so this value MUST be remembered somewhere, or the token + /// will be unusable. pub secret_token: String, #[serde(flatten)] pub info: GetAdminTokenInfoResponse, @@ -346,15 +351,23 @@ pub struct UpdateAdminTokenRequest { pub body: UpdateAdminTokenRequestBody, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct UpdateAdminTokenRequestBody { + /// Name of the admin API token pub name: Option, + /// Expiration time and date, formatted according to RFC 3339 + #[schema(value_type = Option)] pub expiration: Option>, + /// 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. **WARNING:** Granting a scope of `CreateAdminToken` or + /// `UpdateAdminToken` trivially allows for privilege escalation, and is thus + /// functionnally equivalent to granting a scope of `*`. pub scope: Option>, } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct UpdateAdminTokenResponse(pub GetAdminTokenInfoResponse); // ---- DeleteAdminToken ---- diff --git a/src/api/admin/openapi.rs b/src/api/admin/openapi.rs index 01a694e5..24319817 100644 --- a/src/api/admin/openapi.rs +++ b/src/api/admin/openapi.rs @@ -66,6 +66,82 @@ fn GetClusterStatistics() -> () {} )] fn ConnectClusterNodes() -> () {} +// ********************************************** +// Admin API token operations +// ********************************************** + +#[utoipa::path(get, + path = "/v2/ListAdminTokens", + tag = "Admin API token", + description = "Returns all admin API tokens in the cluster.", + responses( + (status = 200, description = "Returns info about all admin API tokens", body = ListAdminTokensResponse), + (status = 500, description = "Internal server error") + ), +)] +fn ListAdminTokens() -> () {} + +#[utoipa::path(get, + path = "/v2/GetAdminTokenInfo", + tag = "Admin API token", + description = " +Return information about a specific admin API token. +You can search by specifying the exact token identifier (`id`) or by specifying a pattern (`search`). + ", + params( + ("id", description = "Admin API token ID"), + ("search", description = "Partial token ID or name to search for"), + ), + responses( + (status = 200, description = "Information about the admin token", body = GetAdminTokenInfoResponse), + (status = 500, description = "Internal server error") + ), +)] +fn GetAdminTokenInfo() -> () {} + +#[utoipa::path(post, + path = "/v2/CreateAdminToken", + tag = "Admin API token", + description = "Creates a new admin API token", + request_body = UpdateAdminTokenRequestBody, + responses( + (status = 200, description = "Admin token has been created", body = CreateAdminTokenResponse), + (status = 500, description = "Internal server error") + ), +)] +fn CreateAdminToken() -> () {} + +#[utoipa::path(post, + path = "/v2/UpdateAdminToken", + tag = "Admin API token", + description = " +Updates information about the specified admin API token. + ", + request_body = UpdateAdminTokenRequestBody, + params( + ("id", description = "Admin API token ID"), + ), + responses( + (status = 200, description = "Admin token has been updated", body = UpdateAdminTokenResponse), + (status = 500, description = "Internal server error") + ), +)] +fn UpdateAdminToken() -> () {} + +#[utoipa::path(post, + path = "/v2/DeleteAdminToken", + tag = "Admin API token", + description = "Delete an admin API token from the cluster, revoking all its permissions.", + params( + ("id", description = "Admin API token ID"), + ), + responses( + (status = 200, description = "Admin token has been deleted"), + (status = 500, description = "Internal server error") + ), +)] +fn DeleteAdminToken() -> () {} + // ********************************************** // Layout operations // ********************************************** @@ -723,6 +799,12 @@ impl Modify for SecurityAddon { GetClusterStatus, GetClusterStatistics, ConnectClusterNodes, + // Admin token operations + ListAdminTokens, + GetAdminTokenInfo, + CreateAdminToken, + UpdateAdminToken, + DeleteAdminToken, // Layout operations GetClusterLayout, GetClusterLayoutHistory,