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