Line data Source code
1 : use super::{
2 : ComputeCredentialKeys, ComputeCredentials, ComputeUserInfo, ComputeUserInfoNoEndpoint,
3 : };
4 : use crate::{
5 : auth::{self, AuthFlow},
6 : console::AuthSecret,
7 : context::RequestMonitoring,
8 : sasl,
9 : stream::{self, Stream},
10 : };
11 : use tokio::io::{AsyncRead, AsyncWrite};
12 : use tracing::{info, warn};
13 :
14 : /// Compared to [SCRAM](crate::scram), cleartext password auth saves
15 : /// one round trip and *expensive* computations (>= 4096 HMAC iterations).
16 : /// These properties are benefical for serverless JS workers, so we
17 : /// use this mechanism for websocket connections.
18 2 : pub async fn authenticate_cleartext(
19 2 : ctx: &mut RequestMonitoring,
20 2 : info: ComputeUserInfo,
21 2 : client: &mut stream::PqStream<Stream<impl AsyncRead + AsyncWrite + Unpin>>,
22 2 : secret: AuthSecret,
23 2 : ) -> auth::Result<ComputeCredentials> {
24 2 : warn!("cleartext auth flow override is enabled, proceeding");
25 2 : ctx.set_auth_method(crate::context::AuthMethod::Cleartext);
26 2 :
27 2 : // pause the timer while we communicate with the client
28 2 : let paused = ctx.latency_timer.pause(crate::metrics::Waiting::Client);
29 :
30 2 : let auth_flow = AuthFlow::new(client)
31 2 : .begin(auth::CleartextPassword(secret))
32 0 : .await?;
33 2 : drop(paused);
34 : // cleartext auth is only allowed to the ws/http protocol.
35 : // If we're here, we already received the password in the first message.
36 : // Scram protocol will be executed on the proxy side.
37 8 : let auth_outcome = auth_flow.authenticate().await?;
38 :
39 2 : let keys = match auth_outcome {
40 2 : sasl::Outcome::Success(key) => key,
41 0 : sasl::Outcome::Failure(reason) => {
42 0 : info!("auth backend failed with an error: {reason}");
43 0 : return Err(auth::AuthError::auth_failed(&*info.user));
44 : }
45 : };
46 :
47 2 : Ok(ComputeCredentials { info, keys })
48 2 : }
49 :
50 : /// Workaround for clients which don't provide an endpoint (project) name.
51 : /// Similar to [`authenticate_cleartext`], but there's a specific password format,
52 : /// and passwords are not yet validated (we don't know how to validate them!)
53 2 : pub async fn password_hack_no_authentication(
54 2 : ctx: &mut RequestMonitoring,
55 2 : info: ComputeUserInfoNoEndpoint,
56 2 : client: &mut stream::PqStream<Stream<impl AsyncRead + AsyncWrite + Unpin>>,
57 2 : ) -> auth::Result<ComputeCredentials> {
58 2 : warn!("project not specified, resorting to the password hack auth flow");
59 2 : ctx.set_auth_method(crate::context::AuthMethod::Cleartext);
60 2 :
61 2 : // pause the timer while we communicate with the client
62 2 : let _paused = ctx.latency_timer.pause(crate::metrics::Waiting::Client);
63 :
64 2 : let payload = AuthFlow::new(client)
65 2 : .begin(auth::PasswordHack)
66 0 : .await?
67 2 : .get_password()
68 2 : .await?;
69 :
70 2 : info!(project = &*payload.endpoint, "received missing parameter");
71 :
72 : // Report tentative success; compute node will check the password anyway.
73 2 : Ok(ComputeCredentials {
74 2 : info: ComputeUserInfo {
75 2 : user: info.user,
76 2 : options: info.options,
77 2 : endpoint: payload.endpoint,
78 2 : },
79 2 : keys: ComputeCredentialKeys::Password(payload.password),
80 2 : })
81 2 : }
|