Skip to content

Commit 2c17d81

Browse files
authored
sasl/scram (#33468)
<!-- Describe the contents of the PR briefly but completely. If you write detailed commit messages, it is acceptable to copy/paste them here, or write "see commit messages for details." If there is only one commit in the PR, GitHub will have already added its commit message above. --> ### Motivation <!-- Which of the following best describes the motivation behind this PR? * This PR fixes a recognized bug. [Ensure issue is linked somewhere.] * This PR adds a known-desirable feature. [Ensure issue is linked somewhere.] * This PR fixes a previously unreported bug. [Describe the bug in detail, as if you were filing a bug report.] * This PR adds a feature that has not yet been specified. [Write a brief specification for the feature, including justification for its inclusion in Materialize, as if you were writing the original feature specification.] * This PR refactors existing code. [Describe what was wrong with the existing code, if it is not obvious.] --> ### Tips for reviewer <!-- Leave some tips for your reviewer, like: * The diff is much smaller if viewed with whitespace hidden. * [Some function/module/file] deserves extra attention. * [Some function/module/file] is pure code movement and only needs a skim. Delete this section if no tips. --> ### Checklist - [ ] This PR has adequate test coverage / QA involvement has been duly considered. ([trigger-ci for additional test/nightly runs](https://trigger-ci.dev.materialize.com/)) - [ ] This PR has an associated up-to-date [design doc](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/design/README.md), is a design doc ([template](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/design/00000000_template.md)), or is sufficiently small to not require a design. <!-- Reference the design in the description. --> - [ ] If this PR evolves [an existing `$T ⇔ Proto$T` mapping](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/command-and-response-binary-encoding.md) (possibly in a backwards-incompatible way), then it is tagged with a `T-proto` label. - [ ] If this PR will require changes to cloud orchestration or tests, there is a companion cloud PR to account for those changes that is tagged with the release-blocker label ([example](MaterializeInc/cloud#5021)). <!-- Ask in #team-cloud on Slack if you need help preparing the cloud PR. --> - [ ] If this PR includes major [user-facing behavior changes](https://github.com/MaterializeInc/materialize/blob/main/doc/developer/guide-changes.md#what-changes-require-a-release-note), I have pinged the relevant PM to schedule a changelog post.
1 parent 7893a49 commit 2c17d81

File tree

33 files changed

+1277
-52
lines changed

33 files changed

+1277
-52
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/adapter/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ anyhow = "1.0.98"
1414
arrow = { version = "55.2.0", default-features = false }
1515
async-stream = "0.3.6"
1616
async-trait = "0.1.88"
17+
base64 = "0.22.1"
1718
bytes = "1.10.1"
1819
bytesize = "1.3.0"
1920
chrono = { version = "0.4.39", default-features = false, features = ["std"] }

src/adapter/src/catalog/open.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ impl Catalog {
151151
source_references: BTreeMap::new(),
152152
storage_metadata: Default::default(),
153153
temporary_schemas: BTreeMap::new(),
154+
mock_authentication_nonce: Default::default(),
154155
config: mz_sql::catalog::CatalogConfig {
155156
start_time: to_datetime((config.now)()),
156157
start_instant: Instant::now(),
@@ -401,6 +402,13 @@ impl Catalog {
401402
.unwrap_or("new")
402403
.to_string();
403404

405+
let mz_authentication_mock_nonce =
406+
txn.get_authentication_mock_nonce().ok_or_else(|| {
407+
Error::new(ErrorKind::SettingError("authentication nonce".to_string()))
408+
})?;
409+
410+
state.mock_authentication_nonce = Some(mz_authentication_mock_nonce);
411+
404412
// Migrate item ASTs.
405413
let builtin_table_update = if !config.skip_migrations {
406414
let migrate_result = migrate::migrate(

src/adapter/src/catalog/state.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ pub struct CatalogState {
142142
#[serde(serialize_with = "mz_ore::serde::map_key_to_string")]
143143
pub(super) source_references: BTreeMap<CatalogItemId, SourceReferences>,
144144
pub(super) storage_metadata: StorageMetadata,
145+
pub(super) mock_authentication_nonce: Option<String>,
145146

146147
// Mutable state not derived from the durable catalog.
147148
#[serde(skip)]
@@ -316,6 +317,7 @@ impl CatalogState {
316317
source_references: Default::default(),
317318
storage_metadata: Default::default(),
318319
license_key: ValidatedLicenseKey::for_tests(),
320+
mock_authentication_nonce: Default::default(),
319321
}
320322
}
321323

@@ -2635,6 +2637,10 @@ impl CatalogState {
26352637
CommentObjectId::NetworkPolicy(id) => self.get_network_policy(&id).name.clone(),
26362638
}
26372639
}
2640+
2641+
pub fn mock_authentication_nonce(&self) -> String {
2642+
self.mock_authentication_nonce.clone().unwrap_or_default()
2643+
}
26382644
}
26392645

26402646
impl ConnectionResolver for CatalogState {

src/adapter/src/client.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ use uuid::Uuid;
5050
use crate::catalog::Catalog;
5151
use crate::command::{
5252
AuthResponse, CatalogDump, CatalogSnapshot, Command, ExecuteResponse, Response,
53+
SASLChallengeResponse, SASLVerifyProofResponse,
5354
};
5455
use crate::coord::{Coordinator, ExecuteContextExtra};
5556
use crate::error::AdapterError;
@@ -168,6 +169,40 @@ impl Client {
168169
Ok(response)
169170
}
170171

172+
pub async fn generate_sasl_challenge(
173+
&self,
174+
user: &String,
175+
client_nonce: &String,
176+
) -> Result<SASLChallengeResponse, AdapterError> {
177+
let (tx, rx) = oneshot::channel();
178+
self.send(Command::AuthenticateGetSASLChallenge {
179+
role_name: user.to_string(),
180+
nonce: client_nonce.to_string(),
181+
tx,
182+
});
183+
let response = rx.await.expect("sender dropped")?;
184+
Ok(response)
185+
}
186+
187+
pub async fn verify_sasl_proof(
188+
&self,
189+
user: &String,
190+
proof: &String,
191+
nonce: &String,
192+
mock_hash: &String,
193+
) -> Result<SASLVerifyProofResponse, AdapterError> {
194+
let (tx, rx) = oneshot::channel();
195+
self.send(Command::AuthenticateVerifySASLProof {
196+
role_name: user.to_string(),
197+
proof: proof.to_string(),
198+
auth_message: nonce.to_string(),
199+
mock_hash: mock_hash.to_string(),
200+
tx,
201+
});
202+
let response = rx.await.expect("sender dropped")?;
203+
Ok(response)
204+
}
205+
171206
/// Upgrades this client to a session client.
172207
///
173208
/// A session is a connection that has successfully negotiated parameters,
@@ -927,6 +962,8 @@ impl SessionClient {
927962
Command::GetWebhook { .. } => typ = Some("webhook"),
928963
Command::Startup { .. }
929964
| Command::AuthenticatePassword { .. }
965+
| Command::AuthenticateGetSASLChallenge { .. }
966+
| Command::AuthenticateVerifySASLProof { .. }
930967
| Command::CatalogSnapshot { .. }
931968
| Command::Commit { .. }
932969
| Command::CancelRequest { .. }

src/adapter/src/command.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,20 @@ pub enum Command {
7373
password: Option<Password>,
7474
},
7575

76+
AuthenticateGetSASLChallenge {
77+
tx: oneshot::Sender<Result<SASLChallengeResponse, AdapterError>>,
78+
role_name: String,
79+
nonce: String,
80+
},
81+
82+
AuthenticateVerifySASLProof {
83+
tx: oneshot::Sender<Result<SASLVerifyProofResponse, AdapterError>>,
84+
role_name: String,
85+
proof: String,
86+
auth_message: String,
87+
mock_hash: String,
88+
},
89+
7690
Execute {
7791
portal_name: String,
7892
session: Session,
@@ -148,6 +162,8 @@ impl Command {
148162
Command::CancelRequest { .. }
149163
| Command::Startup { .. }
150164
| Command::AuthenticatePassword { .. }
165+
| Command::AuthenticateGetSASLChallenge { .. }
166+
| Command::AuthenticateVerifySASLProof { .. }
151167
| Command::CatalogSnapshot { .. }
152168
| Command::PrivilegedCancelRequest { .. }
153169
| Command::GetWebhook { .. }
@@ -166,6 +182,8 @@ impl Command {
166182
Command::CancelRequest { .. }
167183
| Command::Startup { .. }
168184
| Command::AuthenticatePassword { .. }
185+
| Command::AuthenticateGetSASLChallenge { .. }
186+
| Command::AuthenticateVerifySASLProof { .. }
169187
| Command::CatalogSnapshot { .. }
170188
| Command::PrivilegedCancelRequest { .. }
171189
| Command::GetWebhook { .. }
@@ -210,6 +228,22 @@ pub struct AuthResponse {
210228
pub superuser: bool,
211229
}
212230

231+
#[derive(Derivative)]
232+
#[derivative(Debug)]
233+
pub struct SASLChallengeResponse {
234+
pub iteration_count: usize,
235+
/// Base64-encoded salt for the SASL challenge.
236+
pub salt: String,
237+
pub nonce: String,
238+
}
239+
240+
#[derive(Derivative)]
241+
#[derivative(Debug)]
242+
pub struct SASLVerifyProofResponse {
243+
pub verifier: String,
244+
pub auth_resp: AuthResponse,
245+
}
246+
213247
// Facile implementation for `StartupResponse`, which does not use the `allowed`
214248
// feature of `ClientTransmitter`.
215249
impl Transmittable for StartupResponse {

src/adapter/src/coord.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,8 @@ impl Message {
356356
Command::CheckConsistency { .. } => "command-check_consistency",
357357
Command::Dump { .. } => "command-dump",
358358
Command::AuthenticatePassword { .. } => "command-auth_check",
359+
Command::AuthenticateGetSASLChallenge { .. } => "command-auth_get_sasl_challenge",
360+
Command::AuthenticateVerifySASLProof { .. } => "command-auth_verify_sasl_proof",
359361
},
360362
Message::ControllerReady {
361363
controller: ControllerReadiness::Compute,

0 commit comments

Comments
 (0)