Skip to content

Commit afa80fb

Browse files
committed
added check
1 parent 5a924f6 commit afa80fb

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

+1052
-715
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: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
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+
Ok(auth_token)
120+
}
121+
122+
fn authorizer(
123+
&self,
124+
auth_token: &AuthorizationToken,
125+
) -> Result<Authorizer, AuthorizationError> {
126+
default_operation_authorizer::<Self>(auth_token)
127+
}
128+
}
129+
130+
pub trait StreamAuthorization {
131+
fn attenuate(
132+
auth_token: AuthorizationToken,
133+
) -> std::result::Result<AuthorizationToken, AuthorizationError> {
134+
Ok(auth_token)
135+
}
136+
fn authorizer(
137+
auth_token: &AuthorizationToken,
138+
) -> std::result::Result<Authorizer, AuthorizationError> {
139+
default_operation_authorizer::<Self>(&auth_token)
140+
}
141+
}
142+
143+
impl From<biscuit_auth::error::Token> for AuthorizationError {
144+
fn from(_token_error: biscuit_auth::error::Token) -> AuthorizationError {
145+
AuthorizationError::InvalidToken
146+
}
147+
}
148+
149+
pub fn get_auth_token(
150+
req_metadata: &tonic::metadata::MetadataMap,
151+
) -> Result<AuthorizationToken, AuthorizationError> {
152+
let authorization_header_value: &str = req_metadata
153+
.get(http::header::AUTHORIZATION.as_str())
154+
.ok_or(AuthorizationError::AuthorizationTokenMissing)?
155+
.to_str()
156+
.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))
162+
}
163+
164+
pub fn set_auth_token(
165+
auth_token: &AuthorizationToken,
166+
req_metadata: &mut tonic::metadata::MetadataMap,
167+
) {
168+
let authorization_header_value = format!("{AUTHORIZATION_VALUE_PREFIX}{auth_token}");
169+
req_metadata.insert(
170+
http::header::AUTHORIZATION.as_str(),
171+
authorization_header_value.parse().unwrap(),
172+
);
173+
}
174+
175+
pub fn authorize<R: Authorization>(
176+
req: &R,
177+
auth_token: &AuthorizationToken,
178+
) -> Result<(), AuthorizationError> {
179+
let mut authorizer = req.authorizer(auth_token)?;
180+
authorizer.add_token(&auth_token.0)?;
181+
authorizer.authorize()?;
182+
Ok(())
183+
}
184+
185+
pub fn build_tonic_stream_request_with_auth_token<R>(
186+
req: R,
187+
) -> Result<tonic::Request<R>, AuthorizationError> {
188+
AUTHORIZATION_TOKEN
189+
.try_with(|token| {
190+
let mut request = tonic::Request::new(req);
191+
set_auth_token(token, request.metadata_mut());
192+
Ok(request)
193+
})
194+
.unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing))
195+
}
196+
197+
pub fn build_tonic_request_with_auth_token<R: Authorization>(
198+
req: R,
199+
) -> Result<tonic::Request<R>, AuthorizationError> {
200+
AUTHORIZATION_TOKEN
201+
.try_with(|token| {
202+
let mut request = tonic::Request::new(req);
203+
set_auth_token(token, request.metadata_mut());
204+
Ok(request)
205+
})
206+
.unwrap_or(Err(AuthorizationError::AuthorizationTokenMissing))
207+
}
208+
209+
pub fn authorize_stream<R: StreamAuthorization>(
210+
auth_token: &AuthorizationToken,
211+
) -> Result<(), AuthorizationError> {
212+
let mut authorizer = R::authorizer(auth_token)?;
213+
authorizer.add_token(&auth_token.0)?;
214+
authorizer.authorize()?;
215+
Ok(())
216+
}
217+
218+
pub fn execute_with_authorization<F, O>(
219+
token: AuthorizationToken,
220+
f: F,
221+
) -> impl Future<Output = O>
222+
where
223+
F: Future<Output = O>,
224+
{
225+
AUTHORIZATION_TOKEN.scope(token, f)
226+
}
227+
228+
#[cfg(test)]
229+
mod tests {
230+
use super::*;
231+
232+
// #[test]
233+
// fn test_auth_token() {
234+
// let mut req_metadata = tonic::metadata::MetadataMap::new();
235+
// let token =
236+
// let auth_token = "test_token".to_string();
237+
// set_auth_token(&auth_token, &mut req_metadata);
238+
// let auth_token_retrieved = get_auth_token(&req_metadata).unwrap();
239+
// assert_eq!(auth_token_retrieved, auth_token);
240+
// }
241+
242+
#[test]
243+
fn test_auth_token_missing() {
244+
let req_metadata = tonic::metadata::MetadataMap::new();
245+
let missing_error = get_auth_token(&req_metadata).unwrap_err();
246+
assert!(matches!(
247+
missing_error,
248+
AuthorizationError::AuthorizationTokenMissing
249+
));
250+
}
251+
252+
#[test]
253+
fn test_auth_token_invalid() {
254+
let mut req_metadata = tonic::metadata::MetadataMap::new();
255+
req_metadata.insert(
256+
http::header::AUTHORIZATION.as_str(),
257+
"some_token".parse().unwrap(),
258+
);
259+
let missing_error = get_auth_token(&req_metadata).unwrap_err();
260+
assert!(matches!(missing_error, AuthorizationError::InvalidToken));
261+
}
262+
}

0 commit comments

Comments
 (0)