Skip to content

Commit 3a63de4

Browse files
committed
editoast: add check privilege to delete rolling stock endpoint
Signed-off-by: Youness CHRIFI ALAOUI <youness.chrifi@gmail.com>
1 parent 14f617d commit 3a63de4

1 file changed

Lines changed: 69 additions & 9 deletions

File tree

editoast/src/views/rolling_stock.rs

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ use utoipa::ToSchema;
4242
use crate::AppState;
4343
use crate::error::InternalError;
4444
use crate::error::Result;
45-
use crate::views::AuthorizationError;
4645

4746
#[derive(Debug, Serialize, ToSchema)]
4847
pub struct RollingStockWithLiveries {
@@ -322,7 +321,7 @@ pub(in crate::views) async fn update(
322321
State(AppState {
323322
regulator, db_pool, ..
324323
}): State<AppState>,
325-
Extension(state): Extension<crate::authentication::State>,
324+
Extension(authn_state): Extension<crate::authentication::State>,
326325
Path(rolling_stock_id): Path<i64>,
327326
Json(rolling_stock_form): Json<RollingStockForm>,
328327
) -> Result<Json<RollingStockWithLiveries>> {
@@ -333,10 +332,8 @@ pub(in crate::views) async fn update(
333332
rolling_stock_privileges(user, authz::RollingStock(rolling_stock_id)),
334333
&RollingStockPrivilege::CanWrite,
335334
)
336-
.await?
337-
{
338-
return Err(AuthorizationError::Forbidden.into());
339-
};
335+
.await?;
336+
}
340337
let new_rolling_stock = db_pool
341338
.get()
342339
.await?
@@ -396,10 +393,23 @@ pub(in crate::views) struct DeleteRollingStockQueryParams {
396393
)
397394
)]
398395
pub(in crate::views) async fn delete(
399-
State(db_pool): State<Arc<DbConnectionPoolV2>>,
396+
State(AppState {
397+
regulator, db_pool, ..
398+
}): State<AppState>,
399+
Extension(authn_state): Extension<crate::authentication::State>,
400400
Path(rolling_stock_id): Path<i64>,
401401
Query(DeleteRollingStockQueryParams { force }): Query<DeleteRollingStockQueryParams>,
402402
) -> Result<impl IntoResponse> {
403+
if let Some(user) = authn_state.regular_user() {
404+
let authorizer = authn_state.authorizer(regulator.openfga(), db_pool.get().await?);
405+
crate::authorizers::require(
406+
&authorizer,
407+
rolling_stock_privileges(user, authz::RollingStock(rolling_stock_id)),
408+
&RollingStockPrivilege::CanDelete,
409+
)
410+
.await?;
411+
}
412+
403413
let conn = &mut db_pool.get().await?;
404414

405415
let rolling_stock = RollingStock::retrieve_or_fail(conn.clone(), rolling_stock_id, || {
@@ -1520,7 +1530,7 @@ pub mod tests {
15201530
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
15211531
async fn delete_locked_rolling_stock_fails() {
15221532
// GIVEN
1523-
let app = test_app!().skip_authz().build();
1533+
let app = test_app!().build();
15241534
let db_pool = app.db_pool();
15251535

15261536
let locked_rs_name = "locked_fast_rolling_stock_name";
@@ -1534,9 +1544,16 @@ pub mod tests {
15341544
.await
15351545
.expect("Failed to create rolling stock");
15361546

1547+
let user = app
1548+
.user("owner", "Owner")
1549+
.with_roles([Role::OperationalStudies])
1550+
.with_rolling_stock_grant(locked_fast_rolling_stock.id, RollingStockGrant::Owner)
1551+
.create()
1552+
.await;
15371553
// WHEN
15381554
let raw_response = app
15391555
.delete(format!("/rolling_stock/{}", locked_fast_rolling_stock.id).as_str())
1556+
.by_user(&user.info)
15401557
.await;
15411558

15421559
// THEN
@@ -1555,16 +1572,24 @@ pub mod tests {
15551572
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
15561573
async fn delete_unlocked_unused_rolling_stock_succeeds() {
15571574
// GIVEN
1558-
let app = test_app!().skip_authz().build();
1575+
let app = test_app!().build();
15591576
let db_pool = app.db_pool();
15601577

15611578
let rs_name = "fast_rolling_stock_name";
15621579
let fast_rolling_stock = create_fast_rolling_stock(&mut db_pool.get_ok(), rs_name).await;
15631580
assert!(!fast_rolling_stock.locked);
15641581

1582+
let user = app
1583+
.user("owner", "Owner")
1584+
.with_roles([Role::OperationalStudies])
1585+
.with_rolling_stock_grant(fast_rolling_stock.id, RollingStockGrant::Owner)
1586+
.create()
1587+
.await;
1588+
15651589
// WHEN
15661590
let raw_response = app
15671591
.delete(format!("/rolling_stock/{}", fast_rolling_stock.id).as_str())
1592+
.by_user(&user.info)
15681593
.await;
15691594

15701595
// THEN
@@ -1577,6 +1602,41 @@ pub mod tests {
15771602
assert!(!rolling_stock_exists);
15781603
}
15791604

1605+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
1606+
async fn delete_rolling_stock_without_authorization() {
1607+
// GIVEN
1608+
let app = test_app!().build();
1609+
let db_pool = app.db_pool();
1610+
1611+
let fast_rolling_stock =
1612+
create_fast_rolling_stock(&mut db_pool.get_ok(), "unauthorized_rolling_stock").await;
1613+
1614+
// A user with the OperationalStudies role but only a write grant on the rolling stock
1615+
// (delete requires an owner/delete grant)
1616+
let user = app
1617+
.user("writer", "Writer")
1618+
.with_roles([Role::OperationalStudies])
1619+
.with_rolling_stock_grant(fast_rolling_stock.id, RollingStockGrant::Writer)
1620+
.create()
1621+
.await;
1622+
1623+
// WHEN
1624+
let raw_response = app
1625+
.delete(format!("/rolling_stock/{}", fast_rolling_stock.id).as_str())
1626+
.by_user(user.as_ref())
1627+
.await;
1628+
1629+
// THEN
1630+
raw_response.assert_status_forbidden();
1631+
1632+
// The rolling stock should still exist
1633+
let rolling_stock_exists =
1634+
RollingStock::exists(&mut db_pool.get_ok(), fast_rolling_stock.id)
1635+
.await
1636+
.expect("Failed to check if rolling stock exists");
1637+
assert!(rolling_stock_exists);
1638+
}
1639+
15801640
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
15811641
async fn delete_unlocked_used_rolling_stock_requires_force_flag() {
15821642
// GIVEN

0 commit comments

Comments
 (0)