ezidam: apps: form, create new app
This commit is contained in:
parent
d2e1792f3c
commit
f911fc665c
6 changed files with 175 additions and 14 deletions
|
|
@ -38,7 +38,7 @@ impl AdminMenu {
|
||||||
MainItem {
|
MainItem {
|
||||||
id: AdminMenu::Apps.id(),
|
id: AdminMenu::Apps.id(),
|
||||||
label: "Applications",
|
label: "Applications",
|
||||||
link: uri!(routes::admin::apps::admin_apps).to_string(),
|
link: uri!(routes::admin::apps::admin_apps_list).to_string(),
|
||||||
icon: Icon::Apps.svg,
|
icon: Icon::Apps.svg,
|
||||||
sub: None,
|
sub: None,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,8 @@ pub enum Page {
|
||||||
AdminDashboard(AdminDashboard),
|
AdminDashboard(AdminDashboard),
|
||||||
AdminSettingsBranding(AdminSettingsBranding),
|
AdminSettingsBranding(AdminSettingsBranding),
|
||||||
AdminSettingsSecurity(AdminSettingsSecurity),
|
AdminSettingsSecurity(AdminSettingsSecurity),
|
||||||
AdminApps(AdminApps),
|
AdminAppsList(AdminAppsList),
|
||||||
|
AdminAppsNew(AdminAppsNew),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Page {
|
impl Page {
|
||||||
|
|
@ -33,7 +34,8 @@ impl Page {
|
||||||
Page::AdminDashboard(_) => "pages/admin/dashboard",
|
Page::AdminDashboard(_) => "pages/admin/dashboard",
|
||||||
Page::AdminSettingsBranding(_) => "pages/admin/settings/branding",
|
Page::AdminSettingsBranding(_) => "pages/admin/settings/branding",
|
||||||
Page::AdminSettingsSecurity(_) => "pages/admin/settings/security",
|
Page::AdminSettingsSecurity(_) => "pages/admin/settings/security",
|
||||||
Page::AdminApps(_) => "pages/admin/apps/list",
|
Page::AdminAppsList(_) => "pages/admin/apps/list",
|
||||||
|
Page::AdminAppsNew(_) => "pages/admin/apps/new",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,7 +50,8 @@ impl Page {
|
||||||
Page::AdminDashboard(_) => "Admin dashboard",
|
Page::AdminDashboard(_) => "Admin dashboard",
|
||||||
Page::AdminSettingsBranding(_) => "Server branding",
|
Page::AdminSettingsBranding(_) => "Server branding",
|
||||||
Page::AdminSettingsSecurity(_) => "Server security",
|
Page::AdminSettingsSecurity(_) => "Server security",
|
||||||
Page::AdminApps(_) => "Applications",
|
Page::AdminAppsList(_) => "Applications",
|
||||||
|
Page::AdminAppsNew(_) => "New application",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,7 +68,8 @@ impl Page {
|
||||||
Page::AdminDashboard(_) => Some(AdminMenu::Dashboard.into()),
|
Page::AdminDashboard(_) => Some(AdminMenu::Dashboard.into()),
|
||||||
Page::AdminSettingsBranding(_) => Some(AdminMenu::Settings.into()),
|
Page::AdminSettingsBranding(_) => Some(AdminMenu::Settings.into()),
|
||||||
Page::AdminSettingsSecurity(_) => Some(AdminMenu::Settings.into()),
|
Page::AdminSettingsSecurity(_) => Some(AdminMenu::Settings.into()),
|
||||||
Page::AdminApps(_) => Some(AdminMenu::Apps.into()),
|
Page::AdminAppsList(_) => Some(AdminMenu::Apps.into()),
|
||||||
|
Page::AdminAppsNew(_) => Some(AdminMenu::Apps.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,7 +84,8 @@ impl Page {
|
||||||
Page::AdminDashboard(dashboard) => Box::new(dashboard),
|
Page::AdminDashboard(dashboard) => Box::new(dashboard),
|
||||||
Page::AdminSettingsBranding(branding) => Box::new(branding),
|
Page::AdminSettingsBranding(branding) => Box::new(branding),
|
||||||
Page::AdminSettingsSecurity(security) => Box::new(security),
|
Page::AdminSettingsSecurity(security) => Box::new(security),
|
||||||
Page::AdminApps(apps) => Box::new(apps),
|
Page::AdminAppsList(list) => Box::new(list),
|
||||||
|
Page::AdminAppsNew(new) => Box::new(new),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,9 @@ pub fn routes() -> Vec<Route> {
|
||||||
settings_update_branding,
|
settings_update_branding,
|
||||||
settings_security,
|
settings_security,
|
||||||
settings_security_form,
|
settings_security_form,
|
||||||
admin_apps,
|
admin_apps_list,
|
||||||
|
admin_apps_new,
|
||||||
|
admin_apps_new_form,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,8 +51,15 @@ pub mod content {
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
#[serde(crate = "rocket::serde")]
|
#[serde(crate = "rocket::serde")]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AdminApps {
|
pub struct AdminAppsList {
|
||||||
pub user: JwtClaims,
|
pub user: JwtClaims,
|
||||||
pub apps: Vec<App>,
|
pub apps: Vec<App>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AdminAppsNew {
|
||||||
|
pub user: JwtClaims,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,75 @@
|
||||||
use crate::routes::prelude::*;
|
use crate::routes::prelude::*;
|
||||||
use apps::App;
|
use apps::App;
|
||||||
use rocket::get;
|
use hash::{Secret, SecretString};
|
||||||
|
use rocket::{get, post};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
#[get("/admin/apps")]
|
#[get("/admin/apps")]
|
||||||
pub async fn admin_apps(mut db: Connection<Database>, admin: JwtAdmin) -> Result<Page> {
|
pub async fn admin_apps_list(
|
||||||
|
mut db: Connection<Database>,
|
||||||
|
admin: JwtAdmin,
|
||||||
|
flash: Option<FlashMessage<'_>>,
|
||||||
|
) -> Result<Template> {
|
||||||
let mut apps = App::get_all(&mut *db, None).await?;
|
let mut apps = App::get_all(&mut *db, None).await?;
|
||||||
|
|
||||||
// Remove ezidam from list
|
// Remove ezidam from list
|
||||||
apps.retain(|app| *app.id() != AppID("ezidam".into()));
|
apps.retain(|app| *app.id() != AppID("ezidam".into()));
|
||||||
|
|
||||||
Ok(Page::AdminApps(super::content::AdminApps {
|
let page = Page::AdminAppsList(super::content::AdminAppsList {
|
||||||
user: admin.0,
|
user: admin.0,
|
||||||
apps,
|
apps,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(flash
|
||||||
|
.map(|flash| Page::with_flash(page.clone(), flash))
|
||||||
|
.unwrap_or_else(|| page.into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/admin/apps/new")]
|
||||||
|
pub async fn admin_apps_new(admin: JwtAdmin) -> Result<Page> {
|
||||||
|
Ok(Page::AdminAppsNew(super::content::AdminAppsNew {
|
||||||
|
user: admin.0,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, FromForm)]
|
||||||
|
pub struct NewApp<'r> {
|
||||||
|
pub label: &'r str,
|
||||||
|
pub redirect_uri: &'r str,
|
||||||
|
pub is_confidential: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/admin/apps/new", data = "<form>")]
|
||||||
|
pub async fn admin_apps_new_form(
|
||||||
|
mut db: Connection<Database>,
|
||||||
|
_admin: JwtAdmin,
|
||||||
|
form: Form<NewApp<'_>>,
|
||||||
|
) -> Result<Flash<Redirect>> {
|
||||||
|
// Generate app id
|
||||||
|
let app_id = task::spawn_blocking(AppID::default).await?;
|
||||||
|
|
||||||
|
// Parse redirect uri
|
||||||
|
let redirect_uri = Url::parse(form.redirect_uri)?;
|
||||||
|
|
||||||
|
// Generate secret
|
||||||
|
let app_secret = task::spawn_blocking(SecretString::default).await?;
|
||||||
|
// Hash secret
|
||||||
|
let app_secret_hash = task::spawn_blocking(move || Secret::new(app_secret)).await??;
|
||||||
|
|
||||||
|
// Insert in database
|
||||||
|
App::insert(
|
||||||
|
&mut *db,
|
||||||
|
&app_id,
|
||||||
|
form.label,
|
||||||
|
&redirect_uri,
|
||||||
|
&app_secret_hash,
|
||||||
|
form.is_confidential,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Flash::new(
|
||||||
|
Redirect::to(uri!(admin_apps_list)),
|
||||||
|
FlashKind::Success,
|
||||||
|
format!("Application \"{}\" has been created", form.label),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% set new_app_label = "New application" %}
|
{% set new_app_label = "New application" %}
|
||||||
{% set new_app_link = "./new" %}
|
{% set new_app_link = "apps/new" %}
|
||||||
<div class="page-header d-print-none">
|
<div class="page-header d-print-none">
|
||||||
<div class="container-xl">
|
<div class="container-xl">
|
||||||
<div class="row align-items-center">
|
<div class="row align-items-center">
|
||||||
|
|
@ -34,6 +34,14 @@
|
||||||
<!-- Page body -->
|
<!-- Page body -->
|
||||||
<div class="page-body">
|
<div class="page-body">
|
||||||
<div class="container-xl">
|
<div class="container-xl">
|
||||||
|
|
||||||
|
{% if flash %}
|
||||||
|
<div class="alert alert-{{flash.0}}" role="alert">
|
||||||
|
<h4 class="alert-title">Success</h4>
|
||||||
|
<div class="text-muted">{{ flash.1 }}</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if apps | length != 0 %}
|
{% if apps | length != 0 %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div id="table-default" class="table-responsive">
|
<div id="table-default" class="table-responsive">
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
Admin dashboard
|
Admin dashboard
|
||||||
</div>
|
</div>
|
||||||
<h2 class="page-title">
|
<h2 class="page-title">
|
||||||
New application
|
Applications
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -17,7 +17,87 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- Page body -->
|
<!-- Page body -->
|
||||||
<div class="page-body">
|
<div class="page-body">
|
||||||
<div class="container-xl"></div>
|
<div class="container-xl">
|
||||||
|
<form action="" method="post" autocomplete="off" class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">New Application</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<!-- App name -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label required" for="label">Application name</label>
|
||||||
|
<div>
|
||||||
|
<input name="label" id="label" type="text" placeholder="Enter name" class="form-control"
|
||||||
|
required>
|
||||||
|
<small class="form-hint">
|
||||||
|
The name of the application, it will be shown to users when authorizing access.
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Redirect URI -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label required" for="redirect_uri">Redirect URI</label>
|
||||||
|
<div>
|
||||||
|
<input name="redirect_uri" id="redirect_uri" type="url" placeholder="Enter redirect URI"
|
||||||
|
class="form-control" required>
|
||||||
|
<small class="form-hint">Make sure you have control over the provided Redirect URI.</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Confidential app -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label required">Confidentiality</label>
|
||||||
|
<div class="form-selectgroup-boxes row mb-3">
|
||||||
|
|
||||||
|
<!-- Private apps -->
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<label class="form-selectgroup-item">
|
||||||
|
<input name="is_confidential" type="radio" value="true" class="form-selectgroup-input"
|
||||||
|
required>
|
||||||
|
<span class="form-selectgroup-label d-flex align-items-center p-3">
|
||||||
|
<span class="me-3">
|
||||||
|
<span class="form-selectgroup-check"></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="form-selectgroup-label-content">
|
||||||
|
<span class="form-selectgroup-title strong mb-1">Private application</span>
|
||||||
|
<span class="d-block text-muted">
|
||||||
|
For applications with a backend, where the application secret can be securely stored.
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Public apps -->
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<label class="form-selectgroup-item">
|
||||||
|
<input name="is_confidential" type="radio" value="false" class="form-selectgroup-input"
|
||||||
|
required>
|
||||||
|
<span class="form-selectgroup-label d-flex align-items-center p-3">
|
||||||
|
<span class="me-3">
|
||||||
|
<span class="form-selectgroup-check"></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span class="form-selectgroup-label-content">
|
||||||
|
<span class="form-selectgroup-title strong mb-1">Public application</span>
|
||||||
|
<span class="d-block text-muted">
|
||||||
|
For public applications (PWAs, desktop, mobile). The application secret can not be securely stored.
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-end">
|
||||||
|
<button type="submit" class="btn btn-primary">Submit</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue