fix: ReBAC issues (#2659)

This commit is contained in:
Ashish Kolhe
2024-02-06 07:24:45 +05:30
committed by GitHub
parent 0d78499969
commit 1947bca6d2
8 changed files with 133 additions and 34 deletions
+7
View File
@@ -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
View File
@@ -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(),
}));
}
}
+8 -2
View File
@@ -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"))]
+2
View File
@@ -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
+1
View File
@@ -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
+23 -13
View File
@@ -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)),
}
}
+1
View File
@@ -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;
+3 -1
View File
@@ -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}`;
}