From 2caf584cb7923726308fbdeaf42e48f17b482134 Mon Sep 17 00:00:00 2001 From: Philippe Loctaux Date: Sun, 2 Apr 2023 00:52:16 +0200 Subject: [PATCH] ezidam: apps: view, update, new secret --- crates/apps/src/database.rs | 25 +++ crates/database/queries/apps/new_secret.sql | 5 + crates/database/queries/apps/update.sql | 7 + crates/database/sqlx-data.json | 20 +++ crates/database/src/tables/apps.rs | 34 ++++ crates/ezidam/src/id.rs | 30 +++- crates/ezidam/src/page.rs | 5 + crates/ezidam/src/routes/admin.rs | 11 ++ crates/ezidam/src/routes/admin/apps.rs | 97 +++++++++- .../templates/pages/admin/apps/list.html.tera | 10 +- .../templates/pages/admin/apps/view.html.tera | 166 ++++++++++++++++++ crates/hash/src/secret.rs | 6 + 12 files changed, 407 insertions(+), 9 deletions(-) create mode 100644 crates/database/queries/apps/new_secret.sql create mode 100644 crates/database/queries/apps/update.sql create mode 100644 crates/ezidam/templates/pages/admin/apps/view.html.tera diff --git a/crates/apps/src/database.rs b/crates/apps/src/database.rs index 7e76697..0ba46ee 100644 --- a/crates/apps/src/database.rs +++ b/crates/apps/src/database.rs @@ -78,4 +78,29 @@ impl App { .await? .map(Self::from)) } + + pub async fn update( + conn: impl SqliteExecutor<'_>, + id: &AppID, + label: &str, + redirect_uri: &Url, + is_confidential: bool, + ) -> Result, Error> { + Ok(DatabaseApps::update( + conn, + id.as_ref(), + label, + redirect_uri.as_str(), + is_confidential, + ) + .await?) + } + + pub async fn new_secret( + &self, + conn: impl SqliteExecutor<'_>, + secret: &Secret, + ) -> Result, Error> { + Ok(DatabaseApps::new_secret(conn, self.id.as_ref(), secret.hash()).await?) + } } diff --git a/crates/database/queries/apps/new_secret.sql b/crates/database/queries/apps/new_secret.sql new file mode 100644 index 0000000..523acb6 --- /dev/null +++ b/crates/database/queries/apps/new_secret.sql @@ -0,0 +1,5 @@ +update apps + +set secret = ? + +where id is ? \ No newline at end of file diff --git a/crates/database/queries/apps/update.sql b/crates/database/queries/apps/update.sql new file mode 100644 index 0000000..c419f29 --- /dev/null +++ b/crates/database/queries/apps/update.sql @@ -0,0 +1,7 @@ +update apps + +set label = ?, + redirect_uri = ?, + is_confidential = ? + +where id is ? \ No newline at end of file diff --git a/crates/database/sqlx-data.json b/crates/database/sqlx-data.json index d2c3deb..84b0490 100644 --- a/crates/database/sqlx-data.json +++ b/crates/database/sqlx-data.json @@ -80,6 +80,26 @@ }, "query": "select id,\n created_at as \"created_at: DateTime\",\n updated_at as \"updated_at: DateTime\",\n label,\n redirect_uri,\n secret,\n is_confidential as \"is_confidential: bool\",\n is_archived as \"is_archived: bool\"\nfrom apps\n\nwhere is_archived is 0\norder by created_at desc" }, + "184d704e75f00513082dd2c6cc3ae5c3f58b57b222ba4333216b5c50c3c58c71": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 4 + } + }, + "query": "update apps\n\nset label = ?,\n redirect_uri = ?,\n is_confidential = ?\n\nwhere id is ?" + }, + "1e2edc8cf28832344dbfa0878ac01361b6f97c552d6af8477da12cddb03d4865": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 2 + } + }, + "query": "update apps\n\nset secret = ?\n\nwhere id is ?" + }, "3c8e31ffa5cbfd4dded8a272777cb320fb51fd2e53ed25054d24e9801df0c358": { "describe": { "columns": [], diff --git a/crates/database/src/tables/apps.rs b/crates/database/src/tables/apps.rs index 4e461d2..7445976 100644 --- a/crates/database/src/tables/apps.rs +++ b/crates/database/src/tables/apps.rs @@ -102,4 +102,38 @@ impl Apps { .await .map_err(handle_error) } + + pub async fn update( + conn: impl SqliteExecutor<'_>, + id: &str, + label: &str, + redirect_uri: &str, + is_confidential: bool, + ) -> Result, Error> { + let query: SqliteQueryResult = sqlx::query_file!( + "queries/apps/update.sql", + label, + redirect_uri, + is_confidential, + id + ) + .execute(conn) + .await + .map_err(handle_error)?; + + Ok((query.rows_affected() == 1).then_some(())) + } + + pub async fn new_secret( + conn: impl SqliteExecutor<'_>, + id: &str, + secret: &str, + ) -> Result, Error> { + let query: SqliteQueryResult = sqlx::query_file!("queries/apps/new_secret.sql", secret, id) + .execute(conn) + .await + .map_err(handle_error)?; + + Ok((query.rows_affected() == 1).then_some(())) + } } diff --git a/crates/ezidam/src/id.rs b/crates/ezidam/src/id.rs index 14b32a9..7fdab7c 100644 --- a/crates/ezidam/src/id.rs +++ b/crates/ezidam/src/id.rs @@ -1,6 +1,9 @@ -use id::UserID; +use id::{AppID, Error, UserID}; +use rocket::http::impl_from_uri_param_identity; +use rocket::http::uri::fmt::{Formatter, Path, UriDisplay}; use rocket::request::FromParam; use rocket::serde::{Deserialize, Serialize}; +use std::fmt; use std::str::FromStr; #[derive(Serialize, Deserialize)] @@ -8,9 +11,32 @@ use std::str::FromStr; pub struct RocketUserID(pub UserID); impl<'r> FromParam<'r> for RocketUserID { - type Error = id::Error; + type Error = Error; fn from_param(param: &'r str) -> Result { UserID::from_str(param).map(RocketUserID) } } + +#[derive(Serialize, Deserialize)] +#[serde(crate = "rocket::serde")] +pub struct RocketAppID(pub AppID); + +impl<'r> FromParam<'r> for RocketAppID { + type Error = Error; + + fn from_param(param: &'r str) -> Result { + if param == "ezidam" { + return Err(Error::Invalid("App")); + } + AppID::from_str(param).map(RocketAppID) + } +} + +impl UriDisplay for RocketAppID { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + UriDisplay::fmt(&self.0 .0, f) + } +} + +impl_from_uri_param_identity!([Path] RocketAppID); diff --git a/crates/ezidam/src/page.rs b/crates/ezidam/src/page.rs index 0f7d5fd..f41d41e 100644 --- a/crates/ezidam/src/page.rs +++ b/crates/ezidam/src/page.rs @@ -20,6 +20,7 @@ pub enum Page { AdminSettingsSecurity(AdminSettingsSecurity), AdminAppsList(AdminAppsList), AdminAppsNew(AdminAppsNew), + AdminAppsView(AdminAppsView), } impl Page { @@ -36,6 +37,7 @@ impl Page { Page::AdminSettingsSecurity(_) => "pages/admin/settings/security", Page::AdminAppsList(_) => "pages/admin/apps/list", Page::AdminAppsNew(_) => "pages/admin/apps/new", + Page::AdminAppsView(_) => "pages/admin/apps/view", } } @@ -52,6 +54,7 @@ impl Page { Page::AdminSettingsSecurity(_) => "Server security", Page::AdminAppsList(_) => "Applications", Page::AdminAppsNew(_) => "New application", + Page::AdminAppsView(_) => "Application info", } } @@ -70,6 +73,7 @@ impl Page { Page::AdminSettingsSecurity(_) => Some(AdminMenu::Settings.into()), Page::AdminAppsList(_) => Some(AdminMenu::Apps.into()), Page::AdminAppsNew(_) => Some(AdminMenu::Apps.into()), + Page::AdminAppsView(_) => Some(AdminMenu::Apps.into()), } } @@ -86,6 +90,7 @@ impl Page { Page::AdminSettingsSecurity(security) => Box::new(security), Page::AdminAppsList(list) => Box::new(list), Page::AdminAppsNew(new) => Box::new(new), + Page::AdminAppsView(view) => Box::new(view), } } } diff --git a/crates/ezidam/src/routes/admin.rs b/crates/ezidam/src/routes/admin.rs index 1e763f2..4243b59 100644 --- a/crates/ezidam/src/routes/admin.rs +++ b/crates/ezidam/src/routes/admin.rs @@ -17,6 +17,9 @@ pub fn routes() -> Vec { admin_apps_list, admin_apps_new, admin_apps_new_form, + admin_apps_view, + admin_apps_view_form, + admin_apps_new_secret, ] } @@ -62,4 +65,12 @@ pub mod content { pub struct AdminAppsNew { pub user: JwtClaims, } + + #[derive(Serialize)] + #[serde(crate = "rocket::serde")] + #[derive(Clone)] + pub struct AdminAppsView { + pub user: JwtClaims, + pub app: App, + } } diff --git a/crates/ezidam/src/routes/admin/apps.rs b/crates/ezidam/src/routes/admin/apps.rs index 42f9970..d95e3b9 100644 --- a/crates/ezidam/src/routes/admin/apps.rs +++ b/crates/ezidam/src/routes/admin/apps.rs @@ -33,7 +33,7 @@ pub async fn admin_apps_new(admin: JwtAdmin) -> Result { } #[derive(Debug, FromForm)] -pub struct NewApp<'r> { +pub struct AppForm<'r> { pub label: &'r str, pub redirect_uri: &'r str, pub is_confidential: bool, @@ -43,7 +43,7 @@ pub struct NewApp<'r> { pub async fn admin_apps_new_form( mut db: Connection, _admin: JwtAdmin, - form: Form>, + form: Form>, ) -> Result> { // Generate app id let app_id = task::spawn_blocking(AppID::default).await?; @@ -73,3 +73,96 @@ pub async fn admin_apps_new_form( format!("Application \"{}\" has been created", form.label), )) } + +#[get("/admin/apps/")] +pub async fn admin_apps_view( + mut db: Connection, + id: RocketAppID, + admin: JwtAdmin, + flash: Option>, +) -> Result