Files
iam-service/src/handlers/role.rs
2026-01-30 16:31:53 +08:00

133 lines
3.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::handlers::AppState;
use crate::middleware::TenantId;
use crate::middleware::auth::AuthContext;
use crate::models::{CreateRoleRequest, RoleResponse};
use axum::{Json, extract::State};
use common_telemetry::{AppError, AppResponse};
use tracing::instrument;
#[utoipa::path(
post,
path = "/roles",
tag = "Role",
security(
("bearer_auth" = [])
),
request_body = CreateRoleRequest,
responses(
(status = 201, description = "角色创建成功", body = RoleResponse),
(status = 400, description = "请求参数错误"),
(status = 401, description = "未认证"),
(status = 403, description = "无权限")
),
params(
("Authorization" = String, Header, description = "Bearer <access_token>(访问令牌)"),
("X-Tenant-ID" = String, Header, description = "租户 UUID可选若提供需与 Token 中 tenant_id 一致)")
)
)]
#[instrument(skip(state, payload))]
/// 在当前租户下创建角色。
///
/// 业务规则:
/// - 角色归属到当前租户(由 `TenantId` 决定),禁止跨租户写入。
/// - 需要具备 `role:write` 权限,否则返回 403。
///
/// 输入:
/// - Header `Authorization: Bearer <access_token>`(必填)
/// - Body `CreateRoleRequest`(必填)
///
/// 输出:
/// - `201`:返回新建角色信息(含 `id`
///
/// 异常:
/// - `401`:未认证
/// - `403`:租户不匹配或无权限
/// - `400`:请求参数错误
pub async fn create_role_handler(
TenantId(tenant_id): TenantId,
State(state): State<AppState>,
AuthContext {
tenant_id: auth_tenant_id,
user_id,
..
}: AuthContext,
Json(payload): Json<CreateRoleRequest>,
) -> Result<AppResponse<RoleResponse>, AppError> {
if auth_tenant_id != tenant_id {
return Err(AppError::PermissionDenied("tenant:mismatch".into()));
}
state
.authorization_service
.require_permission(tenant_id, user_id, "role:write")
.await?;
let role = state.role_service.create_role(tenant_id, payload).await?;
Ok(AppResponse::created(RoleResponse {
id: role.id,
name: role.name,
description: role.description,
}))
}
#[utoipa::path(
get,
path = "/roles",
tag = "Role",
security(
("bearer_auth" = [])
),
responses(
(status = 200, description = "角色列表", body = [RoleResponse]),
(status = 401, description = "未认证"),
(status = 403, description = "无权限")
),
params(
("Authorization" = String, Header, description = "Bearer <access_token>(访问令牌)"),
("X-Tenant-ID" = String, Header, description = "租户 UUID可选若提供需与 Token 中 tenant_id 一致)")
)
)]
#[instrument(skip(state))]
/// 查询当前租户下的角色列表。
///
/// 业务规则:
/// - 仅返回当前租户角色;若 `X-Tenant-ID` 与 Token 不一致则返回 403。
/// - 需要具备 `role:read` 权限。
///
/// 输入:
/// - Header `Authorization: Bearer <access_token>`(必填)
///
/// 输出:
/// - `200`:角色列表
///
/// 异常:
/// - `401`:未认证
/// - `403`:租户不匹配或无权限
pub async fn list_roles_handler(
TenantId(tenant_id): TenantId,
State(state): State<AppState>,
AuthContext {
tenant_id: auth_tenant_id,
user_id,
..
}: AuthContext,
) -> Result<AppResponse<Vec<RoleResponse>>, AppError> {
if auth_tenant_id != tenant_id {
return Err(AppError::PermissionDenied("tenant:mismatch".into()));
}
state
.authorization_service
.require_permission(tenant_id, user_id, "role:read")
.await?;
let roles = state.role_service.list_roles(tenant_id).await?;
let response = roles
.into_iter()
.map(|r| RoleResponse {
id: r.id,
name: r.name,
description: r.description,
})
.collect();
Ok(AppResponse::ok(response))
}