Line data Source code
1 : //! Client authentication mechanisms.
2 :
3 : pub mod backend;
4 : pub use backend::Backend;
5 :
6 : mod credentials;
7 : pub(crate) use credentials::{
8 : check_peer_addr_is_in_list, endpoint_sni, ComputeUserInfoMaybeEndpoint,
9 : ComputeUserInfoParseError, IpPattern,
10 : };
11 :
12 : mod password_hack;
13 : pub(crate) use password_hack::parse_endpoint_param;
14 : use password_hack::PasswordHackPayload;
15 :
16 : mod flow;
17 : use std::io;
18 : use std::net::IpAddr;
19 :
20 : pub(crate) use flow::*;
21 : use thiserror::Error;
22 : use tokio::time::error::Elapsed;
23 :
24 : use crate::control_plane;
25 : use crate::error::{ReportableError, UserFacingError};
26 :
27 : /// Convenience wrapper for the authentication error.
28 : pub(crate) type Result<T> = std::result::Result<T, AuthError>;
29 :
30 : /// Common authentication error.
31 6 : #[derive(Debug, Error)]
32 : pub(crate) enum AuthError {
33 : #[error(transparent)]
34 : Web(#[from] backend::WebAuthError),
35 :
36 : #[error(transparent)]
37 : GetAuthInfo(#[from] control_plane::errors::GetAuthInfoError),
38 :
39 : /// SASL protocol errors (includes [SCRAM](crate::scram)).
40 : #[error(transparent)]
41 : Sasl(#[from] crate::sasl::Error),
42 :
43 : #[error("Unsupported authentication method: {0}")]
44 : BadAuthMethod(Box<str>),
45 :
46 : #[error("Malformed password message: {0}")]
47 : MalformedPassword(&'static str),
48 :
49 : #[error(
50 : "Endpoint ID is not specified. \
51 : Either please upgrade the postgres client library (libpq) for SNI support \
52 : or pass the endpoint ID (first part of the domain name) as a parameter: '?options=endpoint%3D<endpoint-id>'. \
53 : See more at https://neon.tech/sni"
54 : )]
55 : MissingEndpointName,
56 :
57 : #[error("password authentication failed for user '{0}'")]
58 : AuthFailed(Box<str>),
59 :
60 : /// Errors produced by e.g. [`crate::stream::PqStream`].
61 : #[error(transparent)]
62 : Io(#[from] io::Error),
63 :
64 : #[error(
65 : "This IP address {0} is not allowed to connect to this endpoint. \
66 : Please add it to the allowed list in the Neon console. \
67 : Make sure to check for IPv4 or IPv6 addresses."
68 : )]
69 : IpAddressNotAllowed(IpAddr),
70 :
71 : #[error("Too many connections to this endpoint. Please try again later.")]
72 : TooManyConnections,
73 :
74 : #[error("Authentication timed out")]
75 : UserTimeout(Elapsed),
76 :
77 : #[error("Disconnected due to inactivity after {0}.")]
78 : ConfirmationTimeout(humantime::Duration),
79 : }
80 :
81 : impl AuthError {
82 0 : pub(crate) fn bad_auth_method(name: impl Into<Box<str>>) -> Self {
83 0 : AuthError::BadAuthMethod(name.into())
84 0 : }
85 :
86 0 : pub(crate) fn auth_failed(user: impl Into<Box<str>>) -> Self {
87 0 : AuthError::AuthFailed(user.into())
88 0 : }
89 :
90 0 : pub(crate) fn ip_address_not_allowed(ip: IpAddr) -> Self {
91 0 : AuthError::IpAddressNotAllowed(ip)
92 0 : }
93 :
94 0 : pub(crate) fn too_many_connections() -> Self {
95 0 : AuthError::TooManyConnections
96 0 : }
97 :
98 0 : pub(crate) fn is_auth_failed(&self) -> bool {
99 0 : matches!(self, AuthError::AuthFailed(_))
100 0 : }
101 :
102 0 : pub(crate) fn user_timeout(elapsed: Elapsed) -> Self {
103 0 : AuthError::UserTimeout(elapsed)
104 0 : }
105 :
106 0 : pub(crate) fn confirmation_timeout(timeout: humantime::Duration) -> Self {
107 0 : AuthError::ConfirmationTimeout(timeout)
108 0 : }
109 : }
110 :
111 : impl UserFacingError for AuthError {
112 0 : fn to_string_client(&self) -> String {
113 0 : match self {
114 0 : Self::Web(e) => e.to_string_client(),
115 0 : Self::GetAuthInfo(e) => e.to_string_client(),
116 0 : Self::Sasl(e) => e.to_string_client(),
117 0 : Self::AuthFailed(_) => self.to_string(),
118 0 : Self::BadAuthMethod(_) => self.to_string(),
119 0 : Self::MalformedPassword(_) => self.to_string(),
120 0 : Self::MissingEndpointName => self.to_string(),
121 0 : Self::Io(_) => "Internal error".to_string(),
122 0 : Self::IpAddressNotAllowed(_) => self.to_string(),
123 0 : Self::TooManyConnections => self.to_string(),
124 0 : Self::UserTimeout(_) => self.to_string(),
125 0 : Self::ConfirmationTimeout(_) => self.to_string(),
126 : }
127 0 : }
128 : }
129 :
130 : impl ReportableError for AuthError {
131 0 : fn get_error_kind(&self) -> crate::error::ErrorKind {
132 0 : match self {
133 0 : Self::Web(e) => e.get_error_kind(),
134 0 : Self::GetAuthInfo(e) => e.get_error_kind(),
135 0 : Self::Sasl(e) => e.get_error_kind(),
136 0 : Self::AuthFailed(_) => crate::error::ErrorKind::User,
137 0 : Self::BadAuthMethod(_) => crate::error::ErrorKind::User,
138 0 : Self::MalformedPassword(_) => crate::error::ErrorKind::User,
139 0 : Self::MissingEndpointName => crate::error::ErrorKind::User,
140 0 : Self::Io(_) => crate::error::ErrorKind::ClientDisconnect,
141 0 : Self::IpAddressNotAllowed(_) => crate::error::ErrorKind::User,
142 0 : Self::TooManyConnections => crate::error::ErrorKind::RateLimit,
143 0 : Self::UserTimeout(_) => crate::error::ErrorKind::User,
144 0 : Self::ConfirmationTimeout(_) => crate::error::ErrorKind::User,
145 : }
146 0 : }
147 : }
|