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