Skip to content

Commit 002d98b

Browse files
committed
added check
1 parent 5a924f6 commit 002d98b

File tree

46 files changed

+1177
-538
lines changed

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

+1177
-538
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: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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::future::Future;
21+
22+
use crate::AuthorizationError;
23+
24+
pub type AuthorizationToken = ();
25+
26+
pub trait Authorization {
27+
fn attenuate(
28+
&self,
29+
_auth_token: AuthorizationToken,
30+
) -> Result<AuthorizationToken, AuthorizationError> {
31+
Ok(())
32+
}
33+
}
34+
35+
impl<T> Authorization for T {}
36+
37+
pub trait StreamAuthorization {
38+
fn attenuate(
39+
_auth_token: AuthorizationToken,
40+
) -> std::result::Result<AuthorizationToken, AuthorizationError> {
41+
Ok(())
42+
}
43+
}
44+
45+
impl<T> StreamAuthorization for T {}
46+
47+
pub fn get_auth_token(
48+
_req_metadata: &tonic::metadata::MetadataMap,
49+
) -> Result<AuthorizationToken, AuthorizationError> {
50+
Ok(())
51+
}
52+
53+
pub fn set_auth_token(
54+
_auth_token: &AuthorizationToken,
55+
_req_metadata: &mut tonic::metadata::MetadataMap,
56+
) {
57+
}
58+
59+
pub fn authorize<R: Authorization>(
60+
_req: &R,
61+
_auth_token: &AuthorizationToken,
62+
) -> Result<(), AuthorizationError> {
63+
Ok(())
64+
}
65+
66+
pub fn build_tonic_stream_request_with_auth_token<R>(
67+
req: R,
68+
) -> Result<tonic::Request<R>, AuthorizationError> {
69+
Ok(tonic::Request::new(req))
70+
}
71+
72+
pub fn build_tonic_request_with_auth_token<R: Authorization>(
73+
req: R,
74+
) -> Result<tonic::Request<R>, AuthorizationError> {
75+
Ok(tonic::Request::new(req))
76+
}
77+
78+
pub fn authorize_stream<R: StreamAuthorization>(
79+
_auth_token: &AuthorizationToken,
80+
) -> Result<(), AuthorizationError> {
81+
Ok(())
82+
}
83+
84+
pub fn execute_with_authorization<F, O>(_: AuthorizationToken, f: F) -> impl Future<Output = O>
85+
where F: Future<Output = O> {
86+
f
87+
}
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
// The Quickwit Enterprise Edition (EE) license
2+
// Copyright (c) 2024-present Quickwit Inc.
3+
//
4+
// With regard to the Quickwit Software:
5+
//
6+
// This software and associated documentation files (the "Software") may only be
7+
// used in production, if you (and any entity that you represent) hold a valid
8+
// Quickwit Enterprise license corresponding to your usage.
9+
//
10+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
13+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
14+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
15+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
16+
// SOFTWARE.
17+
//
18+
// For all third party components incorporated into the Quickwit Software, those
19+
// components are licensed under the original license provided by the owner of the
20+
// applicable component.
21+
22+
use std::future::Future;
23+
use std::str::FromStr;
24+
use std::sync::{Arc, OnceLock};
25+
26+
use biscuit_auth::macros::authorizer;
27+
use biscuit_auth::{Authorizer, Biscuit, RootKeyProvider};
28+
29+
use crate::AuthorizationError;
30+
31+
pub struct AuthorizationToken(Biscuit);
32+
33+
impl AuthorizationToken {
34+
pub fn into_biscuit(self) -> Biscuit {
35+
self.0
36+
}
37+
}
38+
39+
impl From<Biscuit> for AuthorizationToken {
40+
fn from(biscuit: Biscuit) -> Self {
41+
AuthorizationToken(biscuit)
42+
}
43+
}
44+
45+
impl std::fmt::Display for AuthorizationToken {
46+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
47+
self.0.fmt(f)
48+
}
49+
}
50+
51+
impl std::fmt::Debug for AuthorizationToken {
52+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
53+
write!(f, "AuthorizationToken({})", &self.0)
54+
}
55+
}
56+
57+
static ROOT_KEY_PROVIDER: OnceLock<Arc<dyn RootKeyProvider + Sync + Send>> = OnceLock::new();
58+
59+
pub fn set_root_key_provider(key_provider: Arc<dyn RootKeyProvider + Sync + Send>) {
60+
if ROOT_KEY_PROVIDER.set(key_provider).is_err() {
61+
tracing::error!("root key provider was already initialized");
62+
}
63+
}
64+
65+
fn get_root_key_provider() -> Arc<dyn RootKeyProvider> {
66+
ROOT_KEY_PROVIDER
67+
.get()
68+
.expect("root key provider should have been initialized beforehand")
69+
.clone()
70+
}
71+
72+
impl FromStr for AuthorizationToken {
73+
type Err = AuthorizationError;
74+
75+
fn from_str(token_base64: &str) -> Result<Self, AuthorizationError> {
76+
let root_key_provider = get_root_key_provider();
77+
let biscuit = Biscuit::from_base64(token_base64, root_key_provider)?;
78+
Ok(AuthorizationToken(biscuit))
79+
}
80+
}
81+
82+
tokio::task_local! {
83+
pub static AUTHORIZATION_TOKEN: AuthorizationToken;
84+
}
85+
86+
const AUTHORIZATION_VALUE_PREFIX: &str = "Bearer ";
87+
88+
fn default_operation_authorizer<T: ?Sized>(
89+
auth_token: &AuthorizationToken,
90+
) -> Result<Authorizer, AuthorizationError> {
91+
let request_type = std::any::type_name::<T>();
92+
let operation: &str = request_type.strip_suffix("Request").unwrap();
93+
let mut authorizer: Authorizer = authorizer!(
94+
r#"
95+
operation({operation});
96+
97+
// We generate the actual user role, by doing an union of the rights granted via roles.
98+
user_right($operation) <- role($role), right($role, $operation);
99+
user_right($operation, $resource) <- role($role), right($role, $operation, $resource);
100+
user_right($operation) <- role("root"), operation($operation);
101+
user_right($operation, $resource) <- role("root"), operation($operation), resource($resource);
102+
103+
// Finally we check that we have access to index1 and index2.
104+
check all operation($operation), right($operation);
105+
106+
allow if true;
107+
"#
108+
);
109+
authorizer.set_time();
110+
authorizer.add_token(&auth_token.0)?;
111+
Ok(authorizer)
112+
}
113+
114+
pub trait Authorization {
115+
fn attenuate(
116+
&self,
117+
auth_token: AuthorizationToken,
118+
) -> Result<AuthorizationToken, AuthorizationError>;
119+
fn authorizer(
120+
&self,
121+
auth_token: &AuthorizationToken,
122+
) -> Result<Authorizer, AuthorizationError> {
123+
default_operation_authorizer::<Self>(auth_token)
124+
}
125+
}
126+
127+
pub trait StreamAuthorization {
128+
fn attenuate(
129+
auth_token: AuthorizationToken,
130+
) -> std::result::Result<AuthorizationToken, AuthorizationError> {
131+
Ok(auth_token)
132+
}
133+
fn authorizer(
134+
auth_token: &AuthorizationToken,
135+
) -> std::result::Result<Authorizer, AuthorizationError> {
136+
default_operation_authorizer::<Self>(&auth_token)
137+
}
138+
}
139+
140+
impl From<biscuit_auth::error::Token> for AuthorizationError {
141+
fn from(_token_error: biscuit_auth::error::Token) -> AuthorizationError {
142+
AuthorizationError::InvalidToken
143+
}
144+
}
145+
146+
pub fn get_auth_token(
147+
req_metadata: &tonic::metadata::MetadataMap,
148+
) -> Result<AuthorizationToken, AuthorizationError> {
149+
let authorization_header_value: &str = req_metadata
150+
.get(http::header::AUTHORIZATION.as_str())
151+
.ok_or(AuthorizationError::AuthorizationTokenMissing)?
152+
.to_str()
153+
.map_err(|_| AuthorizationError::InvalidToken)?;
154+
let authorization_token_str: &str = authorization_header_value
155+
.strip_prefix(AUTHORIZATION_VALUE_PREFIX)
156+
.ok_or(AuthorizationError::InvalidToken)?;
157+
let biscuit: Biscuit = Biscuit::from_base64(authorization_token_str, get_root_key_provider())?;
158+
Ok(AuthorizationToken(biscuit))
159+
}
160+
161+
pub fn set_auth_token(
162+
auth_token: &AuthorizationToken,
163+
req_metadata: &mut tonic::metadata::MetadataMap,
164+
) {
165+
let authorization_header_value = format!("{AUTHORIZATION_VALUE_PREFIX}{auth_token}");
166+
req_metadata.insert(
167+
http::header::AUTHORIZATION.as_str(),
168+
authorization_header_value.parse().unwrap(),
169+
);
170+
}
171+
172+
pub fn authorize<R: Authorization>(
173+
req: &R,
174+
auth_token: &AuthorizationToken,
175+
) -> Result<(), AuthorizationError> {
176+
let mut authorizer = req.authorizer(auth_token)?;
177+
authorizer.add_token(&auth_token.0)?;
178+
authorizer.authorize()?;
179+
Ok(())
180+
}
181+
182+
pub fn build_tonic_stream_request_with_auth_token<R>(
183+
req: R,
184+
) -> Result<tonic::Request<R>, AuthorizationError> {
185+
AUTHORIZATION_TOKEN
186+
.try_with(|token| {
187+
let mut request = tonic::Request::new(req);
188+
set_auth_token(token, request.metadata_mut());
189+
Ok(request)
190+
})
191+
.unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing))
192+
}
193+
194+
pub fn build_tonic_request_with_auth_token<R: Authorization>(
195+
req: R,
196+
) -> Result<tonic::Request<R>, AuthorizationError> {
197+
AUTHORIZATION_TOKEN
198+
.try_with(|token| {
199+
let mut request = tonic::Request::new(req);
200+
set_auth_token(token, request.metadata_mut());
201+
Ok(request)
202+
})
203+
.unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing))
204+
}
205+
206+
pub fn authorize_stream<R: StreamAuthorization>(
207+
auth_token: &AuthorizationToken,
208+
) -> Result<(), AuthorizationError> {
209+
let mut authorizer = R::authorizer(auth_token)?;
210+
authorizer.add_token(&auth_token.0)?;
211+
authorizer.authorize()?;
212+
Ok(())
213+
}
214+
215+
pub fn execute_with_authorization<F, O>(
216+
token: AuthorizationToken,
217+
f: F,
218+
) -> impl Future<Output = O>
219+
where
220+
F: Future<Output = O>,
221+
{
222+
AUTHORIZATION_TOKEN.scope(token, f)
223+
}
224+
225+
#[cfg(test)]
226+
mod tests {
227+
use super::*;
228+
229+
// #[test]
230+
// fn test_auth_token() {
231+
// let mut req_metadata = tonic::metadata::MetadataMap::new();
232+
// let token =
233+
// let auth_token = "test_token".to_string();
234+
// set_auth_token(&auth_token, &mut req_metadata);
235+
// let auth_token_retrieved = get_auth_token(&req_metadata).unwrap();
236+
// assert_eq!(auth_token_retrieved, auth_token);
237+
// }
238+
239+
#[test]
240+
fn test_auth_token_missing() {
241+
let req_metadata = tonic::metadata::MetadataMap::new();
242+
let missing_error = get_auth_token(&req_metadata).unwrap_err();
243+
assert!(matches!(
244+
missing_error,
245+
AuthorizationError::AuthorizationTokenMissing
246+
));
247+
}
248+
249+
#[test]
250+
fn test_auth_token_invalid() {
251+
let mut req_metadata = tonic::metadata::MetadataMap::new();
252+
req_metadata.insert(
253+
http::header::AUTHORIZATION.as_str(),
254+
"some_token".parse().unwrap(),
255+
);
256+
let missing_error = get_auth_token(&req_metadata).unwrap_err();
257+
assert!(matches!(missing_error, AuthorizationError::InvalidToken));
258+
}
259+
}

0 commit comments

Comments
 (0)