Skip to main content

clio_auth/
error.rs

1use poem::error::ResponseError;
2use poem::http::StatusCode;
3use std::io;
4use std::net::IpAddr;
5use std::ops::Range;
6
7use thiserror::Error;
8use tokio::runtime::TryCurrentError;
9use tokio::sync::mpsc;
10use tokio::task::JoinError;
11
12use crate::server::ServerControl;
13
14/// Errors that can occur during helper configuration.
15#[derive(Error, Debug)]
16pub enum ConfigError {
17    /// Web server parameters were not correct.
18    #[error("Invalid server config (expected {expected}, found {found})")]
19    InvalidServerConfig { expected: String, found: String },
20    /// The configured address and port were not available to listen on.
21    #[error("Cannot bind to {addr} on any port from {}-{}", port_range.start, port_range.end - 1)]
22    CannotBindAddress {
23        addr: IpAddr,
24        port_range: Range<u16>,
25    },
26}
27
28/// Errors that can occur from the internal web server during the OAuth flow.
29#[derive(Error, Debug)]
30pub enum ServerError {
31    /// The Tokio runtime could not be found
32    #[error("Tokio must be running")]
33    AsyncRuntimeRequired(#[from] TryCurrentError),
34    #[error("Internal runtime error")]
35    InternalRuntimeError(#[from] JoinError),
36    /// Error sending a signal to the internal server
37    #[error("Error signaling server")]
38    InternalCommError(#[from] mpsc::error::SendError<ServerControl>),
39    /// Problem occurred running the server
40    #[error("Internal server error")]
41    InternalServerError(#[from] io::Error),
42    /// No authorization code was received
43    #[error("No authorization code received")]
44    NoResult,
45}
46
47impl ResponseError for ServerError {
48    fn status(&self) -> StatusCode {
49        use ServerError::*;
50        match self {
51            NoResult => StatusCode::BAD_REQUEST,
52            _ => StatusCode::INTERNAL_SERVER_ERROR,
53        }
54    }
55}
56
57/// Errors that can occur during the authorization flow.
58#[derive(Error, Debug)]
59pub enum AuthError {
60    /// Invalid CSRF token (state parameter). Indicates a possible replay attack.
61    #[error("Invalid CSRF token (state parameter)")]
62    CsrfMismatch,
63    /// No authorization code or PKCE verifier present. Might indicate that `validate` was invoked
64    /// without a successful call to `authorize`.
65    #[error("No authorization code or PKCE verifier present")]
66    InvalidAuthState,
67}
68
69#[cfg(test)]
70mod tests {
71    mod config_error {
72        use crate::ConfigError;
73
74        #[test]
75        fn invalid_server_config() {
76            let error = ConfigError::InvalidServerConfig {
77                expected: "blah".to_owned(),
78                found: "foobar".to_owned(),
79            };
80            assert!(
81                format!("{error}").contains("Invalid server config (expected blah, found foobar)")
82            );
83        }
84
85        #[test]
86        fn cannot_bind_address() {
87            let error = ConfigError::CannotBindAddress {
88                addr: [127, 0, 0, 1].into(),
89                port_range: 5678..6789,
90            };
91            assert!(
92                format!("{error}").contains("Cannot bind to 127.0.0.1 on any port from 5678-6788")
93            )
94        }
95    }
96
97    mod server_error {
98        use crate::ServerError;
99        use poem::error::ResponseError;
100        use poem::http::StatusCode;
101        use std::io;
102        use std::io::ErrorKind;
103
104        #[test]
105        fn no_result_response_error_trait() {
106            let response = ServerError::NoResult.as_response();
107            assert_eq!(response.status(), StatusCode::BAD_REQUEST);
108        }
109
110        #[test]
111        fn internal_server_error_response_error_trait() {
112            let io_error = io::Error::from(ErrorKind::AddrInUse);
113            let response = ServerError::InternalServerError(io_error).as_response();
114            assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
115        }
116    }
117
118    mod auth_error {
119        use crate::AuthError;
120
121        #[test]
122        fn csrf_mismatch() {
123            assert!(format!("{}", AuthError::CsrfMismatch)
124                .contains("Invalid CSRF token (state parameter)"));
125        }
126
127        #[test]
128        fn invalid_auth_state() {
129            assert!(format!("{}", AuthError::InvalidAuthState)
130                .contains("No authorization code or PKCE verifier present"));
131        }
132    }
133}