From fce9ce7123c953cb8bf3d481adc86091897ac82b Mon Sep 17 00:00:00 2001 From: Nikhil Sinha Date: Thu, 5 Sep 2024 20:02:45 +0530 Subject: [PATCH 1/3] fixes on RBAC 1. role assignment to all the priviledges fixes as per sheet - https://docs.google.com/spreadsheets/d/1N-LHWTcyVzdAFR5bAPnOpRLQXdgaQ6wBHQ47skUFlPI/edit?gid=0#gid=0 2. login API restricted for ingestors 3. added authorization for metrics API --- server/src/handlers/http/cluster/mod.rs | 2 + .../src/handlers/http/modal/ingest_server.rs | 3 +- .../src/handlers/http/modal/query_server.rs | 1 + server/src/handlers/http/modal/server.rs | 13 +++- server/src/metrics/mod.rs | 41 +++++++++++++ server/src/rbac/role.rs | 59 +++++++++++++++---- 6 files changed, 104 insertions(+), 15 deletions(-) diff --git a/server/src/handlers/http/cluster/mod.rs b/server/src/handlers/http/cluster/mod.rs index 9d07e7e31..24e045ce6 100644 --- a/server/src/handlers/http/cluster/mod.rs +++ b/server/src/handlers/http/cluster/mod.rs @@ -174,6 +174,7 @@ pub async fn fetch_daily_stats_from_ingestors( let res = reqwest::Client::new() .get(uri) + .header(header::AUTHORIZATION, &ingestor.token) .header(header::CONTENT_TYPE, "application/json") .send() .await; @@ -526,6 +527,7 @@ async fn fetch_cluster_metrics() -> Result, PostError> { let res = reqwest::Client::new() .get(uri) + .header(header::AUTHORIZATION, &ingestor.token) .header(header::CONTENT_TYPE, "application/json") .send() .await; diff --git a/server/src/handlers/http/modal/ingest_server.rs b/server/src/handlers/http/modal/ingest_server.rs index 68747c0b2..58e427773 100644 --- a/server/src/handlers/http/modal/ingest_server.rs +++ b/server/src/handlers/http/modal/ingest_server.rs @@ -147,6 +147,7 @@ impl IngestServer { .service(Server::get_about_factory()) .service(Self::analytics_factory()) .service(Server::get_liveness_factory()) + .service(Server::get_metrics_webscope()) .service(Server::get_readiness_factory()), ) .service(Server::get_ingest_otel_factory()); @@ -192,7 +193,7 @@ impl IngestServer { web::resource("/info").route( web::get() .to(logstream::get_stream_info) - .authorize_for_stream(Action::GetStream), + .authorize_for_stream(Action::GetStreamInfo), ), ) .service( diff --git a/server/src/handlers/http/modal/query_server.rs b/server/src/handlers/http/modal/query_server.rs index 9d6f3fbdf..713129e81 100644 --- a/server/src/handlers/http/modal/query_server.rs +++ b/server/src/handlers/http/modal/query_server.rs @@ -133,6 +133,7 @@ impl QueryServer { .service(Server::get_llm_webscope()) .service(Server::get_oauth_webscope(oidc_client)) .service(Server::get_user_role_webscope()) + .service(Server::get_metrics_webscope()) .service(Self::get_cluster_web_scope()), ) .service(Server::get_generated()); diff --git a/server/src/handlers/http/modal/server.rs b/server/src/handlers/http/modal/server.rs index e17080e31..1ea53f48e 100644 --- a/server/src/handlers/http/modal/server.rs +++ b/server/src/handlers/http/modal/server.rs @@ -148,12 +148,19 @@ impl Server { .service(Self::get_filters_webscope()) .service(Self::get_llm_webscope()) .service(Self::get_oauth_webscope(oidc_client)) - .service(Self::get_user_role_webscope()), + .service(Self::get_user_role_webscope()) + .service(Self::get_metrics_webscope()), ) .service(Self::get_ingest_otel_factory()) .service(Self::get_generated()); } + pub fn get_metrics_webscope() -> Scope { + web::scope("/metrics").service( + web::resource("").route(web::get().to(metrics::get).authorize(Action::Metrics)), + ) + } + // get the dashboards web scope pub fn get_dashboards_webscope() -> Scope { web::scope("/dashboards") @@ -280,7 +287,7 @@ impl Server { web::resource("/info").route( web::get() .to(logstream::get_stream_info) - .authorize_for_stream(Action::GetStream), + .authorize_for_stream(Action::GetStreamInfo), ), ) .service( @@ -391,7 +398,7 @@ impl Server { // get the oauth webscope pub fn get_oauth_webscope(oidc_client: Option) -> Scope { let oauth = web::scope("/o") - .service(resource("/login").route(web::get().to(oidc::login))) + .service(resource("/login").route(web::get().to(oidc::login).authorize(Action::Login))) .service(resource("/logout").route(web::get().to(oidc::logout))) .service(resource("/code").route(web::get().to(oidc::reply_login))); diff --git a/server/src/metrics/mod.rs b/server/src/metrics/mod.rs index c034e580e..b9e2bc555 100644 --- a/server/src/metrics/mod.rs +++ b/server/src/metrics/mod.rs @@ -20,7 +20,9 @@ pub mod prom_utils; pub mod storage; use crate::{handlers::http::metrics_path, stats::FullStats}; +use actix_web::Responder; use actix_web_prometheus::{PrometheusMetrics, PrometheusMetricsBuilder}; +use error::MetricsError; use once_cell::sync::Lazy; use prometheus::{HistogramOpts, HistogramVec, IntCounterVec, IntGaugeVec, Opts, Registry}; @@ -287,3 +289,42 @@ pub async fn fetch_stats_from_storage(stream_name: &str, stats: FullStats) { .with_label_values(&["data", stream_name, "parquet"]) .set(stats.lifetime_stats.storage as i64); } + +use actix_web::HttpResponse; + +pub async fn get() -> Result { + Ok(HttpResponse::Ok().body(format!("{:?}", build_metrics_handler()))) +} + +pub mod error { + + use actix_web::http::header::ContentType; + use http::StatusCode; + + #[derive(Debug, thiserror::Error)] + pub enum MetricsError { + #[error("{0}")] + Custom(String, StatusCode), + } + + impl actix_web::ResponseError for MetricsError { + fn status_code(&self) -> http::StatusCode { + match self { + Self::Custom(_, _) => StatusCode::INTERNAL_SERVER_ERROR, + } + } + + fn error_response(&self) -> actix_web::HttpResponse { + actix_web::HttpResponse::build(self.status_code()) + .insert_header(ContentType::plaintext()) + .body(self.to_string()) + } + } + + #[allow(dead_code)] + fn construct_custom_error() { + let error = + MetricsError::Custom("Some error".to_string(), StatusCode::INTERNAL_SERVER_ERROR); + println!("{:?}", error); + } +} diff --git a/server/src/rbac/role.rs b/server/src/rbac/role.rs index 1532c7c21..ecc382771 100644 --- a/server/src/rbac/role.rs +++ b/server/src/rbac/role.rs @@ -24,7 +24,7 @@ pub enum Action { Query, CreateStream, ListStream, - GetStream, + GetStreamInfo, GetSchema, GetStats, DeleteStream, @@ -63,6 +63,8 @@ pub enum Action { DeleteFilter, ListCache, RemoveCache, + Login, + Metrics, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -102,7 +104,9 @@ impl RoleBuilder { self.stream.clone().unwrap(), self.tag.clone(), ), - Action::PutUser + Action::Login + | Action::Metrics + | Action::PutUser | Action::ListUser | Action::PutUserRoles | Action::GetUserRoles @@ -115,7 +119,7 @@ impl RoleBuilder { | Action::ListRole | Action::CreateStream | Action::DeleteStream - | Action::GetStream + | Action::GetStreamInfo | Action::ListStream | Action::ListCluster | Action::ListClusterMetrics @@ -201,11 +205,14 @@ pub mod model { fn editor_perm_builder() -> RoleBuilder { RoleBuilder { actions: vec![ + Action::Login, + Action::Metrics, Action::Ingest, Action::Query, Action::CreateStream, + Action::DeleteStream, Action::ListStream, - Action::GetStream, + Action::GetStreamInfo, Action::GetSchema, Action::GetStats, Action::GetRetention, @@ -217,8 +224,15 @@ pub mod model { Action::DeleteHotTierEnabled, Action::PutAlert, Action::GetAlert, - Action::GetAbout, Action::QueryLLM, + Action::CreateFilter, + Action::ListFilter, + Action::GetFilter, + Action::DeleteFilter, + Action::ListDashboard, + Action::GetDashboard, + Action::CreateDashboard, + Action::DeleteDashboard, ], stream: Some("*".to_string()), tag: None, @@ -228,17 +242,32 @@ pub mod model { fn writer_perm_builder() -> RoleBuilder { RoleBuilder { actions: vec![ - Action::Ingest, + Action::Login, Action::Query, Action::ListStream, - Action::GetStream, Action::GetSchema, Action::GetStats, - Action::GetRetention, + Action::PutRetention, Action::PutAlert, Action::GetAlert, - Action::GetAbout, + Action::PutRetention, + Action::GetRetention, + Action::PutHotTierEnabled, + Action::GetHotTierEnabled, + Action::DeleteHotTierEnabled, + Action::ListDashboard, + Action::GetDashboard, + Action::CreateDashboard, + Action::DeleteDashboard, + Action::Ingest, Action::QueryLLM, + Action::GetStreamInfo, + Action::GetCacheEnabled, + Action::PutCacheEnabled, + Action::GetFilter, + Action::ListFilter, + Action::CreateFilter, + Action::DeleteFilter, ], stream: None, tag: None, @@ -248,17 +277,25 @@ pub mod model { fn reader_perm_builder() -> RoleBuilder { RoleBuilder { actions: vec![ + Action::Login, Action::Query, Action::ListStream, - Action::GetStream, Action::GetSchema, Action::GetStats, Action::GetRetention, Action::GetAlert, - Action::GetAbout, Action::QueryLLM, Action::ListCluster, Action::GetHotTierEnabled, + Action::ListFilter, + Action::GetFilter, + Action::CreateFilter, + Action::DeleteFilter, + Action::ListDashboard, + Action::GetDashboard, + Action::CreateDashboard, + Action::DeleteDashboard, + Action::GetStreamInfo, ], stream: None, tag: None, From 99048896dc1fc42797fe03686f4c9abe776d1790 Mon Sep 17 00:00:00 2001 From: Nikhil Sinha Date: Thu, 5 Sep 2024 20:42:54 +0530 Subject: [PATCH 2/3] roles correctedwq --- server/src/rbac/role.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/server/src/rbac/role.rs b/server/src/rbac/role.rs index ecc382771..2dfb05805 100644 --- a/server/src/rbac/role.rs +++ b/server/src/rbac/role.rs @@ -250,7 +250,6 @@ pub mod model { Action::PutRetention, Action::PutAlert, Action::GetAlert, - Action::PutRetention, Action::GetRetention, Action::PutHotTierEnabled, Action::GetHotTierEnabled, @@ -282,11 +281,7 @@ pub mod model { Action::ListStream, Action::GetSchema, Action::GetStats, - Action::GetRetention, - Action::GetAlert, Action::QueryLLM, - Action::ListCluster, - Action::GetHotTierEnabled, Action::ListFilter, Action::GetFilter, Action::CreateFilter, From d6f3b555cbc938c0dd5a9a6955110021788533e9 Mon Sep 17 00:00:00 2001 From: Nikhil Sinha Date: Sun, 8 Sep 2024 09:02:51 +0530 Subject: [PATCH 3/3] added authorization to logout api --- server/src/handlers/http/modal/server.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/handlers/http/modal/server.rs b/server/src/handlers/http/modal/server.rs index 1ea53f48e..fb3f88090 100644 --- a/server/src/handlers/http/modal/server.rs +++ b/server/src/handlers/http/modal/server.rs @@ -399,7 +399,9 @@ impl Server { pub fn get_oauth_webscope(oidc_client: Option) -> Scope { let oauth = web::scope("/o") .service(resource("/login").route(web::get().to(oidc::login).authorize(Action::Login))) - .service(resource("/logout").route(web::get().to(oidc::logout))) + .service( + resource("/logout").route(web::get().to(oidc::logout).authorize(Action::Login)), + ) .service(resource("/code").route(web::get().to(oidc::reply_login))); if let Some(client) = oidc_client {