parlov-analysis 0.7.0

Analysis engine trait and signal detection for parlov.
Documentation
//! Auth-block classifier types.
//!
//! Pure data types used by the two-stage classifier. No I/O, no parsing — those live in
//! [`super::auth_parsers`] and [`super::auth_classifier`].

use super::precondition::PreconditionBlock;

/// Family of auth-related block. Each family corresponds to a distinct operator action.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthBlockFamily {
    /// 401 / `WWW-Authenticate` at origin server.
    OriginAuthentication,
    /// 403 with explicit auth-layer evidence (e.g. `insufficient_scope` challenge).
    OriginAuthorization,
    /// 407 / `Proxy-Authenticate`.
    ProxyAuthentication,
    /// 511 captive portal / network gatekeeper.
    NetworkAuthentication,
    /// 3xx redirect to a login/auth endpoint (heuristic).
    LoginRedirect,
    /// JSON/XML auth error envelope on an otherwise non-auth status.
    AuthErrorEnvelope,
}

/// Confidence in an auth-block classification. RFC-conformant signals are `Strong`;
/// non-conformant or heuristic signals are `Medium`; weak/body-only signals are `Weak`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum AuthBlockConfidence {
    /// Weak — body-only signal or generic denial.
    Weak,
    /// Medium — non-RFC-conformant or heuristic signal.
    Medium,
    /// Strong — RFC-grounded signal (e.g. parsed `WWW-Authenticate` challenge).
    Strong,
}

/// Why the credential failed (or was not provided). Determines operator action.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CredentialBlockKind {
    /// Request had no `Authorization` header AND response was an auth challenge.
    NoCredential,
    /// `Authorization` header was present AND response was `invalid_token` or equivalent.
    CredentialRejected,
    /// `Authorization` present AND response was `insufficient_scope`.
    InsufficientScope,
    /// Auth challenge fired but the specific failure reason could not be identified.
    UnknownAuthFailure,
    /// 511 / proxy-auth — the credential concept does not apply at this layer.
    NotApplicable,
}

/// Auth scheme identifier (case-insensitive). RFC 7235.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthScheme {
    /// `Bearer` (RFC 6750).
    Bearer,
    /// `Basic` (RFC 7617).
    Basic,
    /// `Digest` (RFC 7616).
    Digest,
    /// Any other or unrecognised scheme.
    Other,
}

/// Parsed `WWW-Authenticate` or `Proxy-Authenticate` challenge.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AuthChallenge {
    /// Auth scheme identifier.
    pub scheme: AuthScheme,
    /// `realm=` parameter, if present.
    pub realm: Option<String>,
    /// `error=` parameter (RFC 6750 §3), if present.
    pub error: Option<String>,
    /// `error_description=` parameter (RFC 6750 §3), if present.
    pub error_description: Option<String>,
    /// `scope=` parameter, if present.
    pub scope: Option<String>,
}

/// Auth error parsed from a response body (typically JSON).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AuthErrorBodySignal {
    /// Recognised auth error value (e.g. `invalid_token`, `unauthorized`).
    pub code: String,
    /// Confidence in the recognition.
    pub confidence: AuthBlockConfidence,
}

/// Login-redirect signal extracted from a 3xx response.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LoginRedirectSignal {
    /// Raw `Location` header value.
    pub location: String,
    /// Confidence in the heuristic match.
    pub confidence: AuthBlockConfidence,
}

/// Classified auth-block signature on a single response.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AuthBlockSignature {
    /// Family of auth involvement.
    pub family: AuthBlockFamily,
    /// Confidence in the classification.
    pub confidence: AuthBlockConfidence,
    /// HTTP status code on the response.
    pub status: u16,
    /// Why the credential failed or was not provided.
    pub credential_state: CredentialBlockKind,
    /// Parsed challenge from `WWW-Authenticate` or `Proxy-Authenticate`, if present.
    pub challenge: Option<AuthChallenge>,
    /// Parsed auth-error from the response body, if present.
    pub body_signal: Option<AuthErrorBodySignal>,
    /// Login-redirect signal, populated when family is `LoginRedirect`.
    pub login_redirect: Option<LoginRedirectSignal>,
}

/// Result of the auth-gate decision. Three states matter: gate, do-not-gate
/// (preserve evidence), and no-auth-involvement (continue to other gates).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AuthGateDecision {
    /// Both sides classified equivalently — downgrade Contradictory to Inapplicable.
    Gate(PreconditionBlock),
    /// At least one side has auth involvement and a differential exists. Preserve as evidence.
    DoNotGate,
    /// Neither side has auth involvement. Other gates may still apply.
    NoAuthInvolvement,
}