From e06bd31b4c9e5e2e5f0009ff92e51171f86989af Mon Sep 17 00:00:00 2001 From: Philippe Loctaux Date: Sun, 2 Apr 2023 01:59:40 +0200 Subject: [PATCH] ezidam: apps: archive, and revoke refresh tokens associated with app --- crates/apps/src/database.rs | 4 ++ crates/database/queries/apps/archive.sql | 5 ++ .../refresh_tokens/revoke_all_for_app.sql | 6 +++ crates/database/sqlx-data.json | 20 +++++++ crates/database/src/tables/apps.rs | 9 ++++ crates/database/src/tables/refresh_tokens.rs | 13 +++++ crates/ezidam/src/icons.rs | 4 +- crates/ezidam/src/routes/admin.rs | 1 + crates/ezidam/src/routes/admin/apps.rs | 39 ++++++++++++++ .../templates/pages/admin/apps/view.html.tera | 53 ++++++++++++++++++- crates/refresh_tokens/src/database.rs | 7 +++ 11 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 crates/database/queries/apps/archive.sql create mode 100644 crates/database/queries/refresh_tokens/revoke_all_for_app.sql diff --git a/crates/apps/src/database.rs b/crates/apps/src/database.rs index 0ba46ee..2bfc616 100644 --- a/crates/apps/src/database.rs +++ b/crates/apps/src/database.rs @@ -103,4 +103,8 @@ impl App { ) -> Result, Error> { Ok(DatabaseApps::new_secret(conn, self.id.as_ref(), secret.hash()).await?) } + + pub async fn archive(&self, conn: impl SqliteExecutor<'_>) -> Result, Error> { + Ok(DatabaseApps::archive(conn, self.id.as_ref()).await?) + } } diff --git a/crates/database/queries/apps/archive.sql b/crates/database/queries/apps/archive.sql new file mode 100644 index 0000000..33e04b9 --- /dev/null +++ b/crates/database/queries/apps/archive.sql @@ -0,0 +1,5 @@ +update apps + +set is_archived = 1 + +where id is ? \ No newline at end of file diff --git a/crates/database/queries/refresh_tokens/revoke_all_for_app.sql b/crates/database/queries/refresh_tokens/revoke_all_for_app.sql new file mode 100644 index 0000000..9cc5bd1 --- /dev/null +++ b/crates/database/queries/refresh_tokens/revoke_all_for_app.sql @@ -0,0 +1,6 @@ +update refresh_tokens + +set revoked_at = CURRENT_TIMESTAMP + +where app is ? + and revoked_at is null \ No newline at end of file diff --git a/crates/database/sqlx-data.json b/crates/database/sqlx-data.json index 8a7341c..fc8eabf 100644 --- a/crates/database/sqlx-data.json +++ b/crates/database/sqlx-data.json @@ -100,6 +100,16 @@ }, "query": "update apps\n\nset secret = ?\n\nwhere id is ?" }, + "2d562e7b19d7d8303a0e79d143d25fd68743ae30d1ec0b0ca5c7dfc367fdf357": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 1 + } + }, + "query": "update apps\n\nset is_archived = 1\n\nwhere id is ?" + }, "37681902a5f5d87492812a525a6488e75d20c1c436a3ba2c5aa3f54da62fe861": { "describe": { "columns": [ @@ -508,6 +518,16 @@ }, "query": "update settings\n\nset url = ?\n\nwhere id is 0\n" }, + "93b15a942a6c7db595990f00e14fde26d6d36b8c8de9935179d41f6c7c755978": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 1 + } + }, + "query": "update refresh_tokens\n\nset revoked_at = CURRENT_TIMESTAMP\n\nwhere app is ?\n and revoked_at is null" + }, "9f1885c4786f73335b4d614f562bb7cad49c91bfe7f084d8c25c6c571673ab90": { "describe": { "columns": [], diff --git a/crates/database/src/tables/apps.rs b/crates/database/src/tables/apps.rs index 7445976..8932eea 100644 --- a/crates/database/src/tables/apps.rs +++ b/crates/database/src/tables/apps.rs @@ -136,4 +136,13 @@ impl Apps { Ok((query.rows_affected() == 1).then_some(())) } + + pub async fn archive(conn: impl SqliteExecutor<'_>, id: &str) -> Result, Error> { + let query: SqliteQueryResult = sqlx::query_file!("queries/apps/archive.sql", id) + .execute(conn) + .await + .map_err(handle_error)?; + + Ok((query.rows_affected() == 1).then_some(())) + } } diff --git a/crates/database/src/tables/refresh_tokens.rs b/crates/database/src/tables/refresh_tokens.rs index 25f8ed0..fcf67d0 100644 --- a/crates/database/src/tables/refresh_tokens.rs +++ b/crates/database/src/tables/refresh_tokens.rs @@ -96,4 +96,17 @@ impl RefreshTokens { Ok((query.rows_affected() == 1).then_some(())) } + + pub async fn revoke_all_for_app( + conn: impl SqliteExecutor<'_>, + app: &str, + ) -> Result, Error> { + let query: SqliteQueryResult = + sqlx::query_file!("queries/refresh_tokens/revoke_all_for_app.sql", app) + .execute(conn) + .await + .map_err(handle_error)?; + + Ok((query.rows_affected() >= 1).then_some(())) + } } diff --git a/crates/ezidam/src/icons.rs b/crates/ezidam/src/icons.rs index 1377aff..761766c 100644 --- a/crates/ezidam/src/icons.rs +++ b/crates/ezidam/src/icons.rs @@ -35,7 +35,8 @@ impl Icon { "plus", Plus, r#""#, "moon", Moon, r#""#, "sun", Sun, r#""#, - "arrow-left", ArrowLeft, r#""# + "arrow-left", ArrowLeft, r#""#, + "alert-triangle-large", AlertTriangleLarge, r#""# } } @@ -52,6 +53,7 @@ pub fn icons_to_templates(tera: &mut Tera) { Icon::Moon, Icon::Sun, Icon::ArrowLeft, + Icon::AlertTriangleLarge, ]; // For each icon, it will output: ("icons/name", "...") diff --git a/crates/ezidam/src/routes/admin.rs b/crates/ezidam/src/routes/admin.rs index 4243b59..d98eb56 100644 --- a/crates/ezidam/src/routes/admin.rs +++ b/crates/ezidam/src/routes/admin.rs @@ -20,6 +20,7 @@ pub fn routes() -> Vec { admin_apps_view, admin_apps_view_form, admin_apps_new_secret, + admin_apps_archive, ] } diff --git a/crates/ezidam/src/routes/admin/apps.rs b/crates/ezidam/src/routes/admin/apps.rs index d95e3b9..feb7906 100644 --- a/crates/ezidam/src/routes/admin/apps.rs +++ b/crates/ezidam/src/routes/admin/apps.rs @@ -166,3 +166,42 @@ pub async fn admin_apps_new_secret( ), )) } + +#[derive(Debug, FromForm)] +pub struct ArchiveApp { + pub archive: bool, +} + +#[post("/admin/apps//archive", data = "
")] +pub async fn admin_apps_archive( + mut db: Connection, + id: RocketAppID, + _admin: JwtAdmin, + form: Form, +) -> Result> { + if !form.archive { + return Err(Error::bad_request("Not archiving")); + } + + let mut transaction = db.begin().await?; + + // Get app + let app_id = &id.0; + let app = App::get_one_by_id(&mut transaction, app_id.as_ref()) + .await? + .ok_or_else(|| Error::not_found(app_id.to_string()))?; + + // Archive + app.archive(&mut transaction).await?; + + // Revoke refresh tokens + refresh_tokens::RefreshToken::revoke_all_for_app(&mut transaction, app_id).await?; + + transaction.commit().await?; + + Ok(Flash::new( + Redirect::to(uri!(admin_apps_list)), + FlashKind::Success, + format!("Application \"{}\" has been archived", app.label()), + )) +} diff --git a/crates/ezidam/templates/pages/admin/apps/view.html.tera b/crates/ezidam/templates/pages/admin/apps/view.html.tera index 8ea352d..eef8753 100644 --- a/crates/ezidam/templates/pages/admin/apps/view.html.tera +++ b/crates/ezidam/templates/pages/admin/apps/view.html.tera @@ -149,7 +149,14 @@ @@ -157,6 +164,50 @@ + + + {% endblock content %} {% block libs_js %} diff --git a/crates/refresh_tokens/src/database.rs b/crates/refresh_tokens/src/database.rs index 1818f56..82a897b 100644 --- a/crates/refresh_tokens/src/database.rs +++ b/crates/refresh_tokens/src/database.rs @@ -72,4 +72,11 @@ impl RefreshToken { pub async fn use_token(&self, conn: impl SqliteExecutor<'_>) -> Result, Error> { Ok(DatabaseRefreshTokens::use_token(conn, &self.token).await?) } + + pub async fn revoke_all_for_app( + conn: impl SqliteExecutor<'_>, + app: &AppID, + ) -> Result, Error> { + Ok(DatabaseRefreshTokens::revoke_all_for_app(conn, app.as_ref()).await?) + } }