Skip to content

Commit c9fa7c7

Browse files
chore: add admin authorizations to dashboards and filters (#1447)
1 parent 5f6da2e commit c9fa7c7

File tree

5 files changed

+59
-14
lines changed

5 files changed

+59
-14
lines changed

src/handlers/http/users/dashboards.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use crate::{
2323
metastore::MetastoreError,
2424
storage::ObjectStorageError,
2525
users::dashboards::{DASHBOARDS, Dashboard, Tile, validate_dashboard_id},
26-
utils::{get_hash, get_user_from_request},
26+
utils::{get_hash, get_user_from_request, is_admin},
2727
};
2828
use actix_web::{
2929
HttpRequest, HttpResponse, Responder,
@@ -104,8 +104,10 @@ pub async fn update_dashboard(
104104
) -> Result<impl Responder, DashboardError> {
105105
let user_id = get_hash(&get_user_from_request(&req)?);
106106
let dashboard_id = validate_dashboard_id(dashboard_id.into_inner())?;
107+
let is_admin = is_admin(&req).map_err(|e| DashboardError::Custom(e.to_string()))?;
108+
107109
let mut existing_dashboard = DASHBOARDS
108-
.get_dashboard_by_user(dashboard_id, &user_id)
110+
.get_dashboard_by_user(dashboard_id, &user_id, is_admin)
109111
.await
110112
.ok_or(DashboardError::Metadata(
111113
"Dashboard does not exist or user is not authorized",
@@ -189,9 +191,13 @@ pub async fn delete_dashboard(
189191
dashboard_id: Path<String>,
190192
) -> Result<HttpResponse, DashboardError> {
191193
let user_id = get_hash(&get_user_from_request(&req)?);
194+
let is_admin = is_admin(&req).map_err(|e| DashboardError::Custom(e.to_string()))?;
195+
192196
let dashboard_id = validate_dashboard_id(dashboard_id.into_inner())?;
193197

194-
DASHBOARDS.delete_dashboard(&user_id, dashboard_id).await?;
198+
DASHBOARDS
199+
.delete_dashboard(&user_id, dashboard_id, is_admin)
200+
.await?;
195201

196202
Ok(HttpResponse::Ok().finish())
197203
}
@@ -207,9 +213,10 @@ pub async fn add_tile(
207213

208214
let user_id = get_hash(&get_user_from_request(&req)?);
209215
let dashboard_id = validate_dashboard_id(dashboard_id.into_inner())?;
216+
let is_admin = is_admin(&req).map_err(|e| DashboardError::Custom(e.to_string()))?;
210217

211218
let mut dashboard = DASHBOARDS
212-
.get_dashboard_by_user(dashboard_id, &user_id)
219+
.get_dashboard_by_user(dashboard_id, &user_id, is_admin)
213220
.await
214221
.ok_or(DashboardError::Unauthorized)?;
215222

src/handlers/http/users/filters.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::{
2222
parseable::PARSEABLE,
2323
storage::ObjectStorageError,
2424
users::filters::{CURRENT_FILTER_VERSION, FILTERS, Filter},
25-
utils::{actix::extract_session_key_from_req, get_hash, get_user_from_request},
25+
utils::{actix::extract_session_key_from_req, get_hash, get_user_from_request, is_admin},
2626
};
2727
use actix_web::{
2828
HttpRequest, HttpResponse, Responder,
@@ -46,8 +46,11 @@ pub async fn get(
4646
) -> Result<impl Responder, FiltersError> {
4747
let user_id = get_user_from_request(&req)?;
4848
let filter_id = filter_id.into_inner();
49-
50-
if let Some(filter) = FILTERS.get_filter(&filter_id, &get_hash(&user_id)).await {
49+
let is_admin = is_admin(&req).map_err(|e| FiltersError::Custom(e.to_string()))?;
50+
if let Some(filter) = FILTERS
51+
.get_filter(&filter_id, &get_hash(&user_id), is_admin)
52+
.await
53+
{
5154
return Ok((web::Json(filter), StatusCode::OK));
5255
}
5356

@@ -81,7 +84,13 @@ pub async fn update(
8184
let mut user_id = get_user_from_request(&req)?;
8285
user_id = get_hash(&user_id);
8386
let filter_id = filter_id.into_inner();
84-
if FILTERS.get_filter(&filter_id, &user_id).await.is_none() {
87+
let is_admin = is_admin(&req).map_err(|e| FiltersError::Custom(e.to_string()))?;
88+
89+
if FILTERS
90+
.get_filter(&filter_id, &user_id, is_admin)
91+
.await
92+
.is_none()
93+
{
8594
return Err(FiltersError::Metadata(
8695
"Filter does not exist or user is not authorized",
8796
));
@@ -103,8 +112,9 @@ pub async fn delete(
103112
let mut user_id = get_user_from_request(&req)?;
104113
user_id = get_hash(&user_id);
105114
let filter_id = filter_id.into_inner();
115+
let is_admin = is_admin(&req).map_err(|e| FiltersError::Custom(e.to_string()))?;
106116
let filter = FILTERS
107-
.get_filter(&filter_id, &user_id)
117+
.get_filter(&filter_id, &user_id, is_admin)
108118
.await
109119
.ok_or(FiltersError::Metadata(
110120
"Filter does not exist or user is not authorized",

src/users/dashboards.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,10 @@ impl Dashboards {
295295
&self,
296296
user_id: &str,
297297
dashboard_id: Ulid,
298+
is_admin: bool,
298299
) -> Result<(), DashboardError> {
299300
let obj = self
300-
.ensure_dashboard_ownership(dashboard_id, user_id)
301+
.ensure_dashboard_ownership(dashboard_id, user_id, is_admin)
301302
.await?;
302303

303304
{
@@ -335,6 +336,7 @@ impl Dashboards {
335336
&self,
336337
dashboard_id: Ulid,
337338
user_id: &str,
339+
is_admin: bool,
338340
) -> Option<Dashboard> {
339341
self.0
340342
.read()
@@ -344,7 +346,7 @@ impl Dashboards {
344346
d.dashboard_id
345347
.as_ref()
346348
.is_some_and(|id| *id == dashboard_id)
347-
&& d.author == Some(user_id.to_string())
349+
&& (d.author == Some(user_id.to_string()) || is_admin)
348350
})
349351
.cloned()
350352
}
@@ -409,8 +411,9 @@ impl Dashboards {
409411
&self,
410412
dashboard_id: Ulid,
411413
user_id: &str,
414+
is_admin: bool,
412415
) -> Result<Dashboard, DashboardError> {
413-
self.get_dashboard_by_user(dashboard_id, user_id)
416+
self.get_dashboard_by_user(dashboard_id, user_id, is_admin)
414417
.await
415418
.ok_or_else(|| {
416419
DashboardError::Metadata(

src/users/filters.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,13 +132,19 @@ impl Filters {
132132
s.retain(|f| f.filter_id != Some(filter_id.to_string()));
133133
}
134134

135-
pub async fn get_filter(&self, filter_id: &str, user_id: &str) -> Option<Filter> {
135+
pub async fn get_filter(
136+
&self,
137+
filter_id: &str,
138+
user_id: &str,
139+
is_admin: bool,
140+
) -> Option<Filter> {
136141
self.0
137142
.read()
138143
.await
139144
.iter()
140145
.find(|f| {
141-
f.filter_id == Some(filter_id.to_string()) && f.user_id == Some(user_id.to_string())
146+
f.filter_id == Some(filter_id.to_string())
147+
&& (f.user_id == Some(user_id.to_string()) || is_admin)
142148
})
143149
.cloned()
144150
}

src/utils/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,22 @@ pub async fn user_auth_for_datasets(
145145

146146
Ok(())
147147
}
148+
149+
pub fn is_admin(req: &HttpRequest) -> Result<bool, anyhow::Error> {
150+
let session_key =
151+
extract_session_key_from_req(req).map_err(|e| anyhow::Error::msg(e.to_string()))?;
152+
153+
let permissions = Users.get_permissions(&session_key);
154+
155+
// Check if user has admin permissions (Action::All on All resources)
156+
for permission in permissions.iter() {
157+
match permission {
158+
Permission::Resource(Action::All, ParseableResourceType::All) => {
159+
return Ok(true);
160+
}
161+
_ => continue,
162+
}
163+
}
164+
165+
Ok(false)
166+
}

0 commit comments

Comments
 (0)