fix(presenter): M114 错误分类只看结构化字段(status/kind/server_message),不再 Debug 整条错误
- `AuthError::Response` 的判据从 `format!("{detail:?}")`(连中文 what/digest 一起扫)
改成读 `ResponseDetail{status, kind, server_message, digest}`
- 5xx 与拦截页(Html/Text)归 `Unreachable`(「连不上」而不是「服务器拒绝了你」),
AADSTS/consent/whitelist 标记仍优先判 `NotWhitelisted`
- 补两条分类用例:AADSTS 优先于一般拒绝、5xx/拦截页归不可达
This commit is contained in:
1 parent
73badb8a02
commit
178f6ebbff
1 file changed
+59
-2
@@ -5,6 +5,8 @@
|
||||
|
||||
use unlucky_core::auth::AuthError;
|
||||
|
||||
use unlucky_core::auth::BodyKind;
|
||||
|
||||
use crate::dto::{AccountNoticeCode, LoginFailure};
|
||||
|
||||
/// 账户写操作的失败:**结构化码 + 原文**(原文只进日志/详细信息,不上屏)。
|
||||
@@ -44,10 +46,17 @@ pub(super) fn classify(error: &AuthError) -> LoginFailure {
|
||||
LoginFailure::Denied
|
||||
}
|
||||
}
|
||||
// 只看 `ResponseDetail` 的**结构化字段**(status / kind / server_message),
|
||||
// 不把整条错误 Debug 成字符串再去扫——那会把中文 `what`/`digest` 也算进判据里。
|
||||
AuthError::Response(detail) => {
|
||||
let blob = format!("{detail:?}");
|
||||
if mentions_whitelist(&blob) {
|
||||
let server = detail.server_message.as_deref().unwrap_or_default();
|
||||
let digest = detail.digest.as_str();
|
||||
if mentions_whitelist(server) || mentions_whitelist(digest) {
|
||||
LoginFailure::NotWhitelisted
|
||||
} else if detail.status >= 500 || matches!(detail.kind, BodyKind::Html | BodyKind::Text)
|
||||
{
|
||||
// 5xx / 拦截页:服务器或链路出了问题,不是「它拒绝了你」。
|
||||
LoginFailure::Unreachable
|
||||
} else {
|
||||
LoginFailure::Denied
|
||||
}
|
||||
@@ -67,3 +76,51 @@ pub(super) fn mentions_whitelist(text: &str) -> bool {
|
||||
|| lowered.contains("whitelist")
|
||||
|| lowered.contains("not approved")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use unlucky_core::auth::body::ResponseDetail;
|
||||
|
||||
fn response(status: u16, kind: BodyKind, server_message: Option<&str>) -> AuthError {
|
||||
AuthError::Response(Box::new(ResponseDetail {
|
||||
step: "xsts".to_string(),
|
||||
what: "XSTS 授权".to_string(),
|
||||
status,
|
||||
content_type: None,
|
||||
kind,
|
||||
server_message: server_message.map(str::to_string),
|
||||
digest: "digest".to_string(),
|
||||
}))
|
||||
}
|
||||
|
||||
/// 403 带 AADSTS 码 → 白名单未批;403 不带 → 一般性拒绝。
|
||||
#[test]
|
||||
fn aadsts_markers_win_over_the_generic_denial() {
|
||||
assert_eq!(
|
||||
classify(&response(
|
||||
403,
|
||||
BodyKind::Json,
|
||||
Some("AADSTS7000218: app not approved")
|
||||
)),
|
||||
LoginFailure::NotWhitelisted
|
||||
);
|
||||
assert_eq!(
|
||||
classify(&response(403, BodyKind::Json, Some("invalid_grant"))),
|
||||
LoginFailure::Denied
|
||||
);
|
||||
}
|
||||
|
||||
/// 5xx 与「拦截页」(HTML/纯文本)归「连不上」,不是「服务器拒绝了你」。
|
||||
#[test]
|
||||
fn upstream_failures_and_intercept_pages_are_unreachable() {
|
||||
assert_eq!(
|
||||
classify(&response(503, BodyKind::Json, None)),
|
||||
LoginFailure::Unreachable
|
||||
);
|
||||
assert_eq!(
|
||||
classify(&response(200, BodyKind::Html, None)),
|
||||
LoginFailure::Unreachable
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user