Merge branch 'main' into opencode/sunny-orchid

# Conflicts:
#	README.md
This commit is contained in:
RWDai
2026-05-14 20:22:36 +08:00
38 changed files with 363 additions and 789 deletions

View File

@@ -503,7 +503,7 @@ fn normalize_selection_filters(
.map(|value| value.trim().to_ascii_lowercase())
.filter(|value| !value.is_empty() && value != "all")
{
Some(role) if matches!(role.as_str(), "user" | "admin") => Some(role),
Some(role) if crate::roles::normalize_assignable_user_role(&role).is_some() => Some(role),
Some(_) => return Err("role 参数不合法".to_string()),
None => None,
};

View File

@@ -225,16 +225,13 @@ pub(super) fn validate_admin_user_password(password: &str, policy: &str) -> Resu
}
pub(super) fn normalize_admin_user_role(value: Option<&str>) -> Result<String, String> {
match value
let role = value
.map(str::trim)
.filter(|value| !value.is_empty())
.unwrap_or("user")
.to_ascii_lowercase()
.as_str()
{
"user" => Ok("user".to_string()),
"admin" => Ok("admin".to_string()),
_ => Err("角色参数不合法".to_string()),
.unwrap_or("user");
match crate::roles::normalize_assignable_user_role(role) {
Some(role) => Ok(role.to_string()),
None => Err("角色参数不合法".to_string()),
}
}

View File

@@ -2,6 +2,7 @@ use super::super::internal;
use crate::admin_api;
use crate::audit::attach_admin_audit_event;
use crate::control::{
audit_admin_read_only_management_token_permissions,
validate_management_token_admin_route_permission, GatewayPublicRequestContext,
};
use crate::{AppState, GatewayError};
@@ -61,21 +62,32 @@ fn maybe_build_management_token_permission_denied_response(
) -> Option<Response<Body>> {
let decision = request_context.control_decision.as_ref()?;
let admin_principal = decision.admin_principal.as_ref()?;
let token_id = admin_principal.management_token_id.as_deref()?;
let audit_admin_read_only_permissions;
let token_permissions = if crate::roles::can_write_admin_console(&admin_principal.user_role) {
admin_principal.management_token_permissions.as_deref()
} else {
audit_admin_read_only_permissions = audit_admin_read_only_management_token_permissions();
Some(audit_admin_read_only_permissions.as_slice())
};
let denied = validate_management_token_admin_route_permission(
&request_context.request_method,
decision,
admin_principal.management_token_permissions.as_deref(),
token_permissions,
)
.err()?;
let actor_id = admin_principal
.management_token_id
.as_deref()
.unwrap_or(admin_principal.user_id.as_str());
warn!(
trace_id = %request_context.trace_id,
admin_management_token_id = %token_id,
admin_actor_id = %actor_id,
admin_user_role = %admin_principal.user_role,
route_family = decision.route_family.as_deref().unwrap_or("unknown"),
route_kind = decision.route_kind.as_deref().unwrap_or("unknown"),
required_permission = %denied.required_permission,
"management token permission denied"
"admin route permission denied"
);
let mut response = (
@@ -91,10 +103,10 @@ fn maybe_build_management_token_permission_denied_response(
.into_response();
attach_admin_audit_event(
&mut response,
"admin_management_token_permission_denied",
"admin_route_permission_denied",
"permission_denied",
"management_token_permission",
token_id,
"admin_route_permission",
actor_id,
);
Some(response)
}

View File

@@ -228,7 +228,7 @@ async fn maybe_promote_management_token_admin_principal(
let Some(user) = state.find_user_auth_by_id(&token_with_user.user.id).await? else {
return Ok(());
};
if !user.is_active || user.is_deleted || !user.role.eq_ignore_ascii_case("admin") {
if !user.is_active || user.is_deleted || !crate::roles::can_access_admin_console(&user.role) {
return Ok(());
}
let management_token_permissions = match management_token_permission_keys_from_value(