parlov_analysis/aggregation/auth_types.rs
1//! Auth-block classifier types.
2//!
3//! Pure data types used by the two-stage classifier. No I/O, no parsing — those live in
4//! [`super::auth_parsers`] and [`super::auth_classifier`].
5
6use super::precondition::PreconditionBlock;
7
8/// Family of auth-related block. Each family corresponds to a distinct operator action.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum AuthBlockFamily {
11 /// 401 / `WWW-Authenticate` at origin server.
12 OriginAuthentication,
13 /// 403 with explicit auth-layer evidence (e.g. `insufficient_scope` challenge).
14 OriginAuthorization,
15 /// 407 / `Proxy-Authenticate`.
16 ProxyAuthentication,
17 /// 511 captive portal / network gatekeeper.
18 NetworkAuthentication,
19 /// 3xx redirect to a login/auth endpoint (heuristic).
20 LoginRedirect,
21 /// JSON/XML auth error envelope on an otherwise non-auth status.
22 AuthErrorEnvelope,
23}
24
25/// Confidence in an auth-block classification. RFC-conformant signals are `Strong`;
26/// non-conformant or heuristic signals are `Medium`; weak/body-only signals are `Weak`.
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
28pub enum AuthBlockConfidence {
29 /// Weak — body-only signal or generic denial.
30 Weak,
31 /// Medium — non-RFC-conformant or heuristic signal.
32 Medium,
33 /// Strong — RFC-grounded signal (e.g. parsed `WWW-Authenticate` challenge).
34 Strong,
35}
36
37/// Why the credential failed (or was not provided). Determines operator action.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum CredentialBlockKind {
40 /// Request had no `Authorization` header AND response was an auth challenge.
41 NoCredential,
42 /// `Authorization` header was present AND response was `invalid_token` or equivalent.
43 CredentialRejected,
44 /// `Authorization` present AND response was `insufficient_scope`.
45 InsufficientScope,
46 /// Auth challenge fired but the specific failure reason could not be identified.
47 UnknownAuthFailure,
48 /// 511 / proxy-auth — the credential concept does not apply at this layer.
49 NotApplicable,
50}
51
52/// Auth scheme identifier (case-insensitive). RFC 7235.
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub enum AuthScheme {
55 /// `Bearer` (RFC 6750).
56 Bearer,
57 /// `Basic` (RFC 7617).
58 Basic,
59 /// `Digest` (RFC 7616).
60 Digest,
61 /// Any other or unrecognised scheme.
62 Other,
63}
64
65/// Parsed `WWW-Authenticate` or `Proxy-Authenticate` challenge.
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct AuthChallenge {
68 /// Auth scheme identifier.
69 pub scheme: AuthScheme,
70 /// `realm=` parameter, if present.
71 pub realm: Option<String>,
72 /// `error=` parameter (RFC 6750 §3), if present.
73 pub error: Option<String>,
74 /// `error_description=` parameter (RFC 6750 §3), if present.
75 pub error_description: Option<String>,
76 /// `scope=` parameter, if present.
77 pub scope: Option<String>,
78}
79
80/// Auth error parsed from a response body (typically JSON).
81#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct AuthErrorBodySignal {
83 /// Recognised auth error value (e.g. `invalid_token`, `unauthorized`).
84 pub code: String,
85 /// Confidence in the recognition.
86 pub confidence: AuthBlockConfidence,
87}
88
89/// Login-redirect signal extracted from a 3xx response.
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct LoginRedirectSignal {
92 /// Raw `Location` header value.
93 pub location: String,
94 /// Confidence in the heuristic match.
95 pub confidence: AuthBlockConfidence,
96}
97
98/// Classified auth-block signature on a single response.
99#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct AuthBlockSignature {
101 /// Family of auth involvement.
102 pub family: AuthBlockFamily,
103 /// Confidence in the classification.
104 pub confidence: AuthBlockConfidence,
105 /// HTTP status code on the response.
106 pub status: u16,
107 /// Why the credential failed or was not provided.
108 pub credential_state: CredentialBlockKind,
109 /// Parsed challenge from `WWW-Authenticate` or `Proxy-Authenticate`, if present.
110 pub challenge: Option<AuthChallenge>,
111 /// Parsed auth-error from the response body, if present.
112 pub body_signal: Option<AuthErrorBodySignal>,
113 /// Login-redirect signal, populated when family is `LoginRedirect`.
114 pub login_redirect: Option<LoginRedirectSignal>,
115}
116
117/// Result of the auth-gate decision. Three states matter: gate, do-not-gate
118/// (preserve evidence), and no-auth-involvement (continue to other gates).
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub enum AuthGateDecision {
121 /// Both sides classified equivalently — downgrade Contradictory to Inapplicable.
122 Gate(PreconditionBlock),
123 /// At least one side has auth involvement and a differential exists. Preserve as evidence.
124 DoNotGate,
125 /// Neither side has auth involvement. Other gates may still apply.
126 NoAuthInvolvement,
127}