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