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 : pub(crate) use flow::*;
18 : use tokio::time::error::Elapsed;
19 :
20 : use crate::{
21 : console,
22 : error::{ReportableError, UserFacingError},
23 : };
24 : use std::{io, net::IpAddr};
25 : use thiserror::Error;
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 AuthErrorImpl {
33 : #[error(transparent)]
34 : Web(#[from] backend::WebAuthError),
35 :
36 : #[error(transparent)]
37 : GetAuthInfo(#[from] console::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 :
78 0 : #[derive(Debug, Error)]
79 : #[error(transparent)]
80 : pub(crate) struct AuthError(Box<AuthErrorImpl>);
81 :
82 : impl AuthError {
83 0 : pub(crate) fn bad_auth_method(name: impl Into<Box<str>>) -> Self {
84 0 : AuthErrorImpl::BadAuthMethod(name.into()).into()
85 0 : }
86 :
87 0 : pub(crate) fn auth_failed(user: impl Into<Box<str>>) -> Self {
88 0 : AuthErrorImpl::AuthFailed(user.into()).into()
89 0 : }
90 :
91 0 : pub(crate) fn ip_address_not_allowed(ip: IpAddr) -> Self {
92 0 : AuthErrorImpl::IpAddressNotAllowed(ip).into()
93 0 : }
94 :
95 0 : pub(crate) fn too_many_connections() -> Self {
96 0 : AuthErrorImpl::TooManyConnections.into()
97 0 : }
98 :
99 0 : pub(crate) fn is_auth_failed(&self) -> bool {
100 0 : matches!(self.0.as_ref(), AuthErrorImpl::AuthFailed(_))
101 0 : }
102 :
103 0 : pub(crate) fn user_timeout(elapsed: Elapsed) -> Self {
104 0 : AuthErrorImpl::UserTimeout(elapsed).into()
105 0 : }
106 : }
107 :
108 : impl<E: Into<AuthErrorImpl>> From<E> for AuthError {
109 6 : fn from(e: E) -> Self {
110 6 : Self(Box::new(e.into()))
111 6 : }
112 : }
113 :
114 : impl UserFacingError for AuthError {
115 0 : fn to_string_client(&self) -> String {
116 0 : match self.0.as_ref() {
117 0 : AuthErrorImpl::Web(e) => e.to_string_client(),
118 0 : AuthErrorImpl::GetAuthInfo(e) => e.to_string_client(),
119 0 : AuthErrorImpl::Sasl(e) => e.to_string_client(),
120 0 : AuthErrorImpl::AuthFailed(_) => self.to_string(),
121 0 : AuthErrorImpl::BadAuthMethod(_) => self.to_string(),
122 0 : AuthErrorImpl::MalformedPassword(_) => self.to_string(),
123 0 : AuthErrorImpl::MissingEndpointName => self.to_string(),
124 0 : AuthErrorImpl::Io(_) => "Internal error".to_string(),
125 0 : AuthErrorImpl::IpAddressNotAllowed(_) => self.to_string(),
126 0 : AuthErrorImpl::TooManyConnections => self.to_string(),
127 0 : AuthErrorImpl::UserTimeout(_) => self.to_string(),
128 : }
129 0 : }
130 : }
131 :
132 : impl ReportableError for AuthError {
133 0 : fn get_error_kind(&self) -> crate::error::ErrorKind {
134 0 : match self.0.as_ref() {
135 0 : AuthErrorImpl::Web(e) => e.get_error_kind(),
136 0 : AuthErrorImpl::GetAuthInfo(e) => e.get_error_kind(),
137 0 : AuthErrorImpl::Sasl(e) => e.get_error_kind(),
138 0 : AuthErrorImpl::AuthFailed(_) => crate::error::ErrorKind::User,
139 0 : AuthErrorImpl::BadAuthMethod(_) => crate::error::ErrorKind::User,
140 0 : AuthErrorImpl::MalformedPassword(_) => crate::error::ErrorKind::User,
141 0 : AuthErrorImpl::MissingEndpointName => crate::error::ErrorKind::User,
142 0 : AuthErrorImpl::Io(_) => crate::error::ErrorKind::ClientDisconnect,
143 0 : AuthErrorImpl::IpAddressNotAllowed(_) => crate::error::ErrorKind::User,
144 0 : AuthErrorImpl::TooManyConnections => crate::error::ErrorKind::RateLimit,
145 0 : AuthErrorImpl::UserTimeout(_) => crate::error::ErrorKind::User,
146 : }
147 0 : }
148 : }
|