mirror of
https://github.com/neondatabase/neon.git
synced 2026-07-08 14:40:37 +00:00
## Problema5292f7e67/proxy/src/auth/backend.rs (L146-L148)a5292f7e67/proxy/src/console/provider/neon.rs (L90)a5292f7e67/proxy/src/console/provider/neon.rs (L154)## Summary of changes 1. Test backend is only enabled on `cfg(test)`. 2. Postgres mock backend + MD5 auth keys are only enabled on `cfg(feature = testing)` 3. Password hack and cleartext flow will have their passwords validated before proceeding. 4. Distinguish between ClientCredentials with endpoint and without, removing many panics in the process
110 lines
3.2 KiB
Rust
110 lines
3.2 KiB
Rust
use crate::{
|
|
auth, compute,
|
|
console::{self, provider::NodeInfo},
|
|
error::UserFacingError,
|
|
stream::PqStream,
|
|
waiters,
|
|
};
|
|
use pq_proto::BeMessage as Be;
|
|
use thiserror::Error;
|
|
use tokio::io::{AsyncRead, AsyncWrite};
|
|
use tokio_postgres::config::SslMode;
|
|
use tracing::{info, info_span};
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum LinkAuthError {
|
|
/// Authentication error reported by the console.
|
|
#[error("Authentication failed: {0}")]
|
|
AuthFailed(String),
|
|
|
|
#[error(transparent)]
|
|
WaiterRegister(#[from] waiters::RegisterError),
|
|
|
|
#[error(transparent)]
|
|
WaiterWait(#[from] waiters::WaitError),
|
|
|
|
#[error(transparent)]
|
|
Io(#[from] std::io::Error),
|
|
}
|
|
|
|
impl UserFacingError for LinkAuthError {
|
|
fn to_string_client(&self) -> String {
|
|
use LinkAuthError::*;
|
|
match self {
|
|
AuthFailed(_) => self.to_string(),
|
|
_ => "Internal error".to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn hello_message(redirect_uri: &reqwest::Url, session_id: &str) -> String {
|
|
format!(
|
|
concat![
|
|
"Welcome to Neon!\n",
|
|
"Authenticate by visiting:\n",
|
|
" {redirect_uri}{session_id}\n\n",
|
|
],
|
|
redirect_uri = redirect_uri,
|
|
session_id = session_id,
|
|
)
|
|
}
|
|
|
|
pub fn new_psql_session_id() -> String {
|
|
hex::encode(rand::random::<[u8; 8]>())
|
|
}
|
|
|
|
pub(super) async fn authenticate(
|
|
link_uri: &reqwest::Url,
|
|
client: &mut PqStream<impl AsyncRead + AsyncWrite + Unpin>,
|
|
) -> auth::Result<NodeInfo> {
|
|
let psql_session_id = new_psql_session_id();
|
|
let span = info_span!("link", psql_session_id = &psql_session_id);
|
|
let greeting = hello_message(link_uri, &psql_session_id);
|
|
|
|
let db_info = console::mgmt::with_waiter(psql_session_id, |waiter| async {
|
|
// Give user a URL to spawn a new database.
|
|
info!(parent: &span, "sending the auth URL to the user");
|
|
client
|
|
.write_message_noflush(&Be::AuthenticationOk)?
|
|
.write_message_noflush(&Be::CLIENT_ENCODING)?
|
|
.write_message(&Be::NoticeResponse(&greeting))
|
|
.await?;
|
|
|
|
// Wait for web console response (see `mgmt`).
|
|
info!(parent: &span, "waiting for console's reply...");
|
|
waiter.await?.map_err(LinkAuthError::AuthFailed)
|
|
})
|
|
.await?;
|
|
|
|
client.write_message_noflush(&Be::NoticeResponse("Connecting to database."))?;
|
|
|
|
// This config should be self-contained, because we won't
|
|
// take username or dbname from client's startup message.
|
|
let mut config = compute::ConnCfg::new();
|
|
config
|
|
.host(&db_info.host)
|
|
.port(db_info.port)
|
|
.dbname(&db_info.dbname)
|
|
.user(&db_info.user);
|
|
|
|
// Backwards compatibility. pg_sni_proxy uses "--" in domain names
|
|
// while direct connections do not. Once we migrate to pg_sni_proxy
|
|
// everywhere, we can remove this.
|
|
if db_info.host.contains("--") {
|
|
// we need TLS connection with SNI info to properly route it
|
|
config.ssl_mode(SslMode::Require);
|
|
} else {
|
|
config.ssl_mode(SslMode::Disable);
|
|
}
|
|
|
|
if let Some(password) = db_info.password {
|
|
config.password(password.as_ref());
|
|
}
|
|
|
|
Ok(NodeInfo {
|
|
config,
|
|
aux: db_info.aux,
|
|
allow_self_signed_compute: false, // caller may override
|
|
})
|
|
}
|