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