Skip to main content

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}