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