Skip to content

Commit 447113a

Browse files
committed
added check
1 parent 5a924f6 commit 447113a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1018
-308
lines changed

quickwit/Cargo.lock

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

quickwit/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ members = [
3636
"quickwit-serve",
3737
"quickwit-storage",
3838
"quickwit-telemetry",
39-
"quickwit-telemetry",
4039
]
4140

4241
# The following list excludes `quickwit-metastore-utils` and `quickwit-lambda`

quickwit/quickwit-auth/Cargo.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[package]
2+
name = "quickwit-auth"
3+
version.workspace = true
4+
edition.workspace = true
5+
homepage.workspace = true
6+
documentation.workspace = true
7+
repository.workspace = true
8+
authors.workspace = true
9+
license.workspace = true
10+
11+
[dependencies]
12+
biscuit-auth = { workspace = true, optional=true }
13+
http = { workspace = true }
14+
serde = { workspace = true }
15+
thiserror = { workspace = true }
16+
tonic = { workspace = true }
17+
tokio = { workspace = true }
18+
tracing = { workspace = true }
19+
20+
[features]
21+
enterprise = ["biscuit-auth"]
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use std::future::Future;
2+
3+
use crate::AuthorizationError;
4+
5+
pub type AuthorizationToken = ();
6+
7+
pub trait Authorization {
8+
fn attenuate(
9+
&self,
10+
_auth_token: AuthorizationToken,
11+
) -> Result<AuthorizationToken, AuthorizationError> {
12+
Ok(())
13+
}
14+
}
15+
16+
impl<T> Authorization for T {}
17+
18+
pub trait StreamAuthorization {
19+
fn attenuate(
20+
_auth_token: AuthorizationToken,
21+
) -> std::result::Result<AuthorizationToken, AuthorizationError> {
22+
Ok(())
23+
}
24+
}
25+
26+
impl<T> StreamAuthorization for T {}
27+
28+
29+
pub fn get_auth_token(
30+
_req_metadata: &tonic::metadata::MetadataMap,
31+
) -> Result<AuthorizationToken, AuthorizationError> {
32+
Ok(())
33+
}
34+
35+
pub fn set_auth_token(
36+
_auth_token: &AuthorizationToken,
37+
_req_metadata: &mut tonic::metadata::MetadataMap,
38+
) {}
39+
40+
pub fn authorize<R: Authorization>(
41+
_req: &R,
42+
_auth_token: &AuthorizationToken,
43+
) -> Result<(), AuthorizationError> {
44+
Ok(())
45+
}
46+
47+
pub fn build_tonic_stream_request_with_auth_token<R>(
48+
req: R,
49+
) -> Result<tonic::Request<R>, AuthorizationError> {
50+
Ok(tonic::Request::new(req))
51+
}
52+
53+
pub fn build_tonic_request_with_auth_token<R: Authorization>(
54+
req: R,
55+
) -> Result<tonic::Request<R>, AuthorizationError> {
56+
Ok(tonic::Request::new(req))
57+
}
58+
59+
pub fn authorize_stream<R: StreamAuthorization>(
60+
_auth_token: &AuthorizationToken,
61+
) -> Result<(), AuthorizationError> {
62+
Ok(())
63+
}
64+
65+
pub fn execute_with_authorization<F, O>(_: AuthorizationToken, f: F) -> impl Future<Output=O> where F: Future<Output=O> {
66+
f
67+
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
use std::future::Future;
2+
use std::sync::{Arc, OnceLock};
3+
use crate::AuthorizationError;
4+
5+
use biscuit_auth::macros::authorizer;
6+
use biscuit_auth::{Authorizer, Biscuit, RootKeyProvider};
7+
8+
pub type AuthorizationToken = Biscuit;
9+
10+
tokio::task_local! {
11+
pub static AUTHORIZATION_TOKEN: AuthorizationToken;
12+
}
13+
14+
const AUTHORIZATION_VALUE_PREFIX: &str = "Bearer ";
15+
16+
fn default_operation_authorizer<T: ?Sized>(
17+
auth_token: &AuthorizationToken,
18+
) -> Result<Authorizer, AuthorizationError> {
19+
let request_type = std::any::type_name::<T>();
20+
let operation: &str = request_type.strip_suffix("Request").unwrap();
21+
let mut authorizer: Authorizer = authorizer!(
22+
r#"
23+
operation({operation});
24+
25+
// We generate the actual user role, by doing an union of the rights granted via roles.
26+
user_right($operation) <- role($role), right($role, $operation);
27+
user_right($operation, $resource) <- role($role), right($role, $operation, $resource);
28+
user_right($operation) <- role("root"), operation($operation);
29+
user_right($operation, $resource) <- role("root"), operation($operation), resource($resource);
30+
31+
// Finally we check that we have access to index1 and index2.
32+
check all operation($operation), right($operation);
33+
34+
allow if true;
35+
"#
36+
);
37+
authorizer.set_time();
38+
authorizer.add_token(auth_token)?;
39+
Ok(authorizer)
40+
}
41+
42+
pub trait Authorization {
43+
fn attenuate(
44+
&self,
45+
auth_token: AuthorizationToken,
46+
) -> Result<AuthorizationToken, AuthorizationError>;
47+
fn authorizer(
48+
&self,
49+
auth_token: &AuthorizationToken,
50+
) -> Result<Authorizer, AuthorizationError> {
51+
default_operation_authorizer::<Self>(auth_token)
52+
}
53+
}
54+
55+
pub trait StreamAuthorization {
56+
fn attenuate(
57+
auth_token: AuthorizationToken,
58+
) -> std::result::Result<AuthorizationToken, AuthorizationError> {
59+
Ok(auth_token)
60+
}
61+
fn authorizer(
62+
auth_token: &AuthorizationToken,
63+
) -> std::result::Result<Authorizer, AuthorizationError> {
64+
default_operation_authorizer::<Self>(&auth_token)
65+
}
66+
}
67+
68+
static ROOT_KEY_PROVIDER: OnceLock<Arc<dyn RootKeyProvider + Sync + Send>> = OnceLock::new();
69+
70+
pub fn set_root_key_provider(key_provider: Arc<dyn RootKeyProvider + Sync + Send>) {
71+
if ROOT_KEY_PROVIDER.set(key_provider).is_err() {
72+
tracing::error!("root key provider was already initialized");
73+
}
74+
}
75+
76+
fn get_root_key_provider() -> Arc<dyn RootKeyProvider> {
77+
ROOT_KEY_PROVIDER
78+
.get()
79+
.expect("root key provider should have been initialized beforehand")
80+
.clone()
81+
}
82+
83+
impl From<biscuit_auth::error::Token> for AuthorizationError {
84+
fn from(_token_error: biscuit_auth::error::Token) -> AuthorizationError {
85+
AuthorizationError::InvalidToken
86+
}
87+
}
88+
89+
pub fn get_auth_token(
90+
req_metadata: &tonic::metadata::MetadataMap,
91+
) -> Result<AuthorizationToken, AuthorizationError> {
92+
let authorization_header_value: &str = req_metadata
93+
.get(http::header::AUTHORIZATION.as_str())
94+
.ok_or(AuthorizationError::AuthorizationTokenMissing)?
95+
.to_str()
96+
.map_err(|_| AuthorizationError::InvalidToken)?;
97+
let authorization_token_str: &str = authorization_header_value
98+
.strip_prefix(AUTHORIZATION_VALUE_PREFIX)
99+
.ok_or(AuthorizationError::InvalidToken)?;
100+
let biscuit: Biscuit = Biscuit::from_base64(authorization_token_str, get_root_key_provider())?;
101+
Ok(biscuit)
102+
}
103+
104+
pub fn set_auth_token(
105+
auth_token: &AuthorizationToken,
106+
req_metadata: &mut tonic::metadata::MetadataMap,
107+
) {
108+
let authorization_header_value = format!("{AUTHORIZATION_VALUE_PREFIX}{auth_token}");
109+
req_metadata.insert(
110+
http::header::AUTHORIZATION.as_str(),
111+
authorization_header_value.parse().unwrap(),
112+
);
113+
}
114+
115+
pub fn authorize<R: Authorization>(
116+
req: &R,
117+
auth_token: &Biscuit,
118+
) -> Result<(), AuthorizationError> {
119+
let mut authorizer = req.authorizer(auth_token)?;
120+
authorizer.add_token(&auth_token)?;
121+
authorizer.authorize()?;
122+
Ok(())
123+
}
124+
125+
pub fn build_tonic_stream_request_with_auth_token<R>(
126+
req: R,
127+
) -> Result<tonic::Request<R>, AuthorizationError> {
128+
AUTHORIZATION_TOKEN
129+
.try_with(|token| {
130+
let mut request = tonic::Request::new(req);
131+
set_auth_token(token, request.metadata_mut());
132+
Ok(request)
133+
})
134+
.unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing))
135+
}
136+
137+
pub fn build_tonic_request_with_auth_token<R: Authorization>(
138+
req: R,
139+
) -> Result<tonic::Request<R>, AuthorizationError> {
140+
AUTHORIZATION_TOKEN
141+
.try_with(|token| {
142+
let mut request = tonic::Request::new(req);
143+
set_auth_token(token, request.metadata_mut());
144+
Ok(request)
145+
})
146+
.unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing))
147+
}
148+
149+
pub fn authorize_stream<R: StreamAuthorization>(
150+
auth_token: &Biscuit,
151+
) -> Result<(), AuthorizationError> {
152+
let mut authorizer = R::authorizer(auth_token)?;
153+
authorizer.add_token(&auth_token)?;
154+
authorizer.authorize()?;
155+
Ok(())
156+
}
157+
158+
pub fn execute_with_authorization<F, O>(token: AuthorizationToken, f: F) -> impl Future<Output=O> where F: Future<Output=O> {
159+
AUTHORIZATION_TOKEN
160+
.scope(token, f)
161+
}
162+
163+
#[cfg(test)]
164+
mod tests {
165+
use super::*;
166+
167+
#[test]
168+
fn test_auth_token() {
169+
let mut req_metadata = tonic::metadata::MetadataMap::new();
170+
let auth_token = "test_token".to_string();
171+
set_auth_token(&auth_token, &mut req_metadata);
172+
let auth_token_retrieved = get_auth_token(&req_metadata).unwrap();
173+
assert_eq!(auth_token_retrieved, auth_token);
174+
}
175+
176+
#[test]
177+
fn test_auth_token_missing() {
178+
let req_metadata = tonic::metadata::MetadataMap::new();
179+
let missing_error = get_auth_token(&req_metadata).unwrap_err();
180+
assert!(matches!(
181+
missing_error,
182+
AuthorizationError::AuthorizationTokenMissing
183+
));
184+
}
185+
186+
#[test]
187+
fn test_auth_token_invalid() {
188+
let mut req_metadata = tonic::metadata::MetadataMap::new();
189+
req_metadata.insert(
190+
http::header::AUTHORIZATION.as_str(),
191+
"some_token".parse().unwrap(),
192+
);
193+
let missing_error = get_auth_token(&req_metadata).unwrap_err();
194+
assert!(matches!(missing_error, AuthorizationError::InvalidToken));
195+
}
196+
}

quickwit/quickwit-auth/src/lib.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use serde::{Serialize, Deserialize};
2+
3+
#[cfg(not(feature="enterprise"))]
4+
#[path ="community.rs"]
5+
mod implementation;
6+
7+
#[cfg(feature="enterprise")]
8+
#[path ="enterprise.rs"]
9+
mod implementation;
10+
11+
pub use implementation::*;
12+
13+
14+
#[derive(thiserror::Error, Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq)]
15+
pub enum AuthorizationError {
16+
#[error("authorization token missing")]
17+
AuthorizationTokenMissing,
18+
#[error("invalid token")]
19+
InvalidToken,
20+
#[error("permission denied")]
21+
PermissionDenied,
22+
}
23+
24+
impl From<AuthorizationError> for tonic::Status {
25+
fn from(authorization_error: AuthorizationError) -> tonic::Status {
26+
match authorization_error {
27+
AuthorizationError::AuthorizationTokenMissing => {
28+
tonic::Status::unauthenticated("Authorization token missing")
29+
}
30+
AuthorizationError::InvalidToken => {
31+
tonic::Status::unauthenticated("Invalid authorization token")
32+
}
33+
AuthorizationError::PermissionDenied => {
34+
tonic::Status::permission_denied("Permission denied")
35+
}
36+
}
37+
}
38+
}

quickwit/quickwit-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
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"]
82+
enterprise = ["quickwit-config/enterprise", "quickwit-ingest/enterprise", "quickwit-proto/enterprise"]
8383
jemalloc = ["dep:tikv-jemalloc-ctl", "dep:tikv-jemallocator"]
8484
ci-test = []
8585
pprof = ["quickwit-serve/pprof"]

quickwit/quickwit-codegen/example/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ quickwit-codegen = { workspace = true }
4141

4242
[features]
4343
testsuite = ["mockall"]
44+
enterprise = [ "quickwit-auth/enterprise" ]

0 commit comments

Comments
 (0)