mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-05 16:10:43 +00:00
fix: ReBAC issues (#2659)
This commit is contained in:
@@ -200,6 +200,9 @@ pub enum UserRole {
|
||||
#[cfg(feature = "enterprise")]
|
||||
#[serde(rename = "user")]
|
||||
User,
|
||||
#[cfg(feature = "enterprise")]
|
||||
#[serde(rename = "editor")]
|
||||
Editor,
|
||||
}
|
||||
|
||||
impl fmt::Display for UserRole {
|
||||
@@ -211,6 +214,8 @@ impl fmt::Display for UserRole {
|
||||
#[cfg(feature = "enterprise")]
|
||||
UserRole::Viewer => write!(f, "viewer"),
|
||||
#[cfg(feature = "enterprise")]
|
||||
UserRole::Editor => write!(f, "editor"),
|
||||
#[cfg(feature = "enterprise")]
|
||||
UserRole::User => write!(f, "user"),
|
||||
}
|
||||
}
|
||||
@@ -228,6 +233,8 @@ impl FromStr for UserRole {
|
||||
#[cfg(feature = "enterprise")]
|
||||
"viewer" => Ok(UserRole::Viewer),
|
||||
#[cfg(feature = "enterprise")]
|
||||
"editor" => Ok(UserRole::Editor),
|
||||
#[cfg(feature = "enterprise")]
|
||||
"user" => Ok(UserRole::User),
|
||||
#[cfg(feature = "enterprise")]
|
||||
_ => Ok(UserRole::User),
|
||||
|
||||
+88
-18
@@ -123,6 +123,7 @@ pub struct AuthExtractor {
|
||||
pub o2_type: String,
|
||||
pub org_id: String,
|
||||
pub bypass_check: bool,
|
||||
pub parent_id: String,
|
||||
}
|
||||
|
||||
impl FromRequest for AuthExtractor {
|
||||
@@ -141,6 +142,14 @@ impl FromRequest for AuthExtractor {
|
||||
|
||||
use crate::common::utils::http::{get_folder, get_stream_type_from_request};
|
||||
|
||||
let query = web::Query::<HashMap<String, String>>::from_query(req.query_string()).unwrap();
|
||||
let stream_type = match get_stream_type_from_request(&query) {
|
||||
Ok(v) => v,
|
||||
Err(_) => Some(StreamType::Logs),
|
||||
};
|
||||
|
||||
let folder = get_folder(&query);
|
||||
|
||||
let mut method = req.method().to_string();
|
||||
let local_path = req.path().to_string();
|
||||
let path =
|
||||
@@ -162,6 +171,7 @@ impl FromRequest for AuthExtractor {
|
||||
o2_type: format!("stream:{org_id}"),
|
||||
org_id,
|
||||
bypass_check: true,
|
||||
parent_id: folder,
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -191,7 +201,33 @@ impl FromRequest for AuthExtractor {
|
||||
path_columns[url_len - 2]
|
||||
)
|
||||
} else if url_len == 3 {
|
||||
if method.eq("PUT") || method.eq("DELETE") {
|
||||
if path_columns[2].starts_with("alerts") {
|
||||
if method.eq("PUT") || method.eq("DELETE") {
|
||||
format!(
|
||||
"{}:{}",
|
||||
OFGA_MODELS
|
||||
.get(path_columns[1])
|
||||
.map_or(path_columns[1], |model| model.key),
|
||||
path_columns[2]
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{}:{}",
|
||||
OFGA_MODELS
|
||||
.get(path_columns[2])
|
||||
.map_or(path_columns[2], |model| model.key),
|
||||
path_columns[0]
|
||||
)
|
||||
}
|
||||
} else if path_columns[2].starts_with("_values")
|
||||
|| path_columns[2].starts_with("_around")
|
||||
{
|
||||
format!(
|
||||
"{}:{}",
|
||||
OFGA_MODELS.get("streams").unwrap().key,
|
||||
path_columns[1]
|
||||
)
|
||||
} else if method.eq("PUT") || method.eq("DELETE") {
|
||||
format!(
|
||||
"{}:{}",
|
||||
OFGA_MODELS
|
||||
@@ -208,17 +244,45 @@ impl FromRequest for AuthExtractor {
|
||||
path_columns[0]
|
||||
)
|
||||
}
|
||||
} else if url_len == 4 {
|
||||
if method.eq("PUT") || method.eq("DELETE") {
|
||||
format!(
|
||||
"{}:{}",
|
||||
OFGA_MODELS
|
||||
.get(path_columns[2])
|
||||
.map_or(path_columns[2], |model| model.key),
|
||||
path_columns[3]
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{}:{}",
|
||||
OFGA_MODELS
|
||||
.get(path_columns[1])
|
||||
.map_or(path_columns[1], |model| model.key),
|
||||
path_columns[2]
|
||||
)
|
||||
}
|
||||
} else if method.eq("PUT") || method.eq("DELETE") {
|
||||
if path_columns[url_len - 1].eq("delete_fields") {
|
||||
method = "DELETE".to_string();
|
||||
}
|
||||
format!(
|
||||
"{}:{}",
|
||||
OFGA_MODELS
|
||||
.get(path_columns[1])
|
||||
.map_or(path_columns[1], |model| model.key),
|
||||
path_columns[2]
|
||||
)
|
||||
if path_columns[url_len - 1].eq("enable") {
|
||||
format!(
|
||||
"{}:{}",
|
||||
OFGA_MODELS
|
||||
.get(path_columns[2])
|
||||
.map_or(path_columns[2], |model| model.key),
|
||||
path_columns[3]
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{}:{}",
|
||||
OFGA_MODELS
|
||||
.get(path_columns[1])
|
||||
.map_or(path_columns[1], |model| model.key),
|
||||
path_columns[2]
|
||||
)
|
||||
}
|
||||
} else {
|
||||
format!(
|
||||
"{}:{}",
|
||||
@@ -229,14 +293,6 @@ impl FromRequest for AuthExtractor {
|
||||
)
|
||||
};
|
||||
|
||||
let query = web::Query::<HashMap<String, String>>::from_query(req.query_string()).unwrap();
|
||||
let stream_type = match get_stream_type_from_request(&query) {
|
||||
Ok(v) => v,
|
||||
Err(_) => Some(StreamType::Logs),
|
||||
};
|
||||
|
||||
let folder = get_folder(&query);
|
||||
|
||||
if let Some(auth_header) = req.headers().get("Authorization") {
|
||||
if let Ok(auth_str) = auth_header.to_str() {
|
||||
if (method.eq("POST") && path_columns[1].starts_with("_search"))
|
||||
@@ -249,6 +305,7 @@ impl FromRequest for AuthExtractor {
|
||||
o2_type: "".to_string(),
|
||||
org_id: "".to_string(),
|
||||
bypass_check: true, // bypass check permissions
|
||||
parent_id: folder,
|
||||
}));
|
||||
} else if object_type.starts_with("stream") && !method.eq("LIST") {
|
||||
let object_type = match stream_type {
|
||||
@@ -262,10 +319,20 @@ impl FromRequest for AuthExtractor {
|
||||
o2_type: object_type,
|
||||
org_id,
|
||||
bypass_check: false,
|
||||
parent_id: folder,
|
||||
}));
|
||||
} else if object_type.contains("dashboard") && !method.eq("LIST") {
|
||||
let object_type = object_type
|
||||
.replace("dashboard:", format!("dashboard:{}/", folder).as_str());
|
||||
let object_type = if method.eq("POST") {
|
||||
format!(
|
||||
"{}:{}",
|
||||
OFGA_MODELS
|
||||
.get(path_columns[1])
|
||||
.map_or("dfolder", |model| model.parent),
|
||||
folder.as_str(),
|
||||
)
|
||||
} else {
|
||||
object_type
|
||||
};
|
||||
|
||||
return ready(Ok(AuthExtractor {
|
||||
auth: auth_str.to_owned(),
|
||||
@@ -273,6 +340,7 @@ impl FromRequest for AuthExtractor {
|
||||
o2_type: object_type,
|
||||
org_id,
|
||||
bypass_check: false,
|
||||
parent_id: folder,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -282,6 +350,7 @@ impl FromRequest for AuthExtractor {
|
||||
o2_type: object_type,
|
||||
org_id,
|
||||
bypass_check: false,
|
||||
parent_id: folder,
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -301,6 +370,7 @@ impl FromRequest for AuthExtractor {
|
||||
o2_type: "".to_string(),
|
||||
org_id: "".to_string(),
|
||||
bypass_check: true, // bypass check permissions
|
||||
parent_id: "".to_string(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,8 +405,14 @@ pub(crate) async fn check_permissions(user_id: &str, auth_info: AuthExtractor) -
|
||||
} else {
|
||||
object_str
|
||||
};
|
||||
o2_enterprise::enterprise::openfga::authorizer::is_allowed(user_id, &auth_info.method, &obj_str)
|
||||
.await
|
||||
o2_enterprise::enterprise::openfga::authorizer::is_allowed(
|
||||
&auth_info.org_id,
|
||||
user_id,
|
||||
&auth_info.method,
|
||||
&obj_str,
|
||||
&auth_info.parent_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "enterprise"))]
|
||||
|
||||
@@ -172,6 +172,7 @@ async fn query(
|
||||
o2_type: format!("stream:{}/{}", "metrics", name),
|
||||
org_id: org_id.to_string(),
|
||||
bypass_check: false,
|
||||
parent_id: "".to_string(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
@@ -322,6 +323,7 @@ async fn query_range(
|
||||
o2_type: format!("stream:{}/{}", "metrics", name),
|
||||
org_id: org_id.to_string(),
|
||||
bypass_check: false,
|
||||
parent_id: "".to_string(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -159,6 +159,7 @@ pub async fn search(
|
||||
o2_type: format!("stream:{}/{}", stream_type, resp.stream_name),
|
||||
org_id: org_id.clone(),
|
||||
bypass_check: false,
|
||||
parent_id: "".to_string(),
|
||||
},
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -18,11 +18,15 @@ use std::io::Error;
|
||||
use actix_web::{delete, get, post, put, web, HttpResponse};
|
||||
|
||||
use crate::{
|
||||
common::meta::{
|
||||
http::HttpResponse as MetaHttpResponse,
|
||||
saved_view::{
|
||||
CreateViewRequest, CreateViewResponse, DeleteViewResponse, UpdateViewRequest, View,
|
||||
common::{
|
||||
meta::{
|
||||
authz::Authz,
|
||||
http::HttpResponse as MetaHttpResponse,
|
||||
saved_view::{
|
||||
CreateViewRequest, CreateViewResponse, DeleteViewResponse, UpdateViewRequest, View,
|
||||
},
|
||||
},
|
||||
utils::auth::{remove_ownership, set_ownership},
|
||||
},
|
||||
service::db::saved_view,
|
||||
};
|
||||
@@ -128,10 +132,13 @@ pub async fn get_views(path: web::Path<String>) -> Result<HttpResponse, Error> {
|
||||
pub async fn delete_view(path: web::Path<(String, String)>) -> Result<HttpResponse, Error> {
|
||||
let (org_id, view_id) = path.into_inner();
|
||||
match saved_view::delete_view(&org_id, &view_id).await {
|
||||
Ok(_) => Ok(MetaHttpResponse::json(DeleteViewResponse {
|
||||
org_id,
|
||||
view_id,
|
||||
})),
|
||||
Ok(_) => {
|
||||
remove_ownership(&org_id, "savedviews", Authz::new(&view_id)).await;
|
||||
Ok(MetaHttpResponse::json(DeleteViewResponse {
|
||||
org_id,
|
||||
view_id,
|
||||
}))
|
||||
}
|
||||
Err(e) => Ok(MetaHttpResponse::bad_request(e)),
|
||||
}
|
||||
}
|
||||
@@ -168,11 +175,14 @@ pub async fn create_view(
|
||||
let org_id = path.into_inner();
|
||||
|
||||
match saved_view::set_view(&org_id, &view).await {
|
||||
Ok(created_view) => Ok(MetaHttpResponse::json(CreateViewResponse {
|
||||
org_id,
|
||||
view_id: created_view.view_id,
|
||||
view_name: view.view_name.clone(),
|
||||
})),
|
||||
Ok(created_view) => {
|
||||
set_ownership(&org_id, "savedviews", Authz::new(&created_view.view_id)).await;
|
||||
Ok(MetaHttpResponse::json(CreateViewResponse {
|
||||
org_id,
|
||||
view_id: created_view.view_id,
|
||||
view_name: view.view_name.clone(),
|
||||
}))
|
||||
}
|
||||
Err(e) => Ok(MetaHttpResponse::bad_request(e)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1422,6 +1422,7 @@ const useLogs = () => {
|
||||
size: obj.size,
|
||||
query_context: query_context,
|
||||
query_fn: query_fn,
|
||||
stream_type: searchObj.data.stream.streamType,
|
||||
})
|
||||
.then((res) => {
|
||||
searchObj.loading = false;
|
||||
|
||||
@@ -35,6 +35,7 @@ const search = {
|
||||
size,
|
||||
query_context,
|
||||
query_fn,
|
||||
stream_type,
|
||||
}: {
|
||||
org_identifier: string;
|
||||
index: string;
|
||||
@@ -42,8 +43,9 @@ const search = {
|
||||
size: string;
|
||||
query_context: any;
|
||||
query_fn: any;
|
||||
stream_type: string;
|
||||
}) => {
|
||||
let url = `/api/${org_identifier}/${index}/_around?key=${key}&size=${size}&sql=${query_context}`;
|
||||
let url = `/api/${org_identifier}/${index}/_around?key=${key}&size=${size}&sql=${query_context}&type=${stream_type}`;
|
||||
if (query_fn.trim() != "") {
|
||||
url = url + `&query_fn=${query_fn}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user