Skip to content

Commit 2ab75ef

Browse files
committed
blop
1 parent 94b575a commit 2ab75ef

File tree

23 files changed

+304
-45
lines changed

23 files changed

+304
-45
lines changed

config/quickwit.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,9 @@ indexer:
150150

151151
jaeger:
152152
enable_endpoint: ${QW_ENABLE_JAEGER_ENDPOINT:-true}
153+
154+
license: ${QW_LICENSE}
155+
156+
# authorization:
157+
# root_key: ${QW_ROOT_KEY}
158+
# node_token: ${QW_NODE_TOKEN}

quickwit/Cargo.lock

+36
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

quickwit/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ tikv-jemalloc-ctl = "0.5"
240240
tikv-jemallocator = "0.5"
241241
time = { version = "0.3", features = ["std", "formatting", "macros"] }
242242
tokio = { version = "1.40", features = ["full"] }
243+
tokio-inherit-task-local = "0.2"
243244
tokio-metrics = { version = "0.3.1", features = ["rt"] }
244245
tokio-stream = { version = "0.1", features = ["sync"] }
245246
tokio-util = { version = "0.7", features = ["full"] }

quickwit/quickwit-authorize/Cargo.toml

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ authors.workspace = true
99
license.workspace = true
1010

1111
[dependencies]
12+
anyhow = { workspace = true, optional = true }
1213
tower = { workspace = true}
1314
biscuit-auth = { workspace = true, optional=true }
1415
futures = { workspace = true }
1516
http = { workspace = true }
17+
tokio-inherit-task-local = { workspace = true }
1618
serde = { workspace = true }
1719
thiserror = { workspace = true }
1820
tonic = { workspace = true }
@@ -23,4 +25,4 @@ pin-project = { workspace = true }
2325
quickwit-common = { workspace = true }
2426

2527
[features]
26-
enterprise = ["biscuit-auth"]
28+
enterprise = ["dep:biscuit-auth", "dep:anyhow"]

quickwit/quickwit-authorize/src/authorization_layer.rs renamed to quickwit/quickwit-authorize/src/enterprise/authorization_layer.rs

+20
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
// Copyright (C) 2024 Quickwit, Inc.
2+
//
3+
// Quickwit is offered under the AGPL v3.0 and as commercial software.
4+
// For commercial licensing, contact us at [email protected].
5+
//
6+
// AGPL:
7+
// This program is free software: you can redistribute it and/or modify
8+
// it under the terms of the GNU Affero General Public License as
9+
// published by the Free Software Foundation, either version 3 of the
10+
// License, or (at your option) any later version.
11+
//
12+
// This program is distributed in the hope that it will be useful,
13+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
// GNU Affero General Public License for more details.
16+
//
17+
// You should have received a copy of the GNU Affero General Public License
18+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
120
use std::fmt;
221
use std::task::{Context, Poll};
322

@@ -7,6 +26,7 @@ use tower::{Layer, Service};
726

827
use crate::AuthorizationError;
928

29+
#[derive(Clone, Copy, Debug)]
1030
pub struct AuthorizationLayer;
1131

1232
impl<S: Clone> Layer<S> for AuthorizationLayer {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright (C) 2024 Quickwit, Inc.
2+
//
3+
// Quickwit is offered under the AGPL v3.0 and as commercial software.
4+
// For commercial licensing, contact us at [email protected].
5+
//
6+
// AGPL:
7+
// This program is free software: you can redistribute it and/or modify
8+
// it under the terms of the GNU Affero General Public License as
9+
// published by the Free Software Foundation, either version 3 of the
10+
// License, or (at your option) any later version.
11+
//
12+
// This program is distributed in the hope that it will be useful,
13+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
// GNU Affero General Public License for more details.
16+
//
17+
// You should have received a copy of the GNU Affero General Public License
18+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
20+
use std::task::{Context, Poll};
21+
22+
use futures::future::Either;
23+
use http::Request;
24+
use tokio::task::futures::TaskLocalFuture;
25+
use tokio_inherit_task_local::TaskLocalInheritableTable;
26+
use tower::{Layer, Service};
27+
use tracing::debug;
28+
29+
use super::AuthorizationToken;
30+
31+
#[derive(Clone, Copy, Debug)]
32+
pub struct AuthorizationTokenExtractionLayer;
33+
34+
impl<S: Clone> Layer<S> for AuthorizationTokenExtractionLayer {
35+
type Service = AuthorizationTokenExtractionService<S>;
36+
37+
fn layer(&self, service: S) -> Self::Service {
38+
AuthorizationTokenExtractionService { service }
39+
}
40+
}
41+
42+
#[derive(Clone)]
43+
pub struct AuthorizationTokenExtractionService<S> {
44+
service: S,
45+
}
46+
47+
fn get_authorization_token_opt(headers: &http::HeaderMap) -> Option<AuthorizationToken> {
48+
let authorization_header_value = headers.get("Authorization")?;
49+
let authorization_header_str = authorization_header_value.to_str().ok()?;
50+
crate::get_auth_token_from_str(authorization_header_str).ok()
51+
}
52+
53+
impl<B, S> Service<Request<B>> for AuthorizationTokenExtractionService<S>
54+
where S: Service<Request<B>>
55+
{
56+
type Response = S::Response;
57+
type Error = S::Error;
58+
type Future = Either<S::Future, TaskLocalFuture<TaskLocalInheritableTable, S::Future>>;
59+
60+
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
61+
self.service.poll_ready(cx)
62+
}
63+
64+
fn call(&mut self, request: Request<B>) -> Self::Future {
65+
let authorization_token_opt = get_authorization_token_opt(request.headers());
66+
debug!(authorization_token_opt = ?authorization_token_opt, "Authorization token extracted");
67+
let fut = self.service.call(request);
68+
if let Some(authorization_token) = authorization_token_opt {
69+
Either::Right(crate::execute_with_authorization(authorization_token, fut))
70+
} else {
71+
Either::Left(fut)
72+
}
73+
}
74+
}

quickwit/quickwit-authorize/src/enterprise.rs renamed to quickwit/quickwit-authorize/src/enterprise/mod.rs

+48-13
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,31 @@
1919
// components are licensed under the original license provided by the owner of the
2020
// applicable component.
2121

22+
mod authorization_layer;
23+
mod authorization_token_extraction_layer;
24+
2225
use std::future::Future;
2326
use std::str::FromStr;
2427
use std::sync::{Arc, OnceLock};
2528

29+
use anyhow::Context;
30+
pub use authorization_layer::AuthorizationLayer;
31+
pub use authorization_token_extraction_layer::AuthorizationTokenExtractionLayer;
2632
use biscuit_auth::macros::authorizer;
2733
use biscuit_auth::{Authorizer, Biscuit, RootKeyProvider};
34+
use tokio::task::futures::TaskLocalFuture;
35+
use tokio_inherit_task_local::TaskLocalInheritableTable;
36+
use tracing::info;
2837

2938
use crate::AuthorizationError;
3039

40+
tokio_inherit_task_local::inheritable_task_local! {
41+
pub static AUTHORIZATION_TOKEN: AuthorizationToken;
42+
}
43+
44+
static ROOT_KEY_PROVIDER: OnceLock<Arc<dyn RootKeyProvider + Sync + Send>> = OnceLock::new();
45+
static NODE_TOKEN: OnceLock<Arc<AuthorizationToken>> = OnceLock::new();
46+
3147
pub struct AuthorizationToken(Biscuit);
3248

3349
impl AuthorizationToken {
@@ -54,7 +70,22 @@ impl std::fmt::Debug for AuthorizationToken {
5470
}
5571
}
5672

57-
static ROOT_KEY_PROVIDER: OnceLock<Arc<dyn RootKeyProvider + Sync + Send>> = OnceLock::new();
73+
pub fn set_node_token_hex(node_token_hex: &str) -> anyhow::Result<()> {
74+
let node_token =
75+
AuthorizationToken::from_str(node_token_hex).context("failed to set node token")?;
76+
if NODE_TOKEN.set(Arc::new(node_token)).is_err() {
77+
tracing::error!("node token was already initialized");
78+
}
79+
Ok(())
80+
}
81+
82+
pub fn set_root_public_key(root_key_hex: &str) -> anyhow::Result<()> {
83+
let public_key = biscuit_auth::PublicKey::from_bytes_hex(root_key_hex)
84+
.context("failed to parse root public key")?;
85+
let key_provider: Arc<dyn RootKeyProvider + Sync + Send> = Arc::new(public_key);
86+
set_root_key_provider(key_provider);
87+
Ok(())
88+
}
5889

5990
pub fn set_root_key_provider(key_provider: Arc<dyn RootKeyProvider + Sync + Send>) {
6091
if ROOT_KEY_PROVIDER.set(key_provider).is_err() {
@@ -79,10 +110,6 @@ impl FromStr for AuthorizationToken {
79110
}
80111
}
81112

82-
tokio::task_local! {
83-
pub static AUTHORIZATION_TOKEN: AuthorizationToken;
84-
}
85-
86113
const AUTHORIZATION_VALUE_PREFIX: &str = "Bearer ";
87114

88115
fn default_operation_authorizer<T: ?Sized>(
@@ -146,6 +173,16 @@ impl From<biscuit_auth::error::Token> for AuthorizationError {
146173
}
147174
}
148175

176+
pub fn get_auth_token_from_str(
177+
authorization_header_value: &str,
178+
) -> Result<AuthorizationToken, AuthorizationError> {
179+
let authorization_token_str: &str = authorization_header_value
180+
.strip_prefix(AUTHORIZATION_VALUE_PREFIX)
181+
.ok_or(AuthorizationError::InvalidToken)?;
182+
let biscuit: Biscuit = Biscuit::from_base64(authorization_token_str, get_root_key_provider())?;
183+
Ok(AuthorizationToken(biscuit))
184+
}
185+
149186
pub fn get_auth_token(
150187
req_metadata: &tonic::metadata::MetadataMap,
151188
) -> Result<AuthorizationToken, AuthorizationError> {
@@ -154,11 +191,7 @@ pub fn get_auth_token(
154191
.ok_or(AuthorizationError::AuthorizationTokenMissing)?
155192
.to_str()
156193
.map_err(|_| AuthorizationError::InvalidToken)?;
157-
let authorization_token_str: &str = authorization_header_value
158-
.strip_prefix(AUTHORIZATION_VALUE_PREFIX)
159-
.ok_or(AuthorizationError::InvalidToken)?;
160-
let biscuit: Biscuit = Biscuit::from_base64(authorization_token_str, get_root_key_provider())?;
161-
Ok(AuthorizationToken(biscuit))
194+
get_auth_token_from_str(authorization_header_value)
162195
}
163196

164197
pub fn set_auth_token(
@@ -216,15 +249,17 @@ pub fn authorize_stream<R: StreamAuthorization>(
216249
}
217250

218251
pub fn authorize_request<R: Authorization>(req: &R) -> Result<(), AuthorizationError> {
219-
AUTHORIZATION_TOKEN
252+
let res = AUTHORIZATION_TOKEN
220253
.try_with(|auth_token| authorize(req, auth_token))
221-
.unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing))
254+
.unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing));
255+
info!("request authorization");
256+
res
222257
}
223258

224259
pub fn execute_with_authorization<F, O>(
225260
token: AuthorizationToken,
226261
f: F,
227-
) -> impl Future<Output = O>
262+
) -> TaskLocalFuture<TaskLocalInheritableTable, F>
228263
where
229264
F: Future<Output = O>,
230265
{

quickwit/quickwit-authorize/src/lib.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,12 @@
1717
// You should have received a copy of the GNU Affero General Public License
1818
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1919

20-
mod authorization_layer;
21-
2220
#[cfg(not(feature = "enterprise"))]
23-
#[path = "community.rs"]
21+
#[path = "community/mod.rs"]
2422
mod implementation;
2523

2624
#[cfg(feature = "enterprise")]
27-
#[path = "enterprise.rs"]
25+
#[path = "enterprise/mod.rs"]
2826
mod implementation;
2927

3028
pub use implementation::*;

quickwit/quickwit-cli/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ quickwit-metastore = { workspace = true, features = ["testsuite"] }
7979
quickwit-storage = { workspace = true, features = ["testsuite"] }
8080

8181
[features]
82-
enterprise = ["quickwit-config/enterprise", "quickwit-ingest/enterprise", "quickwit-proto/enterprise"]
82+
enterprise = ["quickwit-config/enterprise", "quickwit-ingest/enterprise", "quickwit-proto/enterprise", "quickwit-serve/enterprise"]
8383
jemalloc = ["dep:tikv-jemalloc-ctl", "dep:tikv-jemallocator"]
8484
ci-test = []
8585
pprof = ["quickwit-serve/pprof"]

0 commit comments

Comments
 (0)