Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

editoast: add roles to scenario and study #8401

Merged
merged 1 commit into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions editoast/editoast_authz/src/builtin_role.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use strum::{AsRefStr, EnumString};
use strum::AsRefStr;
use strum::Display;
use strum::EnumString;

use crate::roles::BuiltinRoleSet;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumString, AsRefStr)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumString, AsRefStr, Display)]
#[strum(serialize_all = "snake_case")]
pub enum BuiltinRole {
#[strum(serialize = "operational_studies:write")]
Expand Down
36 changes: 34 additions & 2 deletions editoast/src/views/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use chrono::Utc;
use derivative::Derivative;
use editoast_authz::BuiltinRole;
use editoast_derive::EditoastError;
use editoast_models::DbConnection;
use editoast_models::DbConnectionPoolV2;
use serde::Deserialize;
use serde::Serialize;
Expand All @@ -32,7 +33,6 @@ use crate::modelsv2::Project;
use crate::modelsv2::Retrieve;
use crate::views::pagination::PaginationQueryParam;
use crate::views::AuthorizationError;
use editoast_models::DbConnection;

crate::routes! {
"/projects" => {
Expand Down Expand Up @@ -190,9 +190,17 @@ struct ProjectWithStudyCountList {
)]
async fn list(
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Query(pagination_params): Query<PaginationQueryParam>,
Query(ordering_params): Query<OperationalStudiesOrderingParam>,
) -> Result<Json<ProjectWithStudyCountList>> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsRead].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}
let ordering = ordering_params.ordering;
let settings = pagination_params
.validate(1000)?
Expand Down Expand Up @@ -233,8 +241,16 @@ pub struct ProjectIdParam {
)]
async fn get(
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path(project_id): Path<i64>,
) -> Result<Json<ProjectWithStudyCount>> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsRead].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}
let conn = &mut db_pool.get().await?;
let project =
Project::retrieve_or_fail(conn, project_id, || ProjectError::NotFound { project_id })
Expand All @@ -254,8 +270,16 @@ async fn get(
)]
async fn delete(
Path(project_id): Path<i64>,
Extension(authorizer): AuthorizerExt,
State(db_pool): State<DbConnectionPoolV2>,
) -> Result<impl IntoResponse> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}
let conn = &mut db_pool.get().await?;
if Project::delete_and_prune_document(conn, project_id).await? {
Ok(axum::http::StatusCode::NO_CONTENT)
Expand Down Expand Up @@ -311,10 +335,18 @@ impl From<ProjectPatchForm> for Changeset<Project> {
)
)]
async fn patch(
Path(project_id): Path<i64>,
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path(project_id): Path<i64>,
Json(form): Json<ProjectPatchForm>,
) -> Result<Json<ProjectWithStudyCount>> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}
let conn = &mut db_pool.get().await?;
if let Some(image) = form.image {
check_image_content(conn, image).await?;
Expand Down
55 changes: 50 additions & 5 deletions editoast/src/views/study.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@ use axum::extract::Path;
use axum::extract::Query;
use axum::extract::State;
use axum::response::IntoResponse;
use axum::Extension;
use chrono::NaiveDate;
use chrono::Utc;
use derivative::Derivative;
use diesel_async::scoped_futures::ScopedFutureExt;
use diesel_async::AsyncConnection;
use editoast_authz::BuiltinRole;
use editoast_derive::EditoastError;
use editoast_models::DbConnection;
use editoast_models::DbConnectionPoolV2;
use serde::Deserialize;
use serde::Serialize;
use thiserror::Error;
Expand All @@ -20,12 +24,11 @@ use utoipa::ToSchema;
use super::operational_studies::OperationalStudiesOrderingParam;
use super::pagination::PaginationStats;
use super::scenario;
use super::AuthorizationError;
use super::AuthorizerExt;
use crate::error::InternalError;
use crate::error::Result;
use crate::modelsv2::prelude::*;
use editoast_models::DbConnection;
use editoast_models::DbConnectionPoolV2;

use crate::modelsv2::Project;
use crate::modelsv2::Study;
use crate::modelsv2::Tags;
Expand Down Expand Up @@ -138,9 +141,18 @@ impl StudyCreateForm {
)]
async fn create(
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path(project_id): Path<i64>,
Json(data): Json<StudyCreateForm>,
) -> Result<Json<StudyResponse>> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}

let conn = &mut db_pool.get().await?;

let (study, project) = conn
Expand Down Expand Up @@ -191,9 +203,17 @@ pub struct StudyIdParam {
)
)]
async fn delete(
Path((project_id, study_id)): Path<(i64, i64)>,
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path((project_id, study_id)): Path<(i64, i64)>,
) -> Result<impl IntoResponse> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}
// Check if project exists
let conn = &mut db_pool.get().await?;
let mut project =
Expand Down Expand Up @@ -221,8 +241,16 @@ async fn delete(
)]
async fn get(
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path((project_id, study_id)): Path<(i64, i64)>,
) -> Result<Json<StudyResponse>> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsRead].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}
// Check if project exists
let conn = &mut db_pool.get().await?;
use crate::modelsv2::Retrieve;
Expand Down Expand Up @@ -295,10 +323,18 @@ impl StudyPatchForm {
)
)]
async fn patch(
Path((project_id, study_id)): Path<(i64, i64)>,
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path((project_id, study_id)): Path<(i64, i64)>,
Json(data): Json<StudyPatchForm>,
) -> Result<Json<StudyResponse>> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}
let conn = &mut db_pool.get().await?;
let (study_scenarios, project) = conn
.transaction::<_, InternalError, _>(|conn| {
Expand Down Expand Up @@ -367,10 +403,19 @@ struct StudyListResponse {
)]
async fn list(
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path(project_id): Path<i64>,
Query(pagination_params): Query<PaginationQueryParam>,
Query(ordering_params): Query<OperationalStudiesOrderingParam>,
) -> Result<Json<StudyListResponse>> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsRead].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}

let ordering = ordering_params.ordering;
if !Project::exists(db_pool.get().await?.deref_mut(), project_id).await? {
return Err(ProjectError::NotFound { project_id }.into());
Expand Down
57 changes: 53 additions & 4 deletions editoast/src/views/v2/scenario.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ use axum::extract::Path;
use axum::extract::Query;
use axum::extract::State;
use axum::response::IntoResponse;
use axum::Extension;
use chrono::Utc;
use derivative::Derivative;
use diesel_async::scoped_futures::ScopedFutureExt;
use diesel_async::AsyncConnection;
use editoast_authz::BuiltinRole;
use editoast_derive::EditoastError;
use editoast_models::DbConnection;
use editoast_models::DbConnectionPoolV2;
use serde::Deserialize;
use serde::Serialize;
use thiserror::Error;
Expand All @@ -35,8 +39,8 @@ use crate::views::projects::ProjectIdParam;
use crate::views::scenario::ScenarioIdParam;
use crate::views::study::StudyError;
use crate::views::study::StudyIdParam;
use editoast_models::DbConnection;
use editoast_models::DbConnectionPoolV2;
use crate::views::AuthorizationError;
use crate::views::AuthorizerExt;

crate::routes! {
"/v2/projects/{project_id}/studies/{study_id}/scenarios" => {
Expand Down Expand Up @@ -191,9 +195,18 @@ impl ScenarioResponse {
)]
async fn create(
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path((project_id, study_id)): Path<(i64, i64)>,
Json(data): Json<ScenarioCreateForm>,
) -> Result<Json<ScenarioResponse>> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}

let timetable_id = data.timetable_id;
let infra_id = data.infra_id;
let scenario: Changeset<Scenario> = data.into();
Expand Down Expand Up @@ -252,13 +265,22 @@ async fn create(
)
)]
async fn delete(
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path(ScenarioPathParam {
project_id,
study_id,
scenario_id,
}): Path<ScenarioPathParam>,
State(db_pool): State<DbConnectionPoolV2>,
) -> Result<impl IntoResponse> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}

db_pool
.get()
.await?
Expand Down Expand Up @@ -326,14 +348,23 @@ impl From<ScenarioPatchForm> for <Scenario as crate::modelsv2::Model>::Changeset
)
)]
async fn patch(
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path(ScenarioPathParam {
project_id,
study_id,
scenario_id,
}): Path<ScenarioPathParam>,
State(db_pool): State<DbConnectionPoolV2>,
Json(form): Json<ScenarioPatchForm>,
) -> Result<Json<ScenarioResponse>> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsWrite].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}

let scenarios_response = db_pool
.get()
.await?
Expand Down Expand Up @@ -390,12 +421,21 @@ async fn patch(
)]
async fn get(
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path(ScenarioPathParam {
project_id,
study_id,
scenario_id,
}): Path<ScenarioPathParam>,
) -> Result<Json<ScenarioResponse>> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsRead].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}

let conn = &mut db_pool.get().await?;

let (project, study) = check_project_study(conn, project_id, study_id).await?;
Expand Down Expand Up @@ -435,10 +475,19 @@ struct ListScenariosResponse {
)]
async fn list(
State(db_pool): State<DbConnectionPoolV2>,
Extension(authorizer): AuthorizerExt,
Path((project_id, study_id)): Path<(i64, i64)>,
Query(pagination_params): Query<PaginationQueryParam>,
Query(OperationalStudiesOrderingParam { ordering }): Query<OperationalStudiesOrderingParam>,
) -> Result<Json<ListScenariosResponse>> {
let authorized = authorizer
.check_roles([BuiltinRole::OpsRead].into())
.await
.map_err(AuthorizationError::AuthError)?;
if !authorized {
return Err(AuthorizationError::Unauthorized.into());
}

let _ = check_project_study(db_pool.get().await?.deref_mut(), project_id, study_id).await?;

let settings = pagination_params
Expand Down
Loading